From 0f02e506aa61f984db45f51436e39624e49de90b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Wed, 22 Nov 2023 15:13:03 +0100 Subject: [PATCH 01/85] feat: core structure --- .flake8 | 3 + .gitignore | 173 ++++++++++++++++++++++++++++++++++++++++ .pre-commit-config.yaml | 25 ++++++ LICENSE | 21 +++++ README.md | 0 pyproject.toml | 80 +++++++++++++++++++ tests/__init__.py | 0 7 files changed, 302 insertions(+) create mode 100644 .flake8 create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 pyproject.toml create mode 100644 tests/__init__.py diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..63ea673 --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] +max-line-length=88 +extend-ignore=E203 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e8d849 --- /dev/null +++ b/.gitignore @@ -0,0 +1,173 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm-project.org/#use-with-ide +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# ruff +.ruff_cache/ + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# VSCode +.vscode/ + +# MacOS +.DS_Store + + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..ce38961 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,25 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-yaml + +- repo: https://github.com/compilerla/conventional-pre-commit + rev: v2.3.0 + hooks: + - id: conventional-pre-commit + stages: [commit-msg] + args: ["exp"] # optional: list of Conventional Commits types to allow e.g. [feat, fix, ci, chore, test] + +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.5 + hooks: + # Run the linter. + - id: ruff + types_or: [ python, pyi, jupyter ] + args: [ --fix ] + # Run the formatter. + - id: ruff-format + types_or: [ python, pyi, jupyter ] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..caebf45 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 RationAI + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..846269e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,80 @@ +[project] +name = "" +version = "0.1.0" +description = "" +authors = [] +requires-python = ">=3.11,<3.12" +readme = "README.md" +license = { file = "LICENSE" } + + +##################################### +# Dependencies +##################################### + +dependencies = [] + +[tool.pdm.dev-dependencies] +dev = ["mypy", "pre-commit", "ruff", "pytest"] + + +##################################### +# Scripts +##################################### + +[tool.pdm.scripts] +l = { composite = ["lint", "format", "mypy"] } +tests = "pytest tests" +lint = "ruff check ." +format = "ruff format ." +mypy = "mypy ." +post_install = { composite = [ + "pre-commit install", + "pre-commit install --hook-type commit-msg", +] } + + +##################################### +# Tool configuration +##################################### + +[tool.pylint.messages_control] +disable = ["missing-docstring", "redefined-builtin"] + +[tool.mypy] +strict = true +show_error_codes = true +ignore_missing_imports = true + +[tool.ruff] +fix = true +line-length = 88 +target-version = "py311" +extend-ignore = ["ERA001"] + +[tool.ruff.lint] +unfixable = [ + "ERA", # do not autoremove commented out code +] +extend-select = [ + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "ERA", # flake8-eradicate/eradicate + "I", # isort + "N", # pep8-naming + "PIE", # flake8-pie + "PGH", # pygrep + "RUF", # ruff checks + "SIM", # flake8-simplify + "TCH", # flake8-type-checking + "TID", # flake8-tidy-imports + "UP", # pyupgrade +] + +[tool.ruff.lint.flake8-tidy-imports] +ban-relative-imports = "all" + +[tool.ruff.lint.isort] +lines-between-types = 1 +lines-after-imports = 2 +known-first-party = ["tests"] diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 From 3c62cb678246d6761ac349e74b0b9f7de8d8f8d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Fri, 24 Nov 2023 12:36:54 +0100 Subject: [PATCH 02/85] feat: script for syncing project with template --- sync-with-template.sh | 53 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100755 sync-with-template.sh diff --git a/sync-with-template.sh b/sync-with-template.sh new file mode 100755 index 0000000..01efa74 --- /dev/null +++ b/sync-with-template.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +TEMPLATE_REPO="git@gitlab.ics.muni.cz:rationai/digital-pathology/templates/python.git" +TEMPLATE_BRANCH="master" +COMMIT_FILE="last-template-commit.txt" + + +echo "🔄 Syncing with the template..." + +# Exit on error +set -e + +# Remove template remote if exists +if git remote | grep -q template; then + git remote remove template +fi + +# Add template remote +git remote add template "${TEMPLATE_REPO}" +git fetch template > /dev/null 2>&1 + +# Get last synced commit hash or fallback to the first commit +if [ -s "${COMMIT_FILE}" ] +then + COMMIT_HASH=$(cat "${COMMIT_FILE}") +else + COMMIT_HASH=$(git rev-list --max-parents=0 "template/${TEMPLATE_BRANCH}") +fi + +# Merge template/master into current branch from the last synced commit hash +LATEST_COMMIT=$(git rev-parse "template/${TEMPLATE_BRANCH}") +if [ "$COMMIT_HASH" != "$LATEST_COMMIT" ]; then + set +e + git cherry-pick "${COMMIT_HASH}".."template/${TEMPLATE_BRANCH}" + RETURN_CODE=$? + set -e + + if [ $RETURN_CODE -eq 0 ]; then + git rev-parse template/master > "${COMMIT_FILE}" + echo "🚀 The repository has been successfully synced with the template." + elif [ $RETURN_CODE -eq 1 ]; then + git rev-parse template/master > "${COMMIT_FILE}" + echo "❗️ Auto-syncing failed. Please resolve the conflicts manually." + else + echo "❌ There was a problem with syncing the repository with the template." + fi + git cherry-pick --quit +else + echo "🚀 The repository is already up to date with the template." +fi + +# Clean up +git remote remove template From b0f97c1609a349b7842106ca19608173a560bffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Fri, 24 Nov 2023 13:50:19 +0100 Subject: [PATCH 03/85] fix: remove default "exp" type from commit --- .gitignore | 1 - .pre-commit-config.yaml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 9e8d849..26295c3 100644 --- a/.gitignore +++ b/.gitignore @@ -170,4 +170,3 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ - diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ce38961..7d2c5e4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: hooks: - id: conventional-pre-commit stages: [commit-msg] - args: ["exp"] # optional: list of Conventional Commits types to allow e.g. [feat, fix, ci, chore, test] + args: [] # optional: list of Conventional Commits types to allow e.g. [feat, fix, ci, chore, test] - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.1.5 From 2a22665f4d972f36ac3233230009f8ade4bd28a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Fri, 24 Nov 2023 14:04:24 +0100 Subject: [PATCH 04/85] fix: remove silence --- sync-with-template.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sync-with-template.sh b/sync-with-template.sh index 01efa74..432b121 100755 --- a/sync-with-template.sh +++ b/sync-with-template.sh @@ -17,7 +17,7 @@ fi # Add template remote git remote add template "${TEMPLATE_REPO}" -git fetch template > /dev/null 2>&1 +git fetch template # Get last synced commit hash or fallback to the first commit if [ -s "${COMMIT_FILE}" ] From 13f7425063b43290be56865eeabf89d35c6e8ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Fri, 24 Nov 2023 14:36:05 +0100 Subject: [PATCH 05/85] fix: ignore mlruns --- .gitignore | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 26295c3..237c723 100644 --- a/.gitignore +++ b/.gitignore @@ -163,10 +163,5 @@ cython_debug/ # MacOS .DS_Store - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +# Mlflow +mlruns/ From b8db313f501746956b9eee3a5d940d09d68fade2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Fri, 24 Nov 2023 15:48:40 +0100 Subject: [PATCH 06/85] fix: remove empty line between import types --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 846269e..3da8fd7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,6 +75,5 @@ extend-select = [ ban-relative-imports = "all" [tool.ruff.lint.isort] -lines-between-types = 1 lines-after-imports = 2 known-first-party = ["tests"] From 6f290ec9ee2e5dc22cc7f33cf7abbd30eac6109c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Fri, 24 Nov 2023 18:19:49 +0100 Subject: [PATCH 07/85] feat: separate test dependencies --- pyproject.toml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3da8fd7..c5f9b6f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,8 @@ license = { file = "LICENSE" } dependencies = [] [tool.pdm.dev-dependencies] -dev = ["mypy", "pre-commit", "ruff", "pytest"] +dev = ["mypy", "pre-commit", "ruff"] +test = ["pytest"] ##################################### @@ -50,7 +51,9 @@ ignore_missing_imports = true fix = true line-length = 88 target-version = "py311" -extend-ignore = ["ERA001"] +extend-ignore = [ + "ERA001", # commented out code +] [tool.ruff.lint] unfixable = [ From 2b5660f8cc15e1a6ead6f891aedf109bce270318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Thu, 30 Nov 2023 22:02:21 +0100 Subject: [PATCH 08/85] feat: CI/CD --- .gitlab-ci.yml | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..19a43bf --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,63 @@ +image: python:3.11 + +# Runs jobs only in merge request pipelines. +workflow: + rules: + - if: $CI_PIPELINE_SOURCE == 'merge_request_event' + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + +# Change pip's cache directory to be inside the project directory since we can +# only cache local items. +variables: + PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" + +# Pip's cache doesn't store the python packages +# https://pip.pypa.io/en/stable/topics/caching/ +# +# If you want to also cache the installed packages, you have to install +# them in a virtualenv and cache it as well. +cache: + paths: + - .cache/pip + - .venv + +stages: + - lint + - test + +before_script: + - python --version ; pip --version # For debugging + - pip install -q pdm + - pdm install --no-lock -q + + +ruff-lint: + stage: lint + script: + - pdm lint + +ruff-format: + stage: lint + script: + - pdm format --check + +mypy: + stage: lint + allow_failure: true + script: + - pdm mypy + cache: + paths: + - .mypy_cache + +pytest: + stage: test + script: + - pdm test --junitxml=report.xml + artifacts: + when: always + reports: + junit: report.xml + cache: + paths: + - .pytest_cache diff --git a/pyproject.toml b/pyproject.toml index c5f9b6f..1cb7f6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ test = ["pytest"] [tool.pdm.scripts] l = { composite = ["lint", "format", "mypy"] } -tests = "pytest tests" +test = "pytest tests" lint = "ruff check ." format = "ruff format ." mypy = "mypy ." From 69357fcf7531cf774c024506dd148f1ea47635f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Thu, 30 Nov 2023 22:24:00 +0100 Subject: [PATCH 09/85] fix: pytest allow empty tests --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 19a43bf..d710098 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -53,7 +53,8 @@ mypy: pytest: stage: test script: - - pdm test --junitxml=report.xml + # Allow pytest to fail with exit code 5 (no tests collected) + - pdm test --junitxml=report.xml || ( exit_code=$?; [ $exit_code -eq 5 ] && exit 0 || exit $exit_code ) artifacts: when: always reports: From da6624562396af7aa87a099ea3a2c8af925d6800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Mon, 4 Dec 2023 19:07:11 +0100 Subject: [PATCH 10/85] feat: syncing pipeline + google code styles --- .gitignore | 7 + .gitlab-ci.yml | 74 +++----- .gitlab/ci/lint.yml | 24 +++ .gitlab/ci/sync.yml | 25 +++ .gitlab/ci/test.yml | 14 ++ .pylintrc | 399 ++++++++++++++++++++++++++++++++++++++++++ README.md | 58 ++++++ pyproject.toml | 17 +- sync-with-template.sh | 53 ------ 9 files changed, 563 insertions(+), 108 deletions(-) create mode 100644 .gitlab/ci/lint.yml create mode 100644 .gitlab/ci/sync.yml create mode 100644 .gitlab/ci/test.yml create mode 100644 .pylintrc delete mode 100755 sync-with-template.sh diff --git a/.gitignore b/.gitignore index 237c723..a42f203 100644 --- a/.gitignore +++ b/.gitignore @@ -165,3 +165,10 @@ cython_debug/ # Mlflow mlruns/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d710098..b4bddaa 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,64 +1,36 @@ -image: python:3.11 +default: + image: python:3.11 + before_script: + - python --version ; pip --version # For debugging + - pip install -q pdm + - pdm install --no-lock -q --skip=post_install # skip pre-commit hooks + + # Pip's cache doesn't store the python packages + # https://pip.pypa.io/en/stable/topics/caching/ + cache: + paths: + - .cache/pip + - .venv -# Runs jobs only in merge request pipelines. -workflow: - rules: - - if: $CI_PIPELINE_SOURCE == 'merge_request_event' - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Change pip's cache directory to be inside the project directory since we can # only cache local items. variables: PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" + TEMPLATE_CI_BRANCH: "ci/template" + + +# Runs jobs only in merge request pipelines or if the branch is TEMPLATE_CI_BRANCH +workflow: + rules: + - if: $CI_PIPELINE_SOURCE == 'merge_request_event' + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + - if: $CI_COMMIT_BRANCH == $TEMPLATE_CI_BRANCH -# Pip's cache doesn't store the python packages -# https://pip.pypa.io/en/stable/topics/caching/ -# -# If you want to also cache the installed packages, you have to install -# them in a virtualenv and cache it as well. -cache: - paths: - - .cache/pip - - .venv stages: - lint - test -before_script: - - python --version ; pip --version # For debugging - - pip install -q pdm - - pdm install --no-lock -q - - -ruff-lint: - stage: lint - script: - - pdm lint - -ruff-format: - stage: lint - script: - - pdm format --check -mypy: - stage: lint - allow_failure: true - script: - - pdm mypy - cache: - paths: - - .mypy_cache - -pytest: - stage: test - script: - # Allow pytest to fail with exit code 5 (no tests collected) - - pdm test --junitxml=report.xml || ( exit_code=$?; [ $exit_code -eq 5 ] && exit 0 || exit $exit_code ) - artifacts: - when: always - reports: - junit: report.xml - cache: - paths: - - .pytest_cache +include: .gitlab/ci/*.yml diff --git a/.gitlab/ci/lint.yml b/.gitlab/ci/lint.yml new file mode 100644 index 0000000..6fe5ff9 --- /dev/null +++ b/.gitlab/ci/lint.yml @@ -0,0 +1,24 @@ +ruff-lint: + stage: lint + script: + - pdm lint + rules: + - if: $CI_COMMIT_BRANCH != $TEMPLATE_CI_BRANCH && $CI_PIPELINE_SOURCE != 'schedule' + +ruff-format: + stage: lint + script: + - pdm format --check + rules: + - if: $CI_COMMIT_BRANCH != $TEMPLATE_CI_BRANCH && $CI_PIPELINE_SOURCE != 'schedule' + +mypy: + stage: lint + allow_failure: true + script: + - pdm mypy + cache: + paths: + - .mypy_cache + rules: + - if: $CI_COMMIT_BRANCH != $TEMPLATE_CI_BRANCH && $CI_PIPELINE_SOURCE != 'schedule' diff --git a/.gitlab/ci/sync.yml b/.gitlab/ci/sync.yml new file mode 100644 index 0000000..ef84c36 --- /dev/null +++ b/.gitlab/ci/sync.yml @@ -0,0 +1,25 @@ +sync: + image: bitnami/git + variables: + USER_NAME: "group_1021_bot_390f0b45d22bcd3d4999f51de6ad8742" + USER_EMAIL: "group_1021_bot_390f0b45d22bcd3d4999f51de6ad8742@noreply.gitlab.ics.muni.cz" + TEMPLATE_REPO: https://${USER_NAME}:${GROUP_ACCESS_TOKEN_RW}@gitlab.ics.muni.cz/rationai/digital-pathology/templates/python.git + COMMIT_MESSAGE: "ci: sync with template repository" + MERGE_REQUEST_TITLE: "ci: automated MR from Template" + before_script: + - git config --global user.name "${USER_NAME}" + - git config --global user.email "${USER_EMAIL}" + script: + - git pull "${TEMPLATE_REPO}" --no-rebase --allow-unrelated-histories --no-commit -X theirs + - > + if ! git diff --cached --quiet; then + echo "📂 Files that will be commited:" + git diff --cached --name-only + + git commit -m "${COMMIT_MESSAGE}" + git push "https://${USER_NAME}:${GROUP_ACCESS_TOKEN_RW}@${CI_REPOSITORY_URL#*@}" HEAD:$CI_COMMIT_REF_NAME \ + -o merge_request.create -o merge_request.target=${CI_DEFAULT_BRANCH} -o merge_request.remove_source_branch -o merge_request.title="${MERGE_REQUEST_TITLE}" + fi + rules: + - if: $CI_COMMIT_BRANCH == $TEMPLATE_CI_BRANCH && $CI_PIPELINE_SOURCE == 'schedule' + cache: {} diff --git a/.gitlab/ci/test.yml b/.gitlab/ci/test.yml new file mode 100644 index 0000000..3de6cf2 --- /dev/null +++ b/.gitlab/ci/test.yml @@ -0,0 +1,14 @@ +pytest: + stage: test + script: + # Allow pytest to fail with exit code 5 (no tests collected) + - pdm test --junitxml=report.xml || ( exit_code=$?; [ $exit_code -eq 5 ] && exit 0 || exit $exit_code ) + artifacts: + when: always + reports: + junit: report.xml + cache: + paths: + - .pytest_cache + rules: + - if: $CI_COMMIT_BRANCH != $TEMPLATE_CI_BRANCH && $CI_PIPELINE_SOURCE != 'schedule' diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..cd99157 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,399 @@ +# This Pylint rcfile contains a best-effort configuration to uphold the +# best-practices and style described in the Google Python style guide: +# https://google.github.io/styleguide/pyguide.html +# +# Its canonical open-source location is: +# https://google.github.io/styleguide/pylintrc + +[MAIN] + +# Files or directories to be skipped. They should be base names, not paths. +ignore=third_party + +# Files or directories matching the regex patterns are skipped. The regex +# matches against base names, not paths. +ignore-patterns= + +# Pickle collected data for later comparisons. +persistent=no + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Use multiple processes to speed up Pylint. +jobs=4 + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +disable=R, + abstract-method, + apply-builtin, + arguments-differ, + attribute-defined-outside-init, + backtick, + bad-option-value, + basestring-builtin, + buffer-builtin, + c-extension-no-member, + consider-using-enumerate, + cmp-builtin, + cmp-method, + coerce-builtin, + coerce-method, + delslice-method, + div-method, + eq-without-hash, + execfile-builtin, + file-builtin, + filter-builtin-not-iterating, + fixme, + getslice-method, + global-statement, + hex-method, + idiv-method, + implicit-str-concat, + import-error, + import-self, + import-star-module-level, + input-builtin, + intern-builtin, + invalid-str-codec, + locally-disabled, + long-builtin, + long-suffix, + map-builtin-not-iterating, + misplaced-comparison-constant, + missing-function-docstring, + metaclass-assignment, + next-method-called, + next-method-defined, + no-absolute-import, + no-init, # added + no-member, + no-name-in-module, + no-self-use, + nonzero-method, + oct-method, + old-division, + old-ne-operator, + old-octal-literal, + old-raise-syntax, + parameter-unpacking, + print-statement, + raising-string, + range-builtin-not-iterating, + raw_input-builtin, + rdiv-method, + reduce-builtin, + relative-import, + reload-builtin, + round-builtin, + setslice-method, + signature-differs, + standarderror-builtin, + suppressed-message, + sys-max-int, + trailing-newlines, + unichr-builtin, + unicode-builtin, + unnecessary-pass, + unpacking-in-except, + useless-else-on-loop, + useless-suppression, + using-cmp-argument, + wrong-import-order, + xrange-builtin, + zip-builtin-not-iterating, + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Tells whether to display a full report or only the messages +reports=no + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + + +[BASIC] + +# Good variable names which should always be accepted, separated by a comma +good-names=main,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl + +# Regular expression matching correct function names +function-rgx=^(?:(?PsetUp|tearDown|setUpModule|tearDownModule)|(?P_?[A-Z][a-zA-Z0-9]*)|(?P_?[a-z][a-z0-9_]*))$ + +# Regular expression matching correct variable names +variable-rgx=^[a-z][a-z0-9_]*$ + +# Regular expression matching correct constant names +const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ + +# Regular expression matching correct attribute names +attr-rgx=^_{0,2}[a-z][a-z0-9_]*$ + +# Regular expression matching correct argument names +argument-rgx=^[a-z][a-z0-9_]*$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=^[a-z][a-z0-9_]*$ + +# Regular expression matching correct class names +class-rgx=^_?[A-Z][a-zA-Z0-9]*$ + +# Regular expression matching correct module names +module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$ + +# Regular expression matching correct method names +method-rgx=(?x)^(?:(?P_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P_{0,2}[a-z][a-z0-9_]*))$ + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=12 + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=80 + +# TODO(https://github.com/pylint-dev/pylint/issues/3352): Direct pylint to exempt +# lines made too long by directives to pytype. + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=(?x)( + ^\s*(\#\ )??$| + ^\s*(from\s+\S+\s+)?import\s+.+$) + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=yes + +# Maximum number of lines in a module +max-module-lines=99999 + +# String used as indentation unit. The internal Google style guide mandates 2 +# spaces. Google's externaly-published style guide says 4, consistent with +# PEP 8. Here, we use 2 spaces, for conformity with many open-sourced Google +# projects (like TensorFlow). +indent-string=' ' + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=TODO + + +[STRING] + +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=yes + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_) + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging,absl.logging,tensorflow.io.logging + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub, + TERMIOS, + Bastion, + rexec, + sets + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant, absl + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls, + class_ + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs diff --git a/README.md b/README.md index e69de29..cd979cd 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,58 @@ +# Python project template + +This project template serves as a robust foundation for Python projects, promoting best practices and streamlining development workflows. It comes preconfigured with essential tools and features to enhance the development experience. + +## Tools Included + +- [PDM](https://pdm-project.org) for efficient dependency management. +- [Ruff](https://docs.astral.sh/ruff) for comprehensive linting and code formatting. +- [Pytest](https://docs.pytest.org) for running tests and ensuring code reliability. +- [pre-commit](https://pre-commit.com) for managing pre-commit git hooks ([Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), ruff check, ruff format) +- [GitLab CI/CD](https://docs.gitlab.com/ee/ci) for continuous integration. +- [Pylint](https://pylint.readthedocs.io) for static code analysis, adhering to the [Google style](https://google.github.io/styleguide/pyguide.html). +- [Pydocstyle](https://www.pydocstyle.org) for validating docstring styles, also following the [Google style](https://google.github.io/styleguide/pyguide.html#s3.8-comments-and-docstrings). +- [Mypy](https://mypy-lang.org) for static type checking. + + +## Usage + +Key commands for effective project management: + +- `pdm install` - Installs all project dependencies. +- `pdm add ` - Adds a new dependency to the project. +- `pdm l` - Runs linting, formatting, and mypy checks. +- `pdm test` - Executes tests located in the tests directory. +- `pdm run ` - Runs the specified command within the virtual environment. + +## CI/CD + +Two defined pipelines enhance project development: + +1. **Synchronization pipeline(`ci/template` branch):** + - Triggered by a scheduler for the ci/template branch. + - Handles [synchronization](#synchronizing-the-template) with the template repository. + +2. **Project Pipeline (Main branch and Merge Requests):** + - Triggered by any push to the main branch or any merge request to the main branch. + - Consists of the following stages: + - `lint` - Runs linting, formatting, and mypy checks (ruff check, ruff format, mypy). + - `test` - Executes tests using Pytest. + + +## Synchronizing the Template + +For seamless integration with the template repository: + +1. Create a protected branch named `ci/template`. +2. [Schedule a pipeline](https://docs.gitlab.com/ee/ci/pipelines/schedules.html) for the `ci/template` branch to automatically synchronize with the template repository. +3. Ensure the `ci/template` branch tracks the last synchronized commit. +4. The synchronization pipeline will: + - Check out the last synchronized commit. + - Merge the latest commit from the template repository, prioritizing template changes in case of conflicts. + - Commit and push the changes to the `ci/template` branch. + - Create a merge request to the default branch. + + + +> **Note:** +> Resolve any conflicts manually, ensuring the **last synchronized commit** is retained in the ci/template branch to prevent reappearing conflicts during subsequent synchronizations. diff --git a/pyproject.toml b/pyproject.toml index 1cb7f6b..8b810ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "" version = "0.1.0" description = "" authors = [] -requires-python = ">=3.11,<3.12" +requires-python = ">=3.11" readme = "README.md" license = { file = "LICENSE" } @@ -39,9 +39,6 @@ post_install = { composite = [ # Tool configuration ##################################### -[tool.pylint.messages_control] -disable = ["missing-docstring", "redefined-builtin"] - [tool.mypy] strict = true show_error_codes = true @@ -53,6 +50,14 @@ line-length = 88 target-version = "py311" extend-ignore = [ "ERA001", # commented out code + "D100", # missing docstring in public module + "D101", # missing docstring in public class + "D102", # missing docstring in public method + "D103", # missing docstring in public function + "D104", # missing docstring in public package + "D105", # missing docstring in magic method + "D106", # missing docstring in public nested class + "D107", # missing docstring in __init__ ] [tool.ruff.lint] @@ -72,6 +77,7 @@ extend-select = [ "TCH", # flake8-type-checking "TID", # flake8-tidy-imports "UP", # pyupgrade + "D", # pydocstyle ] [tool.ruff.lint.flake8-tidy-imports] @@ -80,3 +86,6 @@ ban-relative-imports = "all" [tool.ruff.lint.isort] lines-after-imports = 2 known-first-party = ["tests"] + +[tool.ruff.lint.pydocstyle] +convention = "google" diff --git a/sync-with-template.sh b/sync-with-template.sh deleted file mode 100755 index 432b121..0000000 --- a/sync-with-template.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -TEMPLATE_REPO="git@gitlab.ics.muni.cz:rationai/digital-pathology/templates/python.git" -TEMPLATE_BRANCH="master" -COMMIT_FILE="last-template-commit.txt" - - -echo "🔄 Syncing with the template..." - -# Exit on error -set -e - -# Remove template remote if exists -if git remote | grep -q template; then - git remote remove template -fi - -# Add template remote -git remote add template "${TEMPLATE_REPO}" -git fetch template - -# Get last synced commit hash or fallback to the first commit -if [ -s "${COMMIT_FILE}" ] -then - COMMIT_HASH=$(cat "${COMMIT_FILE}") -else - COMMIT_HASH=$(git rev-list --max-parents=0 "template/${TEMPLATE_BRANCH}") -fi - -# Merge template/master into current branch from the last synced commit hash -LATEST_COMMIT=$(git rev-parse "template/${TEMPLATE_BRANCH}") -if [ "$COMMIT_HASH" != "$LATEST_COMMIT" ]; then - set +e - git cherry-pick "${COMMIT_HASH}".."template/${TEMPLATE_BRANCH}" - RETURN_CODE=$? - set -e - - if [ $RETURN_CODE -eq 0 ]; then - git rev-parse template/master > "${COMMIT_FILE}" - echo "🚀 The repository has been successfully synced with the template." - elif [ $RETURN_CODE -eq 1 ]; then - git rev-parse template/master > "${COMMIT_FILE}" - echo "❗️ Auto-syncing failed. Please resolve the conflicts manually." - else - echo "❌ There was a problem with syncing the repository with the template." - fi - git cherry-pick --quit -else - echo "🚀 The repository is already up to date with the template." -fi - -# Clean up -git remote remove template From 97e76976f99fd51000db9e55cb9a98854d9e4359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Wed, 6 Dec 2023 14:42:51 +0100 Subject: [PATCH 11/85] fix: pipeline rules --- .gitlab-ci.yml | 16 +++++++--------- .gitlab/ci/lint.yml | 8 +++++--- .gitlab/ci/sync.yml | 1 + .gitlab/ci/test.yml | 2 +- .pre-commit-config.yaml | 3 ++- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b4bddaa..117b461 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,15 +17,6 @@ default: # only cache local items. variables: PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" - TEMPLATE_CI_BRANCH: "ci/template" - - -# Runs jobs only in merge request pipelines or if the branch is TEMPLATE_CI_BRANCH -workflow: - rules: - - if: $CI_PIPELINE_SOURCE == 'merge_request_event' - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - - if: $CI_COMMIT_BRANCH == $TEMPLATE_CI_BRANCH stages: @@ -33,4 +24,11 @@ stages: - test +# Runs jobs only in merge request pipelines +.merge_request_pipelines: + rules: + - if: $CI_PIPELINE_SOURCE == 'merge_request_event' + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + + include: .gitlab/ci/*.yml diff --git a/.gitlab/ci/lint.yml b/.gitlab/ci/lint.yml index 6fe5ff9..d28060a 100644 --- a/.gitlab/ci/lint.yml +++ b/.gitlab/ci/lint.yml @@ -3,14 +3,16 @@ ruff-lint: script: - pdm lint rules: - - if: $CI_COMMIT_BRANCH != $TEMPLATE_CI_BRANCH && $CI_PIPELINE_SOURCE != 'schedule' + - !reference [.merge_request_pipelines, rules] + ruff-format: stage: lint script: - pdm format --check rules: - - if: $CI_COMMIT_BRANCH != $TEMPLATE_CI_BRANCH && $CI_PIPELINE_SOURCE != 'schedule' + - !reference [.merge_request_pipelines, rules] + mypy: stage: lint @@ -21,4 +23,4 @@ mypy: paths: - .mypy_cache rules: - - if: $CI_COMMIT_BRANCH != $TEMPLATE_CI_BRANCH && $CI_PIPELINE_SOURCE != 'schedule' + - !reference [.merge_request_pipelines, rules] diff --git a/.gitlab/ci/sync.yml b/.gitlab/ci/sync.yml index ef84c36..543dfc6 100644 --- a/.gitlab/ci/sync.yml +++ b/.gitlab/ci/sync.yml @@ -1,6 +1,7 @@ sync: image: bitnami/git variables: + TEMPLATE_CI_BRANCH: "ci/template" USER_NAME: "group_1021_bot_390f0b45d22bcd3d4999f51de6ad8742" USER_EMAIL: "group_1021_bot_390f0b45d22bcd3d4999f51de6ad8742@noreply.gitlab.ics.muni.cz" TEMPLATE_REPO: https://${USER_NAME}:${GROUP_ACCESS_TOKEN_RW}@gitlab.ics.muni.cz/rationai/digital-pathology/templates/python.git diff --git a/.gitlab/ci/test.yml b/.gitlab/ci/test.yml index 3de6cf2..8c89f03 100644 --- a/.gitlab/ci/test.yml +++ b/.gitlab/ci/test.yml @@ -11,4 +11,4 @@ pytest: paths: - .pytest_cache rules: - - if: $CI_COMMIT_BRANCH != $TEMPLATE_CI_BRANCH && $CI_PIPELINE_SOURCE != 'schedule' + - !reference [.merge_request_pipelines, rules] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7d2c5e4..01516a5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,12 +5,13 @@ repos: rev: v4.5.0 hooks: - id: check-yaml + args: [ --unsafe ] - repo: https://github.com/compilerla/conventional-pre-commit rev: v2.3.0 hooks: - id: conventional-pre-commit - stages: [commit-msg] + stages: [ commit-msg ] args: [] # optional: list of Conventional Commits types to allow e.g. [feat, fix, ci, chore, test] - repo: https://github.com/astral-sh/ruff-pre-commit From 225ee7778969518fe53dcd76412d7266e08a27e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Wed, 6 Dec 2023 14:53:12 +0100 Subject: [PATCH 12/85] fix: pylint max line length 88 --- .pylintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index cd99157..c608030 100644 --- a/.pylintrc +++ b/.pylintrc @@ -238,7 +238,7 @@ generated-members= [FORMAT] # Maximum number of characters on a single line. -max-line-length=80 +max-line-length=88 # TODO(https://github.com/pylint-dev/pylint/issues/3352): Direct pylint to exempt # lines made too long by directives to pytype. From 56cdb9c45bea405676dd660406fc4311f7eceef3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Thu, 21 Dec 2023 22:30:06 +0100 Subject: [PATCH 13/85] refactor: new bot user --- .gitlab/ci/sync.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.gitlab/ci/sync.yml b/.gitlab/ci/sync.yml index 543dfc6..3094fd8 100644 --- a/.gitlab/ci/sync.yml +++ b/.gitlab/ci/sync.yml @@ -2,13 +2,14 @@ sync: image: bitnami/git variables: TEMPLATE_CI_BRANCH: "ci/template" - USER_NAME: "group_1021_bot_390f0b45d22bcd3d4999f51de6ad8742" - USER_EMAIL: "group_1021_bot_390f0b45d22bcd3d4999f51de6ad8742@noreply.gitlab.ics.muni.cz" - TEMPLATE_REPO: https://${USER_NAME}:${GROUP_ACCESS_TOKEN_RW}@gitlab.ics.muni.cz/rationai/digital-pathology/templates/python.git + NAME: "🤖 Template Bot 🤖" + USER_NAME: "group_1021_bot_62f237df79b4aaf1aeeb93687a941fbb" + USER_EMAIL: "group_1021_bot_62f237df79b4aaf1aeeb93687a941fbb@noreply.gitlab.ics.muni.cz" + TEMPLATE_REPO: https://${USER_NAME}:${GROUP_ACCESS_TOKEN_TEMPLATE_BOT_RW}@gitlab.ics.muni.cz/rationai/digital-pathology/templates/python.git COMMIT_MESSAGE: "ci: sync with template repository" MERGE_REQUEST_TITLE: "ci: automated MR from Template" before_script: - - git config --global user.name "${USER_NAME}" + - git config --global user.name "${NAME}" - git config --global user.email "${USER_EMAIL}" script: - git pull "${TEMPLATE_REPO}" --no-rebase --allow-unrelated-histories --no-commit -X theirs @@ -18,7 +19,7 @@ sync: git diff --cached --name-only git commit -m "${COMMIT_MESSAGE}" - git push "https://${USER_NAME}:${GROUP_ACCESS_TOKEN_RW}@${CI_REPOSITORY_URL#*@}" HEAD:$CI_COMMIT_REF_NAME \ + git push "https://${USER_NAME}:${GROUP_ACCESS_TOKEN_TEMPLATE_BOT_RW}@${CI_REPOSITORY_URL#*@}" HEAD:$CI_COMMIT_REF_NAME \ -o merge_request.create -o merge_request.target=${CI_DEFAULT_BRANCH} -o merge_request.remove_source_branch -o merge_request.title="${MERGE_REQUEST_TITLE}" fi rules: From 0dd2b046a3aca5590bed8ea8388880cd4e2dd7e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Thu, 21 Dec 2023 22:39:56 +0100 Subject: [PATCH 14/85] chore: format yaml --- .gitlab-ci.yml | 6 +----- .gitlab/ci/lint.yml | 2 -- .pre-commit-config.yaml | 28 ++++++++++++++-------------- 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 117b461..b3246be 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,7 @@ default: image: python:3.11 before_script: - - python --version ; pip --version # For debugging + - python --version ; pip --version # For debugging - pip install -q pdm - pdm install --no-lock -q --skip=post_install # skip pre-commit hooks @@ -12,23 +12,19 @@ default: - .cache/pip - .venv - # Change pip's cache directory to be inside the project directory since we can # only cache local items. variables: PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" - stages: - lint - test - # Runs jobs only in merge request pipelines .merge_request_pipelines: rules: - if: $CI_PIPELINE_SOURCE == 'merge_request_event' - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - include: .gitlab/ci/*.yml diff --git a/.gitlab/ci/lint.yml b/.gitlab/ci/lint.yml index d28060a..618a148 100644 --- a/.gitlab/ci/lint.yml +++ b/.gitlab/ci/lint.yml @@ -5,7 +5,6 @@ ruff-lint: rules: - !reference [.merge_request_pipelines, rules] - ruff-format: stage: lint script: @@ -13,7 +12,6 @@ ruff-format: rules: - !reference [.merge_request_pipelines, rules] - mypy: stage: lint allow_failure: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 01516a5..3464bab 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,26 +1,26 @@ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: -- repo: https://github.com/pre-commit/pre-commit-hooks + - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: - - id: check-yaml - args: [ --unsafe ] + - id: check-yaml + args: [--unsafe] -- repo: https://github.com/compilerla/conventional-pre-commit + - repo: https://github.com/compilerla/conventional-pre-commit rev: v2.3.0 hooks: - - id: conventional-pre-commit - stages: [ commit-msg ] + - id: conventional-pre-commit + stages: [commit-msg] args: [] # optional: list of Conventional Commits types to allow e.g. [feat, fix, ci, chore, test] -- repo: https://github.com/astral-sh/ruff-pre-commit + - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.1.5 hooks: - # Run the linter. - - id: ruff - types_or: [ python, pyi, jupyter ] - args: [ --fix ] - # Run the formatter. - - id: ruff-format - types_or: [ python, pyi, jupyter ] + # Run the linter. + - id: ruff + types_or: [python, pyi, jupyter] + args: [--fix] + # Run the formatter. + - id: ruff-format + types_or: [python, pyi, jupyter] From 9ed4f98aed6f4b32de0b64da800d614cbf40ce3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Fri, 22 Dec 2023 15:19:31 +0100 Subject: [PATCH 15/85] feat: docstring code format --- pyproject.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8b810ef..15bfe62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,6 @@ post_install = { composite = [ [tool.mypy] strict = true -show_error_codes = true ignore_missing_imports = true [tool.ruff] @@ -60,6 +59,10 @@ extend-ignore = [ "D107", # missing docstring in __init__ ] +[tool.ruff.format] +# Enable reformatting of code snippets in docstrings. +docstring-code-format = true + [tool.ruff.lint] unfixable = [ "ERA", # do not autoremove commented out code From c62740a3c65eb235c7e25ee20015ad545f167a85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Fri, 22 Dec 2023 15:40:37 +0100 Subject: [PATCH 16/85] fix: update pre-commit versions --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3464bab..a602928 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,14 +8,14 @@ repos: args: [--unsafe] - repo: https://github.com/compilerla/conventional-pre-commit - rev: v2.3.0 + rev: v3.0.0 hooks: - id: conventional-pre-commit stages: [commit-msg] args: [] # optional: list of Conventional Commits types to allow e.g. [feat, fix, ci, chore, test] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.5 + rev: v0.1.9 hooks: # Run the linter. - id: ruff From ff95fb808afba42a68eecde07a33eef229655236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Sat, 23 Dec 2023 15:42:32 +0100 Subject: [PATCH 17/85] feat: test coverage --- .gitlab/ci/test.yml | 6 +++++- pyproject.toml | 13 ++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.gitlab/ci/test.yml b/.gitlab/ci/test.yml index 8c89f03..9b1e717 100644 --- a/.gitlab/ci/test.yml +++ b/.gitlab/ci/test.yml @@ -2,11 +2,15 @@ pytest: stage: test script: # Allow pytest to fail with exit code 5 (no tests collected) - - pdm test --junitxml=report.xml || ( exit_code=$?; [ $exit_code -eq 5 ] && exit 0 || exit $exit_code ) + - pdm test --cov --cov-report term --cov-report xml:coverage.xml --junitxml=report.xml || ( exit_code=$?; [ $exit_code -eq 5 ] && exit 0 || exit $exit_code ) + coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/' artifacts: when: always reports: junit: report.xml + coverage_report: + coverage_format: cobertura + path: coverage.xml cache: paths: - .pytest_cache diff --git a/pyproject.toml b/pyproject.toml index 15bfe62..d9c3da4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ dependencies = [] [tool.pdm.dev-dependencies] dev = ["mypy", "pre-commit", "ruff"] -test = ["pytest"] +test = ["pytest", "pytest-cov"] ##################################### @@ -39,6 +39,17 @@ post_install = { composite = [ # Tool configuration ##################################### +[tool.coverage.run] +branch = true +omit = ["tests/*"] +source = ["."] + +[tool.coverage.report] +exclude_also = [ + # Don't complain about abstract methods, they aren't run: + "@(abc\\.)?abstractmethod", +] + [tool.mypy] strict = true ignore_missing_imports = true From 603ab7dbfa5758129be8df6ef7b076af8ed00db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Mon, 25 Dec 2023 14:46:11 +0100 Subject: [PATCH 18/85] feat: allow installing gitlab dependecies in CI/CD --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b3246be..9357c60 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,6 +3,8 @@ default: before_script: - python --version ; pip --version # For debugging - pip install -q pdm + # allow pdm to install dependencies from gitlab + - git config --global url."https://${DEPLOY_TOKEN_USERNAME}:${DEPLOY_TOKEN_PASSWORD}@gitlab.ics.muni.cz/".insteadOf "ssh://git@gitlab.ics.muni.cz/" - pdm install --no-lock -q --skip=post_install # skip pre-commit hooks # Pip's cache doesn't store the python packages From cdc32c10051953b276808d58efa9d5e4ecb444e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Sat, 6 Jan 2024 18:18:43 +0100 Subject: [PATCH 19/85] chore: ignore mlflow_store --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a42f203..f490645 100644 --- a/.gitignore +++ b/.gitignore @@ -165,6 +165,7 @@ cython_debug/ # Mlflow mlruns/ +mlflow_store/ # PyCharm # JetBrains specific template is maintained in a separate JetBrains.gitignore that can From 584cdf216c6b423c242a72d933071aa0a75d9228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Fri, 24 May 2024 10:43:04 +0200 Subject: [PATCH 20/85] chore: new libary template --- .gitlab-ci.yml | 33 +--- .gitlab/ci/lint.yml | 24 --- .gitlab/ci/sync.yml | 27 --- .gitlab/ci/test.yml | 18 -- .mypy.ini | 5 + .ruff.toml | 51 +++++ LICENSE | 2 +- README.md | 59 +----- pdm.lock | 362 ++++++++++++++++++++++++++++++++++ pyproject.toml | 87 +------- rationai/staining/__init__.py | 0 11 files changed, 428 insertions(+), 240 deletions(-) delete mode 100644 .gitlab/ci/lint.yml delete mode 100644 .gitlab/ci/sync.yml delete mode 100644 .gitlab/ci/test.yml create mode 100644 .mypy.ini create mode 100644 .ruff.toml create mode 100644 pdm.lock create mode 100644 rationai/staining/__init__.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9357c60..1675c5c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,32 +1,9 @@ -default: - image: python:3.11 - before_script: - - python --version ; pip --version # For debugging - - pip install -q pdm - # allow pdm to install dependencies from gitlab - - git config --global url."https://${DEPLOY_TOKEN_USERNAME}:${DEPLOY_TOKEN_PASSWORD}@gitlab.ics.muni.cz/".insteadOf "ssh://git@gitlab.ics.muni.cz/" - - pdm install --no-lock -q --skip=post_install # skip pre-commit hooks - - # Pip's cache doesn't store the python packages - # https://pip.pypa.io/en/stable/topics/caching/ - cache: - paths: - - .cache/pip - - .venv - -# Change pip's cache directory to be inside the project directory since we can -# only cache local items. -variables: - PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" +include: + project: rationai/digital-pathology/templates/ci-templates + file: + - Pytest.gitlab-ci.yml + - Python-Lint.gitlab-ci.yml stages: - lint - test - -# Runs jobs only in merge request pipelines -.merge_request_pipelines: - rules: - - if: $CI_PIPELINE_SOURCE == 'merge_request_event' - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - -include: .gitlab/ci/*.yml diff --git a/.gitlab/ci/lint.yml b/.gitlab/ci/lint.yml deleted file mode 100644 index 618a148..0000000 --- a/.gitlab/ci/lint.yml +++ /dev/null @@ -1,24 +0,0 @@ -ruff-lint: - stage: lint - script: - - pdm lint - rules: - - !reference [.merge_request_pipelines, rules] - -ruff-format: - stage: lint - script: - - pdm format --check - rules: - - !reference [.merge_request_pipelines, rules] - -mypy: - stage: lint - allow_failure: true - script: - - pdm mypy - cache: - paths: - - .mypy_cache - rules: - - !reference [.merge_request_pipelines, rules] diff --git a/.gitlab/ci/sync.yml b/.gitlab/ci/sync.yml deleted file mode 100644 index 3094fd8..0000000 --- a/.gitlab/ci/sync.yml +++ /dev/null @@ -1,27 +0,0 @@ -sync: - image: bitnami/git - variables: - TEMPLATE_CI_BRANCH: "ci/template" - NAME: "🤖 Template Bot 🤖" - USER_NAME: "group_1021_bot_62f237df79b4aaf1aeeb93687a941fbb" - USER_EMAIL: "group_1021_bot_62f237df79b4aaf1aeeb93687a941fbb@noreply.gitlab.ics.muni.cz" - TEMPLATE_REPO: https://${USER_NAME}:${GROUP_ACCESS_TOKEN_TEMPLATE_BOT_RW}@gitlab.ics.muni.cz/rationai/digital-pathology/templates/python.git - COMMIT_MESSAGE: "ci: sync with template repository" - MERGE_REQUEST_TITLE: "ci: automated MR from Template" - before_script: - - git config --global user.name "${NAME}" - - git config --global user.email "${USER_EMAIL}" - script: - - git pull "${TEMPLATE_REPO}" --no-rebase --allow-unrelated-histories --no-commit -X theirs - - > - if ! git diff --cached --quiet; then - echo "📂 Files that will be commited:" - git diff --cached --name-only - - git commit -m "${COMMIT_MESSAGE}" - git push "https://${USER_NAME}:${GROUP_ACCESS_TOKEN_TEMPLATE_BOT_RW}@${CI_REPOSITORY_URL#*@}" HEAD:$CI_COMMIT_REF_NAME \ - -o merge_request.create -o merge_request.target=${CI_DEFAULT_BRANCH} -o merge_request.remove_source_branch -o merge_request.title="${MERGE_REQUEST_TITLE}" - fi - rules: - - if: $CI_COMMIT_BRANCH == $TEMPLATE_CI_BRANCH && $CI_PIPELINE_SOURCE == 'schedule' - cache: {} diff --git a/.gitlab/ci/test.yml b/.gitlab/ci/test.yml deleted file mode 100644 index 9b1e717..0000000 --- a/.gitlab/ci/test.yml +++ /dev/null @@ -1,18 +0,0 @@ -pytest: - stage: test - script: - # Allow pytest to fail with exit code 5 (no tests collected) - - pdm test --cov --cov-report term --cov-report xml:coverage.xml --junitxml=report.xml || ( exit_code=$?; [ $exit_code -eq 5 ] && exit 0 || exit $exit_code ) - coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/' - artifacts: - when: always - reports: - junit: report.xml - coverage_report: - coverage_format: cobertura - path: coverage.xml - cache: - paths: - - .pytest_cache - rules: - - !reference [.merge_request_pipelines, rules] diff --git a/.mypy.ini b/.mypy.ini new file mode 100644 index 0000000..1c6f44e --- /dev/null +++ b/.mypy.ini @@ -0,0 +1,5 @@ +[mypy] +strict = True +ignore_missing_imports = True +disallow_untyped_calls = False +disable_error_code = no-any-return \ No newline at end of file diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 0000000..fb5e992 --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,51 @@ +fix = true +line-length = 88 +target-version = "py311" + +[format] +# Enable reformatting of code snippets in docstrings. +docstring-code-format = true + +[lint] +unfixable = [ + "ERA", # do not autoremove commented out code +] +extend-select = [ + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "ERA", # flake8-eradicate/eradicate + "I", # isort + "N", # pep8-naming + "PIE", # flake8-pie + "PGH", # pygrep + "RUF", # ruff checks + "SIM", # flake8-simplify + "TCH", # flake8-type-checking + "TID", # flake8-tidy-imports + "UP", # pyupgrade + "D", # pydocstyle +] +extend-ignore = [ + "ERA001", # commented out code + "D100", # missing docstring in public module + "D101", # missing docstring in public class + "D102", # missing docstring in public method + "D103", # missing docstring in public function + "D104", # missing docstring in public package + "D105", # missing docstring in magic method + "D106", # missing docstring in public nested class + "D107", # missing docstring in __init__ + "N812", # lowercase imported as non lowercase + "TCH002", # move third-party into a type-checking block + "F722", # jaxtyping +] + +[lint.flake8-tidy-imports] +ban-relative-imports = "all" + +[lint.isort] +lines-after-imports = 2 +known-first-party = ["tests"] + +[lint.pydocstyle] +convention = "google" diff --git a/LICENSE b/LICENSE index caebf45..1ee08b6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 RationAI +Copyright (c) 2024 RationAI Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index cd979cd..e79c243 100644 --- a/README.md +++ b/README.md @@ -1,58 +1 @@ -# Python project template - -This project template serves as a robust foundation for Python projects, promoting best practices and streamlining development workflows. It comes preconfigured with essential tools and features to enhance the development experience. - -## Tools Included - -- [PDM](https://pdm-project.org) for efficient dependency management. -- [Ruff](https://docs.astral.sh/ruff) for comprehensive linting and code formatting. -- [Pytest](https://docs.pytest.org) for running tests and ensuring code reliability. -- [pre-commit](https://pre-commit.com) for managing pre-commit git hooks ([Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), ruff check, ruff format) -- [GitLab CI/CD](https://docs.gitlab.com/ee/ci) for continuous integration. -- [Pylint](https://pylint.readthedocs.io) for static code analysis, adhering to the [Google style](https://google.github.io/styleguide/pyguide.html). -- [Pydocstyle](https://www.pydocstyle.org) for validating docstring styles, also following the [Google style](https://google.github.io/styleguide/pyguide.html#s3.8-comments-and-docstrings). -- [Mypy](https://mypy-lang.org) for static type checking. - - -## Usage - -Key commands for effective project management: - -- `pdm install` - Installs all project dependencies. -- `pdm add ` - Adds a new dependency to the project. -- `pdm l` - Runs linting, formatting, and mypy checks. -- `pdm test` - Executes tests located in the tests directory. -- `pdm run ` - Runs the specified command within the virtual environment. - -## CI/CD - -Two defined pipelines enhance project development: - -1. **Synchronization pipeline(`ci/template` branch):** - - Triggered by a scheduler for the ci/template branch. - - Handles [synchronization](#synchronizing-the-template) with the template repository. - -2. **Project Pipeline (Main branch and Merge Requests):** - - Triggered by any push to the main branch or any merge request to the main branch. - - Consists of the following stages: - - `lint` - Runs linting, formatting, and mypy checks (ruff check, ruff format, mypy). - - `test` - Executes tests using Pytest. - - -## Synchronizing the Template - -For seamless integration with the template repository: - -1. Create a protected branch named `ci/template`. -2. [Schedule a pipeline](https://docs.gitlab.com/ee/ci/pipelines/schedules.html) for the `ci/template` branch to automatically synchronize with the template repository. -3. Ensure the `ci/template` branch tracks the last synchronized commit. -4. The synchronization pipeline will: - - Check out the last synchronized commit. - - Merge the latest commit from the template repository, prioritizing template changes in case of conflicts. - - Commit and push the changes to the `ci/template` branch. - - Create a merge request to the default branch. - - - -> **Note:** -> Resolve any conflicts manually, ensuring the **last synchronized commit** is retained in the ci/template branch to prevent reappearing conflicts during subsequent synchronizations. +# Staining \ No newline at end of file diff --git a/pdm.lock b/pdm.lock new file mode 100644 index 0000000..e37f981 --- /dev/null +++ b/pdm.lock @@ -0,0 +1,362 @@ +# This file is @generated by PDM. +# It is not intended for manual editing. + +[metadata] +groups = ["default", "dev", "test"] +strategy = ["cross_platform", "inherit_metadata"] +lock_version = "4.4.1" +content_hash = "sha256:a47116b50eb36761e82c588a7bd1e9db7d133253b95d0a734e324389a42b7b90" + +[[package]] +name = "cfgv" +version = "3.4.0" +requires_python = ">=3.8" +summary = "Validate configuration and produce human readable error messages." +groups = ["dev"] +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 = "colorama" +version = "0.4.6" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +summary = "Cross-platform colored terminal text." +groups = ["test"] +marker = "sys_platform == \"win32\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.5.1" +requires_python = ">=3.8" +summary = "Code coverage measurement for Python" +groups = ["test"] +files = [ + {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"}, + {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"}, + {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"}, + {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"}, + {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"}, + {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"}, + {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"}, + {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"}, + {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"}, + {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"}, +] + +[[package]] +name = "coverage" +version = "7.5.1" +extras = ["toml"] +requires_python = ">=3.8" +summary = "Code coverage measurement for Python" +groups = ["test"] +dependencies = [ + "coverage==7.5.1", +] +files = [ + {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"}, + {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"}, + {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"}, + {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"}, + {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"}, + {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"}, + {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"}, + {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"}, + {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"}, + {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"}, +] + +[[package]] +name = "distlib" +version = "0.3.8" +summary = "Distribution utilities" +groups = ["dev"] +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + +[[package]] +name = "filelock" +version = "3.14.0" +requires_python = ">=3.8" +summary = "A platform independent file lock." +groups = ["dev"] +files = [ + {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, + {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, +] + +[[package]] +name = "identify" +version = "2.5.36" +requires_python = ">=3.8" +summary = "File identification library for Python" +groups = ["dev"] +files = [ + {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"}, + {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +requires_python = ">=3.7" +summary = "brain-dead simple config-ini parsing" +groups = ["test"] +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "mypy" +version = "1.10.0" +requires_python = ">=3.8" +summary = "Optional static typing for Python" +groups = ["dev"] +dependencies = [ + "mypy-extensions>=1.0.0", + "typing-extensions>=4.1.0", +] +files = [ + {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"}, + {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"}, + {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"}, + {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"}, + {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"}, + {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"}, + {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"}, + {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"}, + {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"}, + {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"}, + {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"}, + {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +requires_python = ">=3.5" +summary = "Type system extensions for programs checked with the mypy type checker." +groups = ["dev"] +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "nodeenv" +version = "1.8.0" +requires_python = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +summary = "Node.js virtual environment builder" +groups = ["dev"] +dependencies = [ + "setuptools", +] +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]] +name = "packaging" +version = "24.0" +requires_python = ">=3.7" +summary = "Core utilities for Python packages" +groups = ["test"] +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "platformdirs" +version = "4.2.2" +requires_python = ">=3.8" +summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +groups = ["dev"] +files = [ + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +requires_python = ">=3.8" +summary = "plugin and hook calling mechanisms for python" +groups = ["test"] +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[[package]] +name = "pre-commit" +version = "3.7.1" +requires_python = ">=3.9" +summary = "A framework for managing and maintaining multi-language pre-commit hooks." +groups = ["dev"] +dependencies = [ + "cfgv>=2.0.0", + "identify>=1.0.0", + "nodeenv>=0.11.1", + "pyyaml>=5.1", + "virtualenv>=20.10.0", +] +files = [ + {file = "pre_commit-3.7.1-py2.py3-none-any.whl", hash = "sha256:fae36fd1d7ad7d6a5a1c0b0d5adb2ed1a3bda5a21bf6c3e5372073d7a11cd4c5"}, + {file = "pre_commit-3.7.1.tar.gz", hash = "sha256:8ca3ad567bc78a4972a3f1a477e94a79d4597e8140a6e0b651c5e33899c3654a"}, +] + +[[package]] +name = "pytest" +version = "8.2.1" +requires_python = ">=3.8" +summary = "pytest: simple powerful testing with Python" +groups = ["test"] +dependencies = [ + "colorama; sys_platform == \"win32\"", + "iniconfig", + "packaging", + "pluggy<2.0,>=1.5", +] +files = [ + {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, + {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, +] + +[[package]] +name = "pytest-cov" +version = "5.0.0" +requires_python = ">=3.8" +summary = "Pytest plugin for measuring coverage." +groups = ["test"] +dependencies = [ + "coverage[toml]>=5.2.1", + "pytest>=4.6", +] +files = [ + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.1" +requires_python = ">=3.6" +summary = "YAML parser and emitter for Python" +groups = ["dev"] +files = [ + {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-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {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-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "ruff" +version = "0.4.5" +requires_python = ">=3.7" +summary = "An extremely fast Python linter and code formatter, written in Rust." +groups = ["dev"] +files = [ + {file = "ruff-0.4.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8f58e615dec58b1a6b291769b559e12fdffb53cc4187160a2fc83250eaf54e96"}, + {file = "ruff-0.4.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:84dd157474e16e3a82745d2afa1016c17d27cb5d52b12e3d45d418bcc6d49264"}, + {file = "ruff-0.4.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25f483ad9d50b00e7fd577f6d0305aa18494c6af139bce7319c68a17180087f4"}, + {file = "ruff-0.4.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:63fde3bf6f3ad4e990357af1d30e8ba2730860a954ea9282c95fc0846f5f64af"}, + {file = "ruff-0.4.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78e3ba4620dee27f76bbcad97067766026c918ba0f2d035c2fc25cbdd04d9c97"}, + {file = "ruff-0.4.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:441dab55c568e38d02bbda68a926a3d0b54f5510095c9de7f95e47a39e0168aa"}, + {file = "ruff-0.4.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1169e47e9c4136c997f08f9857ae889d614c5035d87d38fda9b44b4338909cdf"}, + {file = "ruff-0.4.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:755ac9ac2598a941512fc36a9070a13c88d72ff874a9781493eb237ab02d75df"}, + {file = "ruff-0.4.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4b02a65985be2b34b170025a8b92449088ce61e33e69956ce4d316c0fe7cce0"}, + {file = "ruff-0.4.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:75a426506a183d9201e7e5664de3f6b414ad3850d7625764106f7b6d0486f0a1"}, + {file = "ruff-0.4.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6e1b139b45e2911419044237d90b60e472f57285950e1492c757dfc88259bb06"}, + {file = "ruff-0.4.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a6f29a8221d2e3d85ff0c7b4371c0e37b39c87732c969b4d90f3dad2e721c5b1"}, + {file = "ruff-0.4.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d6ef817124d72b54cc923f3444828ba24fa45c3164bc9e8f1813db2f3d3a8a11"}, + {file = "ruff-0.4.5-py3-none-win32.whl", hash = "sha256:aed8166c18b1a169a5d3ec28a49b43340949e400665555b51ee06f22813ef062"}, + {file = "ruff-0.4.5-py3-none-win_amd64.whl", hash = "sha256:b0b03c619d2b4350b4a27e34fd2ac64d0dabe1afbf43de57d0f9d8a05ecffa45"}, + {file = "ruff-0.4.5-py3-none-win_arm64.whl", hash = "sha256:9d15de3425f53161b3f5a5658d4522e4eee5ea002bf2ac7aa380743dd9ad5fba"}, + {file = "ruff-0.4.5.tar.gz", hash = "sha256:286eabd47e7d4d521d199cab84deca135557e6d1e0f0d01c29e757c3cb151b54"}, +] + +[[package]] +name = "setuptools" +version = "70.0.0" +requires_python = ">=3.8" +summary = "Easily download, build, install, upgrade, and uninstall Python packages" +groups = ["dev"] +files = [ + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.0" +requires_python = ">=3.8" +summary = "Backported and Experimental Type Hints for Python 3.8+" +groups = ["dev"] +files = [ + {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"}, + {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"}, +] + +[[package]] +name = "virtualenv" +version = "20.26.2" +requires_python = ">=3.7" +summary = "Virtual Python Environment builder" +groups = ["dev"] +dependencies = [ + "distlib<1,>=0.3.7", + "filelock<4,>=3.12.2", + "platformdirs<5,>=3.9.1", +] +files = [ + {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"}, + {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"}, +] diff --git a/pyproject.toml b/pyproject.toml index d9c3da4..309b89a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,105 +1,24 @@ [project] -name = "" +name = "rationai-staining" version = "0.1.0" description = "" authors = [] requires-python = ">=3.11" readme = "README.md" license = { file = "LICENSE" } - - -##################################### -# Dependencies -##################################### - dependencies = [] [tool.pdm.dev-dependencies] dev = ["mypy", "pre-commit", "ruff"] test = ["pytest", "pytest-cov"] - -##################################### -# Scripts -##################################### - [tool.pdm.scripts] l = { composite = ["lint", "format", "mypy"] } test = "pytest tests" -lint = "ruff check ." -format = "ruff format ." +lint = "ruff check" +format = "ruff format" mypy = "mypy ." post_install = { composite = [ "pre-commit install", "pre-commit install --hook-type commit-msg", ] } - - -##################################### -# Tool configuration -##################################### - -[tool.coverage.run] -branch = true -omit = ["tests/*"] -source = ["."] - -[tool.coverage.report] -exclude_also = [ - # Don't complain about abstract methods, they aren't run: - "@(abc\\.)?abstractmethod", -] - -[tool.mypy] -strict = true -ignore_missing_imports = true - -[tool.ruff] -fix = true -line-length = 88 -target-version = "py311" -extend-ignore = [ - "ERA001", # commented out code - "D100", # missing docstring in public module - "D101", # missing docstring in public class - "D102", # missing docstring in public method - "D103", # missing docstring in public function - "D104", # missing docstring in public package - "D105", # missing docstring in magic method - "D106", # missing docstring in public nested class - "D107", # missing docstring in __init__ -] - -[tool.ruff.format] -# Enable reformatting of code snippets in docstrings. -docstring-code-format = true - -[tool.ruff.lint] -unfixable = [ - "ERA", # do not autoremove commented out code -] -extend-select = [ - "B", # flake8-bugbear - "C4", # flake8-comprehensions - "ERA", # flake8-eradicate/eradicate - "I", # isort - "N", # pep8-naming - "PIE", # flake8-pie - "PGH", # pygrep - "RUF", # ruff checks - "SIM", # flake8-simplify - "TCH", # flake8-type-checking - "TID", # flake8-tidy-imports - "UP", # pyupgrade - "D", # pydocstyle -] - -[tool.ruff.lint.flake8-tidy-imports] -ban-relative-imports = "all" - -[tool.ruff.lint.isort] -lines-after-imports = 2 -known-first-party = ["tests"] - -[tool.ruff.lint.pydocstyle] -convention = "google" diff --git a/rationai/staining/__init__.py b/rationai/staining/__init__.py new file mode 100644 index 0000000..e69de29 From 168b7957966b639e18c08f5d4a00df225a692b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Bal=C3=A1k?= <525126@mail.muni.cz> Date: Sat, 17 Aug 2024 14:50:24 +0200 Subject: [PATCH 21/85] feat: routines for separating and combining stain channels, and stain estimation --- .gitlab-ci.yml | 2 + .mypy.ini | 3 +- .pre-commit-config.yaml | 13 +- README.md | 4 +- docs/index.md | 1 + docs/installation.md | 13 + docs/reference/color-conversion.md | 1 + docs/reference/constants.md | 1 + docs/reference/estimate-stain-vectors.md | 1 + docs/reference/index.md | 4 + docs/reference/typing.md | 1 + mkdocs.yml | 68 +++ pdm.lock | 510 ++++++++++++++------ pyproject.toml | 13 +- pytest.ini | 6 + rationai/staining/__init__.py | 14 + rationai/staining/constants.py | 14 + rationai/staining/convert_color.py | 135 ++++++ rationai/staining/estimate_stain_vectors.py | 63 +++ rationai/staining/typing.py | 36 ++ rationai/staining/utils/__init__.py | 6 + rationai/staining/utils/inv_mat.py | 8 + rationai/staining/utils/normalize.py | 9 + rationai/staining/utils/residual.py | 12 + tests/__init__.py | 0 tests/conftest.py | 42 ++ tests/data/generated/h_dab.jpg | Bin 0 -> 43338 bytes tests/data/generated/h_e.jpg | Bin 0 -> 42565 bytes tests/data/sample1/eosin.png | Bin 0 -> 183161 bytes tests/data/sample1/hematoxylin.png | Bin 0 -> 203258 bytes tests/data/sample1/original.jpg | Bin 0 -> 83398 bytes tests/data/sample1/residual.png | Bin 0 -> 79895 bytes tests/scripts/generate_artifical_tissue.py | 68 +++ tests/test_color_conversion.py | 31 ++ tests/test_stain_estimator.py | 105 ++++ tests/test_stain_reconstruction.py | 40 ++ tests/test_stain_separation.py | 52 ++ 37 files changed, 1127 insertions(+), 149 deletions(-) create mode 100644 docs/index.md create mode 100644 docs/installation.md create mode 100644 docs/reference/color-conversion.md create mode 100644 docs/reference/constants.md create mode 100644 docs/reference/estimate-stain-vectors.md create mode 100644 docs/reference/index.md create mode 100644 docs/reference/typing.md create mode 100644 mkdocs.yml create mode 100644 pytest.ini create mode 100644 rationai/staining/constants.py create mode 100644 rationai/staining/convert_color.py create mode 100644 rationai/staining/estimate_stain_vectors.py create mode 100644 rationai/staining/typing.py create mode 100644 rationai/staining/utils/__init__.py create mode 100644 rationai/staining/utils/inv_mat.py create mode 100644 rationai/staining/utils/normalize.py create mode 100644 rationai/staining/utils/residual.py delete mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/data/generated/h_dab.jpg create mode 100644 tests/data/generated/h_e.jpg create mode 100644 tests/data/sample1/eosin.png create mode 100644 tests/data/sample1/hematoxylin.png create mode 100644 tests/data/sample1/original.jpg create mode 100644 tests/data/sample1/residual.png create mode 100644 tests/scripts/generate_artifical_tissue.py create mode 100644 tests/test_color_conversion.py create mode 100644 tests/test_stain_estimator.py create mode 100644 tests/test_stain_reconstruction.py create mode 100644 tests/test_stain_separation.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1675c5c..9d40d40 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,7 +3,9 @@ include: file: - Pytest.gitlab-ci.yml - Python-Lint.gitlab-ci.yml + - MkDocs.gitlab-ci.yml stages: - lint - test + - deploy diff --git a/.mypy.ini b/.mypy.ini index 1c6f44e..8d15f3c 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -2,4 +2,5 @@ strict = True ignore_missing_imports = True disallow_untyped_calls = False -disable_error_code = no-any-return \ No newline at end of file +disable_error_code = no-any-return +explicit_package_bases = True \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a602928..83fa055 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,25 +2,24 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-yaml args: [--unsafe] - repo: https://github.com/compilerla/conventional-pre-commit - rev: v3.0.0 + rev: v3.4.0 hooks: - id: conventional-pre-commit stages: [commit-msg] args: [] # optional: list of Conventional Commits types to allow e.g. [feat, fix, ci, chore, test] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.9 + rev: v0.6.1 hooks: # Run the linter. - id: ruff - types_or: [python, pyi, jupyter] - args: [--fix] - # Run the formatter. + entry: pdm lint --force-exclude + # Run the formatter. - id: ruff-format - types_or: [python, pyi, jupyter] + entry: pdm format --force-exclude diff --git a/README.md b/README.md index e79c243..1328bc6 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ -# Staining \ No newline at end of file +# Staining + +[[`Documentation`](https://rationai.gitlab-pages.ics.muni.cz/digital-pathology/libraries/staining)] \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..00dd3b4 --- /dev/null +++ b/docs/index.md @@ -0,0 +1 @@ +Staining library \ No newline at end of file diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..15a16f7 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,13 @@ +# Installation + +=== "pip" + + ```bash + pip install git+ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git + ``` + +=== "pdm" + + ```bash + pdm add git+ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git + ``` diff --git a/docs/reference/color-conversion.md b/docs/reference/color-conversion.md new file mode 100644 index 0000000..43b6952 --- /dev/null +++ b/docs/reference/color-conversion.md @@ -0,0 +1 @@ +::: rationai.staining.convert_color \ No newline at end of file diff --git a/docs/reference/constants.md b/docs/reference/constants.md new file mode 100644 index 0000000..616707f --- /dev/null +++ b/docs/reference/constants.md @@ -0,0 +1 @@ +::: rationai.staining.constants diff --git a/docs/reference/estimate-stain-vectors.md b/docs/reference/estimate-stain-vectors.md new file mode 100644 index 0000000..520ea02 --- /dev/null +++ b/docs/reference/estimate-stain-vectors.md @@ -0,0 +1 @@ +::: rationai.staining.estimate_stain_vectors \ No newline at end of file diff --git a/docs/reference/index.md b/docs/reference/index.md new file mode 100644 index 0000000..47a1bf1 --- /dev/null +++ b/docs/reference/index.md @@ -0,0 +1,4 @@ +# Reference + +Here's the reference or code API, the classes, functions, parameters, attributes, and +all the `rationai-staining` parts you can use in your applications. \ No newline at end of file diff --git a/docs/reference/typing.md b/docs/reference/typing.md new file mode 100644 index 0000000..36b0bbe --- /dev/null +++ b/docs/reference/typing.md @@ -0,0 +1 @@ +::: rationai.staining.typing \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..7df5f07 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,68 @@ +site_name: Staining + +nav: + - Home: index.md + - Getting Started: + - Installation: installation.md + - Reference: + - reference/index.md + - reference/color-conversion.md + - reference/estimate-stain-vectors.md + - reference/constants.md + - reference/typing.md + +theme: + name: material + features: + - announce.dismiss + - content.action.edit + - content.action.view + - content.code.annotate + - content.code.copy + - content.tooltips + - navigation.indexes + - navigation.sections + - navigation.top + - navigation.tabs + - navigation.footer + - navigation.tracking + - search.highlight + - search.share + - search.suggest + - toc.follow + palette: + - media: "(prefers-color-scheme)" + toggle: + icon: material/link + name: Switch to light mode + - media: "(prefers-color-scheme: light)" + scheme: default + primary: indigo + accent: indigo + toggle: + icon: material/toggle-switch + name: Switch to dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: black + accent: indigo + toggle: + icon: material/toggle-switch-off + name: Switch to system preference + +plugins: + - search + - mkdocstrings + +markdown_extensions: + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.tabbed: + alternate_style: true + combine_header_slug: true + slugify: !!python/object/apply:pymdownx.slugs.slugify + kwds: + case: lower diff --git a/pdm.lock b/pdm.lock index e37f981..e698408 100644 --- a/pdm.lock +++ b/pdm.lock @@ -3,9 +3,12 @@ [metadata] groups = ["default", "dev", "test"] -strategy = ["cross_platform", "inherit_metadata"] -lock_version = "4.4.1" -content_hash = "sha256:a47116b50eb36761e82c588a7bd1e9db7d133253b95d0a734e324389a42b7b90" +strategy = ["inherit_metadata"] +lock_version = "4.5.0" +content_hash = "sha256:a7dd9cc2a5c546a0337b5d1b9ab80d4197f0e484eeb9bbd3c3dcad0709af7758" + +[[metadata.targets]] +requires_python = ">=3.11" [[package]] name = "cfgv" @@ -32,68 +35,107 @@ files = [ [[package]] name = "coverage" -version = "7.5.1" +version = "7.6.1" requires_python = ">=3.8" summary = "Code coverage measurement for Python" groups = ["test"] files = [ - {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"}, - {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"}, - {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"}, - {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"}, - {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"}, - {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"}, - {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"}, - {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"}, - {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"}, - {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, + {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, + {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, + {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, + {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, + {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, + {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, + {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, + {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, + {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, ] [[package]] name = "coverage" -version = "7.5.1" +version = "7.6.1" extras = ["toml"] requires_python = ">=3.8" summary = "Code coverage measurement for Python" groups = ["test"] dependencies = [ - "coverage==7.5.1", + "coverage==7.6.1", + "tomli; python_full_version <= \"3.11.0a6\"", ] files = [ - {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"}, - {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"}, - {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"}, - {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"}, - {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"}, - {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"}, - {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"}, - {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"}, - {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"}, - {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, + {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, + {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, + {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, + {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, + {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, + {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, + {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, + {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, + {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, ] [[package]] @@ -108,24 +150,39 @@ files = [ [[package]] name = "filelock" -version = "3.14.0" +version = "3.15.4" requires_python = ">=3.8" summary = "A platform independent file lock." groups = ["dev"] files = [ - {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, - {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, + {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, + {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, ] [[package]] name = "identify" -version = "2.5.36" +version = "2.6.0" requires_python = ">=3.8" summary = "File identification library for Python" groups = ["dev"] files = [ - {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"}, - {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"}, + {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, + {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, +] + +[[package]] +name = "imageio" +version = "2.35.0" +requires_python = ">=3.8" +summary = "Library for reading and writing a wide range of image, video, scientific, and volumetric data formats." +groups = ["default"] +dependencies = [ + "numpy<2.0.0", + "pillow>=8.3.2", +] +files = [ + {file = "imageio-2.35.0-py3-none-any.whl", hash = "sha256:ebfb39c0ad7acf93750d7ee151664238aaa9208f202288ea6f3ca7cb0ed719c3"}, + {file = "imageio-2.35.0.tar.gz", hash = "sha256:e88f759803d2968e19a55fa8372e9fbe12967a8598fe042d8719ca46d03f4256"}, ] [[package]] @@ -139,29 +196,45 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "lazy-loader" +version = "0.4" +requires_python = ">=3.7" +summary = "Makes it easy to load subpackages and functions on demand." +groups = ["default"] +dependencies = [ + "importlib-metadata; python_version < \"3.8\"", + "packaging", +] +files = [ + {file = "lazy_loader-0.4-py3-none-any.whl", hash = "sha256:342aa8e14d543a154047afb4ba8ef17f5563baad3fc610d7b15b213b0f119efc"}, + {file = "lazy_loader-0.4.tar.gz", hash = "sha256:47c75182589b91a4e1a85a136c074285a5ad4d9f39c63e0d7fb76391c4574cd1"}, +] + [[package]] name = "mypy" -version = "1.10.0" +version = "1.11.1" requires_python = ">=3.8" summary = "Optional static typing for Python" groups = ["dev"] dependencies = [ "mypy-extensions>=1.0.0", - "typing-extensions>=4.1.0", + "tomli>=1.1.0; python_version < \"3.11\"", + "typing-extensions>=4.6.0", ] files = [ - {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"}, - {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"}, - {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"}, - {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"}, - {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"}, - {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"}, - {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"}, - {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"}, - {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"}, - {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"}, - {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"}, - {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"}, + {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, + {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, + {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, + {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, + {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, + {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, + {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, + {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, + {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, + {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, + {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, + {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, ] [[package]] @@ -175,29 +248,116 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "networkx" +version = "3.3" +requires_python = ">=3.10" +summary = "Python package for creating and manipulating graphs and networks" +groups = ["default"] +files = [ + {file = "networkx-3.3-py3-none-any.whl", hash = "sha256:28575580c6ebdaf4505b22c6256a2b9de86b316dc63ba9e93abde3d78dfdbcf2"}, + {file = "networkx-3.3.tar.gz", hash = "sha256:0c127d8b2f4865f59ae9cb8aafcd60b5c70f3241ebd66f7defad7c4ab90126c9"}, +] + [[package]] name = "nodeenv" -version = "1.8.0" -requires_python = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +version = "1.9.1" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" summary = "Node.js virtual environment builder" groups = ["dev"] -dependencies = [ - "setuptools", +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, ] + +[[package]] +name = "numpy" +version = "1.26.4" +requires_python = ">=3.9" +summary = "Fundamental package for array computing in Python" +groups = ["default"] files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, ] [[package]] name = "packaging" -version = "24.0" -requires_python = ">=3.7" +version = "24.1" +requires_python = ">=3.8" summary = "Core utilities for Python packages" +groups = ["default", "test"] +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "perlin-noise" +version = "1.13" +summary = "Python implementation for Perlin Noise with unlimited coordinates space" groups = ["test"] files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, + {file = "perlin_noise-1.13-py3-none-any.whl", hash = "sha256:09f274c8da38f5a2a48b1626032a1b7738990df758a3bc7549ff3464869c6ca4"}, + {file = "perlin_noise-1.13.tar.gz", hash = "sha256:0e0f074743336fbe9cf2cdbdeb74c75d43780a8b3cbc3415859cd396ab4da177"}, +] + +[[package]] +name = "pillow" +version = "10.4.0" +requires_python = ">=3.8" +summary = "Python Imaging Library (Fork)" +groups = ["default"] +files = [ + {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, + {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, + {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, + {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, + {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, + {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, + {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, + {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, + {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, + {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, + {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, ] [[package]] @@ -224,7 +384,7 @@ files = [ [[package]] name = "pre-commit" -version = "3.7.1" +version = "3.8.0" requires_python = ">=3.9" summary = "A framework for managing and maintaining multi-language pre-commit hooks." groups = ["dev"] @@ -236,25 +396,27 @@ dependencies = [ "virtualenv>=20.10.0", ] files = [ - {file = "pre_commit-3.7.1-py2.py3-none-any.whl", hash = "sha256:fae36fd1d7ad7d6a5a1c0b0d5adb2ed1a3bda5a21bf6c3e5372073d7a11cd4c5"}, - {file = "pre_commit-3.7.1.tar.gz", hash = "sha256:8ca3ad567bc78a4972a3f1a477e94a79d4597e8140a6e0b651c5e33899c3654a"}, + {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, + {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, ] [[package]] name = "pytest" -version = "8.2.1" +version = "8.3.2" requires_python = ">=3.8" summary = "pytest: simple powerful testing with Python" groups = ["test"] dependencies = [ "colorama; sys_platform == \"win32\"", + "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", "iniconfig", "packaging", - "pluggy<2.0,>=1.5", + "pluggy<2,>=1.5", + "tomli>=1; python_version < \"3.11\"", ] files = [ - {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, - {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, + {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, + {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, ] [[package]] @@ -274,89 +436,165 @@ files = [ [[package]] name = "pyyaml" -version = "6.0.1" -requires_python = ">=3.6" +version = "6.0.2" +requires_python = ">=3.8" summary = "YAML parser and emitter for Python" groups = ["dev"] files = [ - {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-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {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-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] [[package]] name = "ruff" -version = "0.4.5" +version = "0.6.1" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["dev"] files = [ - {file = "ruff-0.4.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8f58e615dec58b1a6b291769b559e12fdffb53cc4187160a2fc83250eaf54e96"}, - {file = "ruff-0.4.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:84dd157474e16e3a82745d2afa1016c17d27cb5d52b12e3d45d418bcc6d49264"}, - {file = "ruff-0.4.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25f483ad9d50b00e7fd577f6d0305aa18494c6af139bce7319c68a17180087f4"}, - {file = "ruff-0.4.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:63fde3bf6f3ad4e990357af1d30e8ba2730860a954ea9282c95fc0846f5f64af"}, - {file = "ruff-0.4.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78e3ba4620dee27f76bbcad97067766026c918ba0f2d035c2fc25cbdd04d9c97"}, - {file = "ruff-0.4.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:441dab55c568e38d02bbda68a926a3d0b54f5510095c9de7f95e47a39e0168aa"}, - {file = "ruff-0.4.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1169e47e9c4136c997f08f9857ae889d614c5035d87d38fda9b44b4338909cdf"}, - {file = "ruff-0.4.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:755ac9ac2598a941512fc36a9070a13c88d72ff874a9781493eb237ab02d75df"}, - {file = "ruff-0.4.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4b02a65985be2b34b170025a8b92449088ce61e33e69956ce4d316c0fe7cce0"}, - {file = "ruff-0.4.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:75a426506a183d9201e7e5664de3f6b414ad3850d7625764106f7b6d0486f0a1"}, - {file = "ruff-0.4.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6e1b139b45e2911419044237d90b60e472f57285950e1492c757dfc88259bb06"}, - {file = "ruff-0.4.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a6f29a8221d2e3d85ff0c7b4371c0e37b39c87732c969b4d90f3dad2e721c5b1"}, - {file = "ruff-0.4.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d6ef817124d72b54cc923f3444828ba24fa45c3164bc9e8f1813db2f3d3a8a11"}, - {file = "ruff-0.4.5-py3-none-win32.whl", hash = "sha256:aed8166c18b1a169a5d3ec28a49b43340949e400665555b51ee06f22813ef062"}, - {file = "ruff-0.4.5-py3-none-win_amd64.whl", hash = "sha256:b0b03c619d2b4350b4a27e34fd2ac64d0dabe1afbf43de57d0f9d8a05ecffa45"}, - {file = "ruff-0.4.5-py3-none-win_arm64.whl", hash = "sha256:9d15de3425f53161b3f5a5658d4522e4eee5ea002bf2ac7aa380743dd9ad5fba"}, - {file = "ruff-0.4.5.tar.gz", hash = "sha256:286eabd47e7d4d521d199cab84deca135557e6d1e0f0d01c29e757c3cb151b54"}, + {file = "ruff-0.6.1-py3-none-linux_armv6l.whl", hash = "sha256:b4bb7de6a24169dc023f992718a9417380301b0c2da0fe85919f47264fb8add9"}, + {file = "ruff-0.6.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:45efaae53b360c81043e311cdec8a7696420b3d3e8935202c2846e7a97d4edae"}, + {file = "ruff-0.6.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:bc60c7d71b732c8fa73cf995efc0c836a2fd8b9810e115be8babb24ae87e0850"}, + {file = "ruff-0.6.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c7477c3b9da822e2db0b4e0b59e61b8a23e87886e727b327e7dcaf06213c5cf"}, + {file = "ruff-0.6.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a0af7ab3f86e3dc9f157a928e08e26c4b40707d0612b01cd577cc84b8905cc9"}, + {file = "ruff-0.6.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:392688dbb50fecf1bf7126731c90c11a9df1c3a4cdc3f481b53e851da5634fa5"}, + {file = "ruff-0.6.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5278d3e095ccc8c30430bcc9bc550f778790acc211865520f3041910a28d0024"}, + {file = "ruff-0.6.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe6d5f65d6f276ee7a0fc50a0cecaccb362d30ef98a110f99cac1c7872df2f18"}, + {file = "ruff-0.6.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2e0dd11e2ae553ee5c92a81731d88a9883af8db7408db47fc81887c1f8b672e"}, + {file = "ruff-0.6.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d812615525a34ecfc07fd93f906ef5b93656be01dfae9a819e31caa6cfe758a1"}, + {file = "ruff-0.6.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:faaa4060f4064c3b7aaaa27328080c932fa142786f8142aff095b42b6a2eb631"}, + {file = "ruff-0.6.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:99d7ae0df47c62729d58765c593ea54c2546d5de213f2af2a19442d50a10cec9"}, + {file = "ruff-0.6.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9eb18dfd7b613eec000e3738b3f0e4398bf0153cb80bfa3e351b3c1c2f6d7b15"}, + {file = "ruff-0.6.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c62bc04c6723a81e25e71715aa59489f15034d69bf641df88cb38bdc32fd1dbb"}, + {file = "ruff-0.6.1-py3-none-win32.whl", hash = "sha256:9fb4c4e8b83f19c9477a8745e56d2eeef07a7ff50b68a6998f7d9e2e3887bdc4"}, + {file = "ruff-0.6.1-py3-none-win_amd64.whl", hash = "sha256:c2ebfc8f51ef4aca05dad4552bbcf6fe8d1f75b2f6af546cc47cc1c1ca916b5b"}, + {file = "ruff-0.6.1-py3-none-win_arm64.whl", hash = "sha256:3bc81074971b0ffad1bd0c52284b22411f02a11a012082a76ac6da153536e014"}, + {file = "ruff-0.6.1.tar.gz", hash = "sha256:af3ffd8c6563acb8848d33cd19a69b9bfe943667f0419ca083f8ebe4224a3436"}, ] [[package]] -name = "setuptools" -version = "70.0.0" -requires_python = ">=3.8" -summary = "Easily download, build, install, upgrade, and uninstall Python packages" -groups = ["dev"] +name = "scikit-image" +version = "0.24.0" +requires_python = ">=3.9" +summary = "Image processing in Python" +groups = ["default"] +dependencies = [ + "imageio>=2.33", + "lazy-loader>=0.4", + "networkx>=2.8", + "numpy>=1.23", + "packaging>=21", + "pillow>=9.1", + "scipy>=1.9", + "tifffile>=2022.8.12", +] +files = [ + {file = "scikit_image-0.24.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:272909e02a59cea3ed4aa03739bb88df2625daa809f633f40b5053cf09241831"}, + {file = "scikit_image-0.24.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:190ebde80b4470fe8838764b9b15f232a964f1a20391663e31008d76f0c696f7"}, + {file = "scikit_image-0.24.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59c98cc695005faf2b79904e4663796c977af22586ddf1b12d6af2fa22842dc2"}, + {file = "scikit_image-0.24.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa27b3a0dbad807b966b8db2d78da734cb812ca4787f7fbb143764800ce2fa9c"}, + {file = "scikit_image-0.24.0-cp311-cp311-win_amd64.whl", hash = "sha256:dacf591ac0c272a111181afad4b788a27fe70d213cfddd631d151cbc34f8ca2c"}, + {file = "scikit_image-0.24.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6fccceb54c9574590abcddc8caf6cefa57c13b5b8b4260ab3ff88ad8f3c252b3"}, + {file = "scikit_image-0.24.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ccc01e4760d655aab7601c1ba7aa4ddd8b46f494ac46ec9c268df6f33ccddf4c"}, + {file = "scikit_image-0.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18836a18d3a7b6aca5376a2d805f0045826bc6c9fc85331659c33b4813e0b563"}, + {file = "scikit_image-0.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8579bda9c3f78cb3b3ed8b9425213c53a25fa7e994b7ac01f2440b395babf660"}, + {file = "scikit_image-0.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:82ab903afa60b2da1da2e6f0c8c65e7c8868c60a869464c41971da929b3e82bc"}, + {file = "scikit_image-0.24.0.tar.gz", hash = "sha256:5d16efe95da8edbeb363e0c4157b99becbd650a60b77f6e3af5768b66cf007ab"}, +] + +[[package]] +name = "scipy" +version = "1.14.0" +requires_python = ">=3.10" +summary = "Fundamental algorithms for scientific computing in Python" +groups = ["default"] +dependencies = [ + "numpy<2.3,>=1.23.5", +] +files = [ + {file = "scipy-1.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6d056a8709ccda6cf36cdd2eac597d13bc03dba38360f418560a93050c76a16e"}, + {file = "scipy-1.14.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f0a50da861a7ec4573b7c716b2ebdcdf142b66b756a0d392c236ae568b3a93fb"}, + {file = "scipy-1.14.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:94c164a9e2498e68308e6e148646e486d979f7fcdb8b4cf34b5441894bdb9caf"}, + {file = "scipy-1.14.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a7d46c3e0aea5c064e734c3eac5cf9eb1f8c4ceee756262f2c7327c4c2691c86"}, + {file = "scipy-1.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9eee2989868e274aae26125345584254d97c56194c072ed96cb433f32f692ed8"}, + {file = "scipy-1.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3154691b9f7ed73778d746da2df67a19d046a6c8087c8b385bc4cdb2cfca74"}, + {file = "scipy-1.14.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c40003d880f39c11c1edbae8144e3813904b10514cd3d3d00c277ae996488cdb"}, + {file = "scipy-1.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:5b083c8940028bb7e0b4172acafda6df762da1927b9091f9611b0bcd8676f2bc"}, + {file = "scipy-1.14.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bff2438ea1330e06e53c424893ec0072640dac00f29c6a43a575cbae4c99b2b9"}, + {file = "scipy-1.14.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:bbc0471b5f22c11c389075d091d3885693fd3f5e9a54ce051b46308bc787e5d4"}, + {file = "scipy-1.14.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:64b2ff514a98cf2bb734a9f90d32dc89dc6ad4a4a36a312cd0d6327170339eb0"}, + {file = "scipy-1.14.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:7d3da42fbbbb860211a811782504f38ae7aaec9de8764a9bef6b262de7a2b50f"}, + {file = "scipy-1.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d91db2c41dd6c20646af280355d41dfa1ec7eead235642178bd57635a3f82209"}, + {file = "scipy-1.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a01cc03bcdc777c9da3cfdcc74b5a75caffb48a6c39c8450a9a05f82c4250a14"}, + {file = "scipy-1.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:65df4da3c12a2bb9ad52b86b4dcf46813e869afb006e58be0f516bc370165159"}, + {file = "scipy-1.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:4c4161597c75043f7154238ef419c29a64ac4a7c889d588ea77690ac4d0d9b20"}, + {file = "scipy-1.14.0.tar.gz", hash = "sha256:b5923f48cb840380f9854339176ef21763118a7300a88203ccd0bdd26e58527b"}, +] + +[[package]] +name = "tifffile" +version = "2024.8.10" +requires_python = ">=3.9" +summary = "Read and write TIFF files" +groups = ["default"] +dependencies = [ + "numpy", +] files = [ - {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, - {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, + {file = "tifffile-2024.8.10-py3-none-any.whl", hash = "sha256:1c224564fa92e7e9f9a0ed65880b2ece97c3f0d10029ffbebfa5e62b3f6b343d"}, + {file = "tifffile-2024.8.10.tar.gz", hash = "sha256:fdc12124f1478a07b1524641dc6b50cf6bde0483011a63fd2a773094090c3dcf"}, ] [[package]] name = "typing-extensions" -version = "4.12.0" +version = "4.12.2" requires_python = ">=3.8" summary = "Backported and Experimental Type Hints for Python 3.8+" groups = ["dev"] files = [ - {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"}, - {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] name = "virtualenv" -version = "20.26.2" +version = "20.26.3" requires_python = ">=3.7" summary = "Virtual Python Environment builder" groups = ["dev"] dependencies = [ "distlib<1,>=0.3.7", "filelock<4,>=3.12.2", + "importlib-metadata>=6.6; python_version < \"3.8\"", "platformdirs<5,>=3.9.1", ] files = [ - {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"}, - {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"}, + {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, + {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, ] diff --git a/pyproject.toml b/pyproject.toml index 309b89a..2291c91 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,23 +2,28 @@ name = "rationai-staining" version = "0.1.0" description = "" -authors = [] +authors = [{ name = "Tomáš Balák" }] requires-python = ">=3.11" readme = "README.md" license = { file = "LICENSE" } -dependencies = [] +dependencies = [ + "numpy>=1.26.4", + "pillow>=10.3.0", + "scikit-image>=0.23.2", +] [tool.pdm.dev-dependencies] dev = ["mypy", "pre-commit", "ruff"] -test = ["pytest", "pytest-cov"] +test = ["pytest", "pytest-cov", "perlin-noise>=1.12"] [tool.pdm.scripts] l = { composite = ["lint", "format", "mypy"] } test = "pytest tests" -lint = "ruff check" +lint = "ruff check --fix" format = "ruff format" mypy = "mypy ." post_install = { composite = [ + "pre-commit autoupdate", "pre-commit install", "pre-commit install --hook-type commit-msg", ] } diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..276b351 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,6 @@ +[pytest] +markers = + color_conversion: tests expected values and method behavior of the ColorConversion Enum + stain_separation: tests expected behavior of the stain separation functionality + stain_reconstruction: tests that applying color combination after color separation return yields roughly the same image + stain_estimator: tests stain estimation functionality \ No newline at end of file diff --git a/rationai/staining/__init__.py b/rationai/staining/__init__.py index e69de29..e71fe94 100644 --- a/rationai/staining/__init__.py +++ b/rationai/staining/__init__.py @@ -0,0 +1,14 @@ +from rationai.staining.convert_color import ( + ColorConversion, + ConversionType, + convert_color, +) +from rationai.staining.estimate_stain_vectors import estimate_stain_vectors + + +__all__ = [ + "ColorConversion", + "ConversionType", + "convert_color", + "estimate_stain_vectors", +] diff --git a/rationai/staining/constants.py b/rationai/staining/constants.py new file mode 100644 index 0000000..6e26e51 --- /dev/null +++ b/rationai/staining/constants.py @@ -0,0 +1,14 @@ +QUPATH_H = (0.651, 0.701, 0.290) +""" +QuPath value for default Hematoxylin staining vector. +""" + +QUPATH_E = (0.216, 0.801, 0.558) +""" +QuPath value for default Eosin staining vector. +""" + +QUPATH_DAB = (0.269, 0.568, 0.778) +""" +QuPath value for default DAB staining vector. +""" diff --git a/rationai/staining/convert_color.py b/rationai/staining/convert_color.py new file mode 100644 index 0000000..5ae45da --- /dev/null +++ b/rationai/staining/convert_color.py @@ -0,0 +1,135 @@ +from enum import Enum, auto, unique +from typing import overload + +import numpy as np +from PIL.Image import Image +from skimage.color import combine_stains, separate_stains + +from rationai.staining.constants import QUPATH_DAB, QUPATH_E, QUPATH_H +from rationai.staining.typing import ( + RGBArray, + StainArray, +) +from rationai.staining.utils import inv_mat, residual + + +@unique +class ConversionType(Enum): + """Enum representing different types of conversions.""" + + STAIN2RGB = auto() + RGB2STAIN = auto() + + +@unique +class ColorConversion(Enum): + """Enum representing different color conversions. + + Value of the enum is a tuple with deconvolution matrix and + the type of the conversion. + + Note: + Stain channels are denoted by their first letters + (H for hematoxylin, D for DAB, ...) and residual channel + is denoted by the letter R. + """ + + HER2RGB = ( + (QUPATH_H, QUPATH_E, residual(QUPATH_H, QUPATH_E)), + ConversionType.STAIN2RGB, + ) + """**Combines** `Hematoxylin`, `Eosin` and `Residual` channels **to RGB**.""" + + HDR2RGB = ( + (QUPATH_H, QUPATH_DAB, residual(QUPATH_H, QUPATH_DAB)), + ConversionType.STAIN2RGB, + ) + """**Combines** `Hematoxylin`, `DAB` and `Residual` channels **to RGB**.""" + + RGB2HER = (inv_mat(HER2RGB[0]), ConversionType.RGB2STAIN) + """**Separates RGB image** into `Hematoxylin`, `Eosin` and `Residual` channels.""" + + RGB2HDR = (inv_mat(HDR2RGB[0]), ConversionType.RGB2STAIN) + """**Separates RGB image** into `Hematoxylin`, `DAB` and `Residual` channels.""" + + @property + def conv_type(self) -> ConversionType: + """Returns conversion type associated with the color conversion. + + Returns: + Conversion type associated with the color conversion. + """ + return self.value[1] + + @property + def matrix(self) -> StainArray: + """Returns the real form of the color deconvolution matrix. + + Returns: + Deconvolution matrix (3x3) represented by the color conversion + as a numpy array. + """ + return np.stack(self.value[0]) + + @property + def inverse(self) -> "ColorConversion": + """Returns the inverse color conversion. + + Returns: + Inverse color conversion (i. e., RGB2HER is inverse to HER2RGB). + """ + match self: + case ColorConversion.RGB2HER: + return ColorConversion.HER2RGB + case ColorConversion.RGB2HDR: + return ColorConversion.HDR2RGB + case ColorConversion.HER2RGB: + return ColorConversion.RGB2HER + case ColorConversion.HDR2RGB: + return ColorConversion.RGB2HDR + + +@overload +def convert_color( + tile: RGBArray | Image, conversion: ColorConversion +) -> tuple[StainArray, ...]: ... + + +@overload +def convert_color(tile: StainArray, conversion: ColorConversion) -> RGBArray: ... + + +def convert_color( + tile: RGBArray | StainArray | Image, conversion: ColorConversion +) -> RGBArray | tuple[StainArray, ...]: + """Converts a tile into the specified color space. + + Based on the provided conversion enum, either color deconvolution + or stain channel combination is performed. + + Note: + Due to implementation details, it is **NOT GUARANTEED** that + `convert_tile(convert_tile(I, conv), conv.inverse) == I`. Difference between the + images can also vary depending on the used conversion. + + Args: + tile: Tile that should be converted. Can either be a RGB image or three stain + channels stacked along the last axis. Stain channels can be stacked using + the following code: + >>> stacked_stains = np.stack([c0, c1, c2], axis=-1) + + Here, `c[0-2]` are the individual stain channels. + + conversion: Desired color conversion. + + Returns: + Depending on the conversion, the result can either be a RGB image, + or a tuple of the separated stain channels. + """ + match conversion.conv_type: + case ConversionType.RGB2STAIN: + return tuple(np.moveaxis(separate_stains(tile, conversion.matrix), -1, 0)) + + case ConversionType.STAIN2RGB: + tile = np.asarray(tile, dtype=np.float64) + return (255 * combine_stains(tile, conversion.matrix)).astype(np.uint8) diff --git a/rationai/staining/estimate_stain_vectors.py b/rationai/staining/estimate_stain_vectors.py new file mode 100644 index 0000000..83f0540 --- /dev/null +++ b/rationai/staining/estimate_stain_vectors.py @@ -0,0 +1,63 @@ +import numpy as np +from PIL.Image import Image + +from rationai.staining.typing import StainArray, Tile +from rationai.staining.utils import normalize + + +def estimate_stain_vectors( + img: Tile, i0: int = 240, alpha: int = 1, beta: float = 0.15 +) -> StainArray: + """Detects two most dominant staining vectors in the image. + + Args: + img: Stained image with three RGB channels. + i0: Transmitted light intensity. Defaults to 240. + alpha: Percentile offset for robust estimation. Defaults to 1. + beta: Threshold for transparent pixels in the OD-space. Defaults to 0.15. + + Returns: + Array with two most dominant color vectors. + + References: + Adapted from: `https://github.com/schaugf/HEnorm_python` + + Paper: A method for normalizing histology slides for quantitative analysis, + M Macenko, M Niethammer, JS Marron, D Borland, JT Woosley, G Xiaojun, + C Schmitt, NE Thomas, IEEE ISBI, 2009. dx.doi.org/10.1109/ISBI.2009.5193250 + """ + if isinstance(img, Image): + img = np.asarray(img) + + img = img.reshape((-1, 3)) + + # Calculate the optical density + od = -np.log((img.astype(np.float64) + 1) / i0) # (H*W, 3) + + # remove transparent pixels + od_hat = od[~np.any(od < beta, axis=-1)] + + if od_hat.size <= 6: + # Not enough stained pixels remained, return empty vectors + return np.full(shape=(2, 3), fill_value=np.nan) + + # Compute eigenvectors + _, eigvecs = np.linalg.eigh(np.cov(od_hat.T)) + + # Project on the plane spanned by the eigenvectors + # corresponding to the two largest eigenvalues. + t_hat = od_hat.dot(eigvecs[:, 1:3]) + + phi = np.arctan2(t_hat[:, 1], t_hat[:, 0]) + + min_phi = np.percentile(phi, alpha) + max_phi = np.percentile(phi, 100 - alpha) + + v_min = eigvecs[:, 1:3].dot(np.array([(np.cos(min_phi), np.sin(min_phi))]).T) + v_max = eigvecs[:, 1:3].dot(np.array([(np.cos(max_phi), np.sin(max_phi))]).T) + + # Make hematoxylin first and eosin second (heuristic) + if v_min[0] > v_max[0]: + return np.array((normalize(v_min[:, 0]), normalize(v_max[:, 0]))) + + return np.array((normalize(v_max[:, 0]), normalize(v_min[:, 0]))) diff --git a/rationai/staining/typing.py b/rationai/staining/typing.py new file mode 100644 index 0000000..e57a567 --- /dev/null +++ b/rationai/staining/typing.py @@ -0,0 +1,36 @@ +from typing import TypeAlias + +from numpy import float64, uint8 +from numpy.typing import NDArray +from PIL.Image import Image + + +Tile: TypeAlias = NDArray[float64 | uint8] | Image +""" +Type definition for a single tile. +""" + +StainArray: TypeAlias = NDArray[float64] +""" +Type definition for an array of stain values. +""" + +StainChannels: TypeAlias = tuple[StainArray, StainArray, StainArray] +""" +Type definition for all three deconvoluted stain channels. +""" + +StainTuple: TypeAlias = tuple[float, float, float] +""" +Type definition for a tuple of stain values. +""" + +StainTupleMatrix: TypeAlias = tuple[StainTuple, StainTuple, StainTuple] +""" +Type definition for a stain matrix represented by a tuple of tuples. +""" + +RGBArray: TypeAlias = NDArray[uint8] +""" +Type definition for an array of RGB values. +""" diff --git a/rationai/staining/utils/__init__.py b/rationai/staining/utils/__init__.py new file mode 100644 index 0000000..ca302ed --- /dev/null +++ b/rationai/staining/utils/__init__.py @@ -0,0 +1,6 @@ +from rationai.staining.utils.inv_mat import inv_mat +from rationai.staining.utils.normalize import normalize +from rationai.staining.utils.residual import residual + + +__all__ = ["inv_mat", "normalize", "residual"] diff --git a/rationai/staining/utils/inv_mat.py b/rationai/staining/utils/inv_mat.py new file mode 100644 index 0000000..4ec3419 --- /dev/null +++ b/rationai/staining/utils/inv_mat.py @@ -0,0 +1,8 @@ +import numpy as np + +from rationai.staining.typing import StainTupleMatrix + + +def inv_mat(matrix: StainTupleMatrix) -> StainTupleMatrix: + mat = np.linalg.inv(np.stack(matrix)) + return tuple(tuple(row) for row in mat) # type: ignore [return-value] diff --git a/rationai/staining/utils/normalize.py b/rationai/staining/utils/normalize.py new file mode 100644 index 0000000..d0acb37 --- /dev/null +++ b/rationai/staining/utils/normalize.py @@ -0,0 +1,9 @@ +from numpy import float64 +from numpy.linalg import norm +from numpy.typing import NDArray + + +def normalize(x: NDArray[float64]) -> NDArray[float64]: + """Normalizes a given vector to unit length.""" + n = norm(x) + return x if n == 0 else x / n diff --git a/rationai/staining/utils/residual.py b/rationai/staining/utils/residual.py new file mode 100644 index 0000000..a031289 --- /dev/null +++ b/rationai/staining/utils/residual.py @@ -0,0 +1,12 @@ +import numpy as np +from numpy import float64 + +from rationai.staining.typing import StainTuple +from rationai.staining.utils.normalize import normalize + + +def residual(c0: StainTuple, c1: StainTuple) -> StainTuple: + c0_arr = np.array(c0, dtype=float64) + c1_arr = np.array(c1, dtype=float64) + + return tuple(np.round(normalize(np.cross(c0_arr, c1_arr)), decimals=3)) diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..d560f2e --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,42 @@ +import numpy as np +import pytest +from numpy.typing import NDArray +from PIL import Image + +from rationai.staining.typing import RGBArray + + +@pytest.fixture +def white() -> RGBArray: + """Returns an array with all-white RBG image of size (512, 512).""" + return np.full(shape=(512, 512, 3), dtype=np.uint8, fill_value=255) + + +@pytest.fixture +def black() -> RGBArray: + """Returns an array with all-black RBG image of size (512, 512).""" + return np.full(shape=(512, 512, 3), dtype=np.uint8, fill_value=0) + + +@pytest.fixture +def sample1_original() -> RGBArray: + """Returns original of the sample1 image.""" + return np.asarray(Image.open("tests/data/sample1/original.jpg")) + + +@pytest.fixture +def sample1_hematoxylin() -> NDArray[np.uint8]: + """Returns hematoxylin channel of the sample1 image (created by QuPath).""" + return np.asarray(Image.open("tests/data/sample1/hematoxylin.png")) + + +@pytest.fixture +def sample1_eosin() -> NDArray[np.uint8]: + """Returns eosin channel of the sample1 image (created by QuPath).""" + return np.asarray(Image.open("tests/data/sample1/eosin.png")) + + +@pytest.fixture +def sample1_residual() -> NDArray[np.uint8]: + """Returns residual channel of the sample1 image (created by QuPath).""" + return np.asarray(Image.open("tests/data/sample1/residual.png")) diff --git a/tests/data/generated/h_dab.jpg b/tests/data/generated/h_dab.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4ceaaf9daaff92df778098148eebda50edcd1dc2 GIT binary patch literal 43338 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 ZMrI7Ph{oem}pYi;f_&@KKF>C+; literal 0 HcmV?d00001 diff --git a/tests/data/generated/h_e.jpg b/tests/data/generated/h_e.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d3552c2f1dc4367053bb581629f992bc43dd00e0 GIT binary patch literal 42565 zcmbTd1yEdVvo1Pha1An8a0xO%aMvVwfZ#5{b+F(N0t5)d-~j@GAy{x3+&yT5ySuwP zcfS9e{hxi$-lyu^yQaEore@V!tJhj@_tQ^5{WSfw40x_2rzi(NK>+|zkRQO)9N-NA z6CE7`9Ssu$0|N^S6C0NV4;Kdqmx73xfP|WomWG;=ii(bji-nGXgOQ4gRfvs)n}?sD zpO!^LLYP;ai;ti8pG8oxu&{7(aLMuT$a(3h=z0G?A5U!nLd<7?X8<4y1K=4U3Xl-x zsS7}hyiPQfe;t7Td7wN4qN1T=U}9n8AWx`%4tRzF1U^FrqM@OpBF_#${trMUL?fc- zkwGU`|A4{ZO2Qiymx;;v8qp5Y7&&I*Gj|Kd!X_mnr=Vnh$->IU&MzP+BrGEO=B=!p zyn>>Vrk1vjuAaVug{76Xjjf%%`$rE?FK-{;kT0QO;a?-5@d@7&lajxuq-JI3-XiV8%<_{T4lXI@AH5~8Bf^Pm&SsAGI^C1&6a!X$Yemx*Y{ zV&u~}2AR8!V3RWOuP~qdz7s3c_Ls>wB@ti$PP_8{uM%rk%0?)Ox-^=syOXmI zWY}1SHd<(82ff2{25o@&ss8K>jK;@n-4NofAKxnenut zk>kgdo>epQrhvJLa)LOYW3q*!*()<;ytcs2?ne8o6?!9-j9*{J!2) zR*EW`@LKUfC~JX4Y!;LiP?pQDqQmJdTQ#2fLgVASZbEef?|M+$b2-;R@Mgo(@l<5> zE8Ka4O2thBzVOtE$Gwwi0&U?#33NAfwqLFmE`v>ET-&!q(6hT&kp|)eM2q?ha|(aM z*-78Z!0UUNED}z)_6(i?$A~7jCxDFUeRA>>Afm8-RT;;GYmj5tE`ETDP`sj{hmy_4 zOBfr2DPdYSeRqj;KXZvD(Soxxs-HP+|J@S+2f(XGQzxc&epce#HCy}DiZkE1QtpMD zbOwjr6uGH-Jg=MUV&WiF#6y3NlkkA{IK$iYAkg}8D)P_L#+JqYpzGC1EzM1j|CFLB zmu~sJz=_voXx4V}=-XPsUef7v-foPAo_G1c`RIA#*Vf;gnl-{Dg!~}BAQJU=ryWq% zpU>)@DX$xrIt?6tQ~juB8~(~@M3mmtJt&8=1JAJ-j}E)MoKvYtIz6=tTV$R;TmU@g zl~XI0rB@%=Z<$WAjC1Kncrx&F#u3?U$h0j7h=@q2yd2!ee=()L5s!NSCO5Ml&kn2&h$OUw#ygE&|0@uo1XM z6PI5}tr+D*kM@p=4DfKM6$n7)lo}e0`-6xnaPMt)OoD#;Gf%zJ*FV3E@B;BJ5wK|m zWz>B#$Cn<&9Lf{k38m5Y$}8Ah<@q$ScSYkw-85Rdy%N{$ny6t1%hfA-hAB*US?2Ks z2z|3kaC1K5O`vI_p>iPtjx_3x!u88k}_6#v7{z3noC-n4&*G`)6yCXj7YP&)OaK#U6UHQAZrzSf}cG zzI0QMOxU)zZ|L*nHW0xk_{#uSJjWPYx=#~jEv@@4t4~9%R8hcXN49_ASM-kSh?oAJ zVA_vnslP#4LT>SzmhGzxuX-3J=uc^ae?Pgji=2;{REbW!NQpJdM0T92yu}6W+S~2s zSwt?CWoGVSjLPu+n*wjo{h>Uy%1@|q<@50X_h2bWIn2IxiZaXNrI_>d01Y;3$Fh`~ zNFUR%b(HFIAaPue(H6|zlwZ%`fwB4OVVm|3!H!}7zdh9DQx!}1U>*5gYL$Ke$!7eU zZsfJ|DkM9X-O%2CHnisG0=6~yUSa6Zr(>#R8lPQ2nf_>XvyfpepIvR75#3$8DuoXn zM6-7gpI0PnE5;N@Bl0RKec)VO7K2-pIZ2qo$I2s`Ut{plg#=%brM!?b?<;>gNO9Z) zlg=!&sbaSjvhT$A^4D@ar!g8>bffhX z;T>AV=LTN9{#Ipy9dA~ZBdZ+eSPq?#FLaW}Q^^ntNZhlHH4K;(hMWmrRm zrQEAvCF0Yd`Pi-pl1Ca39gZ@{$GPb;j!jz#gH!L`IPI<&;V@LbSrb23cGNiL*LLv& zjzP`Txwin0wFb3x9MH1s`X=^RgjQ9K;EjQ{{2wJQt|6CV0t#EHIj(-+uh!$ zz>TaeU_Mo_E*yRT5*dc6Ual~+9>ye=KuC8dMW#Nl37<>!Y#GOV7FU=>Z;9*Qtr3DD z=1GAY;s=iL+$iFg?cz3|tw-9Whg2^}&v(zQF<{O?NHjm6HWcK&RD`UN$p=U#B~ zhxT!?i&DQ0!nrb*-0~;z106oPq-nsHtrAa?q0}Oku^%DZBahyaYhdK?5 z24tO@Sk&mwdTu3Igal1dxdjr52b!EDRj*RzE({J>JOQ|dtr!o+Js{FjJi-z@N?X>n zK#hvc883;+Eu&G6LrR$%RJ-}gL zd_cIsSvrZ)k-nB_AABMIUj6tYN59JFD2IJoZ;cg&8c5cSasfCO_r2k2EnB=!(75xs z`o@IGs1@N-n9Twdkx&#*n5@{&buSGgN+k0>M263Jy%pop-mf`p9Mcpz9zq|^H5rpc zl0RxN<_q`N$l0Ag^TKA$`us0rs}TMDLSYO0CqnYGu9VQ%O?fYsfro8$l^En#*kCLR6vU8YFa(HW_%2v8HIQ1QK~++O;Sr!^=UjaM7T=w{XRO0s&} z8!Eo^fqaPLtHGjYUIL1P`p?GVrSPl&Tqmq%o9IjYia@>WmhCj^m-e7qXGL8;zo~>b z0YhX570inIbv6)WT}L2+jh*4*^$*UFr5$D?P~tVc_kQ!u-9pQIhyDw{p$fg4$*v0j zf{@+|sjU|F2lr2moHI$OKD!et&Ptn;+(Z&kJ5Kw>C9U!S85aI$uF zgkzMONhhumVJ?-ZaeSjD=plTD9V~pXbgSF!PXle+nS~WmrpsDkAIkK8{j3EXLkl@S zr`QIM#c+z6j1f@zW{w-!&dml#N^~$d@~Hrr3miH8qOG! zHEcP|2R9Xleh3*^NPV=liB%O|EAg`e{pHZSBUJcXb7f5$v3!6dZI#sh`_nK9L6wqn zUo0jQ@s(oV;5;yOAq%?sEiHZSxh6AIWW_r-$}(cSC>s`r5sZ1rcCG;&@V7fI1ov(< z44b1X((>w6vS(t@?)G)h)3~ULtdRq*NlS7Da#by2g*!>CJlK2NPKzZ1+oonN)}H|6 z{@e+jG}R>fI?r&Li=F`fB5RMp_`o_ePmO{DiN5=k_dg=V#bn1H|FX<%`AaW-%vg~c z4$LsvDGE*EBPn}f_RTzxW@6m7_SfN`VwizmG&wxSQ)=wE)VI7QuQ|9ZaG^CWR~Ce} zFseT%J}3|TthOi-YMwZFIjU_*!9xW^c_y(nD5@m-SflKxFsCV&Va>hB_m9?;qv>d!_voglJDH$H_bVwM;460UlP9O z?IMfZ*B*W=cNVT}(RZpGB!BW{V6h@rTM$LM=W5Fepd*AV)$<&6IYbu}M2P+@s14u= zN?fhC2=5T>RA$KfA)-YpOu;TqtDyj#pLG2_{_SGsT*T2oBH!-F;-#DcsT4Ty&5;UX z+y)UCyUFV|0uR3jyBo0bPvOCmP=t>%j{mxYw0F9BE5vNI66=$;oSoc3^EezTi4yWI z0eJdQeY!;Eh*=TB5XQ`Q4G~nD?z$4un_@`FeK%ic{-MQ{uHaesB6G{kmIE2MQuq`zysWg1 zZ>-rZ-hPrtaI*fjYeq7Ml+v$7hU!_*-#m^km;jrzoNSiiMWL;~EaaN_?Mb|A+!;W^FOXd#t&s$wWjZTp`>T78!-0XS2+(#LqM9#?mx1S< zbk#{2+fXgG>W{T-5t}1GB{V)48rA!-u{e&5$v1Vi;_- z=^4~Zgoj+zV_^|?s}bO?P;+|!pws?O!7L4_4A#2~%-LZYFgd6+jE!CL_Ib8F#)IXz1LR+(Kry4GEsYAaQ zZhTS{XMg2sF-`DT-beS|7cal4nMi_Q`RKm8^&tFlyiC2gmB7~Xk$U9A*iWZB-ccnj zwzwf3;rnnS0J?>-+4PD9l$T` z6a_Rf72|JQljtvF@lo@6pTkCxrCa~Lo<*6w>0kmFO;rHQI$-fRNuMQgBXL%dI#R5@ z&)kOzzL_EmVj24%$W2|Eq?%La<26e6jwwA^?f4W6|LMGfWvZN~L|Z zFIDygC~Zg`EYcO_=CQ{s&w}orw}wkHj*p8}8w&TwQy$b1$#e^T)}ze_0(bP&H4Cuq z&pbtxz{+~V0xIZwYvTu$3%MG-%ILTlMj$3jUypTP=-(VI%bYT@kLr_JH3`D+72Cw| z+o0>tQkz z3ZgmO;{FMswD?PzNR3#YAxw^(ZbHtebkn2YPC+z|6J@eO-0> zEB?89PRKX^c3KQTNU&6d8wN(mV!g4liXWC6?=a%V^p@85SM*8UM{6Bw>@>=AHk}RQ zk+X|@u~`GYohJaMzjC-yql%bT7+ASa82Z*?qkl$6JRdYd-RAY@dB(jKtY=!D!wcjASW%de(dJv3flhNGpjjQD8fue zX6>b=F6;XBUrxqfCv&)7<3E_l>cnMf;kxMEKwTtMQKY0D3X}AfLpPFSqnZAVQ_E1U z)7Q=?trQ$jIZCE)=p)d*#L2+ZC}ZNw@E_$uo$d7n9}PmwvcxO3JpxqS@)+qp(E;qQ z!YLjncwskIyz{PUCl3y^TS;oGUKSTjse2!bh^?$aZAA6+504@DcVi`D3s(~UAi>Sl zZIe<%!)76}Fp$N@Ti58{jTy+>vR4{?Fbfu0e-oxR6Le0eOQ^3AT`P*1pj5)iSC<71 z`+S*eApL0pr_`StXJ3(a+!CchBT?3|CS4PEg|DT@yaS1_j!Lr^x zQTNV55nRfA0xiiXf4CE-YA$YX0? zB1)C2JsRgPT&8U_`xr&XI!As{?%a1ibP3aDXwHphPF7U~GuPEh=l=x59sy5)n141N zrQ_F%?vc9wa(4NAfx$wt1vsp{>V`@~Sc!cy)BIA1nVgm*UcFiVdct5w^%nj%YjwW^ z3U-$pjjS*p#C-oouDNett@h@&KvC8>Q7NrZIq79hGEV!icB&9u6eeHGt=owP)>65{ zL%+Bf4lP~jEGQcZ5?KShe91Z<4By+bj6-J(_?~U69YNQ`xiC@BZ=&TzsAGz38}QSA z>T}j)KoC(s#lPU4#=-R);cDeWG>By>Vk6k7dH9o*hSy#4{kqV9b5H#In^Nkq)gO z`-GXSVgi>9CB-I@AufV!s@k7}V1}Q!y+W+N{$U<|{zKE`n~cutzG7}t?J44YrlKOt z!w+9&4}D(LQ$4=z6Pt+PKbZVGce}bR!+8RqpvA2!=wwwG9uhbMH69_?K>AM z3x}geW0qVTsOTuBzpaVAAPnhs-(6@sm?~R1FJvv0G^n=I$>4`x)#%J7eyj0{EuK6r zos!#@KbL6V#q-91HVL_}l$a~q5w`r<|LepiLiEym3%E&T>3jEFZTMoqUwR~KqKU?PSl1b1uI>c>P@a@;{Fg?VQOfw{c}|7>3+8|*xJ{e9g+!Rb zj4&@$Xux5FvrF-CewdMK?P0>9ROdyh+vcpH;w+mkt|(2HFLXCo+hqIHm0VxOz|c#w z?RY4nxJmsNPLXDXRvUaF+Ti8m&$E!F>0tB;|L~vJ4OepY(RvW;V5_#XRfs~b@Q3?1 z(K3Us!&3!zrY|<5=W*`*#44*_Hz=?3Fb3;Amtl$iOt7s+5Mb9Dw2)qps)Gv>(Aq2{tk+%z4=$Fy*jE-jYEL zx>vM_x&;9!eZr+L8)sMYBKzv%IS!{c++j8tplY%PVV;v~eQL<|t^F6n`Y^pS$~YuR z1jcR@^I=R{6pDPtn{7+oT-1sOVlzG$hmP9m6Cq>#q8rGA;&JE$I3$eG{wyS62bq|$ z*Nc>OTgWtT)ML$I$1-<4lL?ArEej%cKp(8}m{rM)t=p*|5&3CNaZBO3EVd;xC7F6Z z{4B(RHkDAQkDFVij!W!W%S2TVw}!`pzi7QzH^?}K?Jb?7j<+5YsT~*p*EiB$E|;v5 zeZfH1hai!g**z{Kn4oM=PiPa@_^Qvaf^Yq^QDIlMDY%HV2AEjk2GA~rdZVy_5>$f2C@2LHzoJdd+R2|6~k zvLqT3#Ji%bS;g1KS#^OV_X-umhcC+!;Rneh_r+f3taBK_@*FbV(mfiyn7@4>m8;DT z#y*mB0k7ik1K_+GbbPUb(f9=J32j?YU9o(t3;UvG!UNH%s48kaJvHCT!D6ksIYMN_ zN2PJevAO=b?eN!M^Mv8bs5Z5un-($McZyg@u{45XQL!>o03|T{FhHFnN?Qf;C`t*9 z6Ue@T!r{O9k$-rSZGRju>g1@ZdP`jGTpqmaw1#iAVR|;R^}DPuUAH15cLU^QT^>YiEFUz7?o+$h<=$r-+TjN#PM?vHG_6 zB24g(3=^9Z+HM4A(=B@1-9}*iu9f*S+%xhX4PPNMUJ<5c_D~9)RG1nPlCcvMV$TeV za~595uUWy!zuI_o{rvjvvoHtBR$N=()>!KNu~1OX32IT`rxKc^sJ3kg=Dq~k_#-ea z?m%#I^3rn;t(Z^-+s4tAPMbnR7#RI4RX#nkr%)}PvbA^pBW+j%S3(42>P~4rkYpFt zqU`PZN5&(YxH#zkc2@ogyLP&~Ph%*^XN2UyxJ098?(?u?2{1!$M;OB}7Rby!6Q`5S&7 zd|c;*^c3?wX#t{}wH)V>hVKNIc-8Nnr|mT-1QYTC*F4!T`^2})b+7}0~)#jny#qMCE+7zh|-wLul2-z zPhX*qW}3-Gp$SUEx%lsKsG$saY4>%Vu44&9M)>nFG52K7D?&07UJUNF#10! zefEu&^WA#k!y<6t5xxdiIP#}1lBG8*f{aGxrR5Du;pPuji_zfWI+;4ArH@H^m%4Y6 zhH{baNKY?=sm>ao0OL|h+q)tw%3o^y;>WOieS7?KL+hQ60jbOiz)wK#1gTJXP z&-CH6)r7M@5`s_D2+RnO9JJCxO-<72;R`)t|1r@X%5_|XFpwqsSJQ1pBjR9%jrC3X z;S+$-z2lK!++Q91!>xSwZJVMioiGM0l(uVniwZx5k)b(8<`@#WvM-Taa~Jmnc*VD} zel&b;flxj`DDMZaNOf za1*E5+%z1hFU<=$dw0MT^_jP8Jq=$p_1%TrJbzkq<&2}OEQuC;=7QR%C#EUjl2%B5 zWE1&JHy~W^pO70vTWawF37_to9X-`KgLx!UPoI@B8v8kca z0jATYibh37gpHR7hZw4PK#iZwc@6wcYoemVTgfyuB8-B~Lv$aBy z8zQ6N32?Mhc&;-kPaxj-!`Hf6DucSY=LppMeyNK<=Hk&~ME6B*$C>?mR9cVDgl&|+ zp4vm&Oy?5fR2!_4W?4`$WBEUo@qd`VMjvVchw4Z#jDi{;JFb`RQefKd2|#==Yhhb1 zW@vn{Vp;#%lpKg!K(RBvel}Ux&z3Tti=9DPKfWjR+~dK1QQ3|Kx;U#f!=$rQ#XTg# z!a4LBrJcHAD=7>5CRA8cC%9Z)mV`8bVnr`Iyu;viHx%l)YEEKina}i~$duu3a1p*d zSBDG?IRoU#z<{9cOv~#H#4YHlhG=K7=_z`#uZ}WKBMAl8in(&*lXZ9JtPHb5+>bn^_n%1TS5Xst)sG&XvT&XMlwUADYG=>G!;f z;wVNBM_;K^;)l`R2TpSLNy*HeJg_1=3lcl3>6p0{`ycsrL6>{2H@6v&kCc<69?5n( zi#w4Jdi9fD3GRST)sO_GO|6sIZ^aW)x2#zeV$c5R3`3JG%&LR+H~kNynK*iY zECBamt=d$p$MnjBNg=@&YfqmOUPzCTrTh4&k2onNzYSQkf{?jgn))dGk#*M~Fd4lt z!?}i>#@dxP8p9;&Gl7Ysc(sS~J zRJQpwI65pR*TENAjVkO@a43qaP(dlk%|eXexxbpf)Kl2%-IqNOG<#ftem58!Hu2h6 z;Jf$~eYw#eG6^oCkjEcm4qB zxP1SYy1C1(PA2|!ISb9bj&XWhLnRc#J1~eradbO=(RC#mWS~A~9Uw!37$Q<8E{|#A4n=nnY_db!l(IJqvhD{5og+opj8o{V5ejn42wpxHg>KblMSukiUyYil zEcf07mak%{V%Dx|Rpl7$-ZtG66nz)Nm?!RIOj7C_-!>dy%XVb?{}wxxn(ANIlsPkr z3cv~&f03>(=kwI~bl`^cyn{E(^hg@YlJSKZzUDeS8b|o~xfhrW`1tUtxPl>GkQ}69 zVjBOmwl8ar7gKx%5(Wmr6Holq#QY>Mtx_9r+p!61O8G2xKEuTf6CL55? z5vtnlDF?V@a-yJ_p*1+iW}7}PvOw|2LgCpyk ze9yLY8xy#m@dU7u|4-kQdTgeUHO1d+sZ;S9=*6pR&DIN=CqSRn<6z8x`W}h#pU%3Z zz1A|2pAmcl*qR39S&I)PSl@fh!PX>dKS}H{94$Z0zQs!_Lmh=|SbymTT=a4dsq$Qp ze30kvY_0Ms0R?K02yhZDzbVikfNUKmQRplaSBGOzP_(b+YlxA2OB_Z9V%$rqFSpso zwq&C2Nt{+(n(P3~8#>^4di#)M_VGFP%~0njy87eE^5o0j3cMC0M6KfUScM^rHoDvB zHM(8zyq<=bh^)I;Ni|7gA>`_jWQ?={b-fr&xF};Hbd*JK3I7%ka4mI;SI?qAvSDMg zaz#L@BbPR`T*}t$(+Oi#Jr(|gnwhXKTYN8l=!Th6R+8{ukh1$&t}1`#0Cil%D$XjP z^0ml|oX3#cN9`aDHNJV(GX=LnH#=zVclKaKVSra@om_uUj6rsR5Iq}mVJjwXWY0$? z_oMfGb`cmqGYn5KzxT*C3%VltmEF+6&Y5eaI!Nd*U$JYc^Z@Z^UhiFd_Vc08Pn0%w zlJs@t(GnwuY`9pCA2K8`+rdlmzUC=WzWq@`f?kW1#}mkqG2&n^)PK!4*!i!JfxoyO z@4@9%a>O}{lg|Jr>)zgw?sswHMM}SvWz|nl({j12FT`!SP4P?lW2)>8-k*KkxS6F& zU}8*r0yJe$i2%-qt6j7&sJ(4s{ApS!o1-`XRN75(yncqoU2FX1iYEP-;McnK0UR#J%k!sJN2^0F(<8^cPcnOwDyVz4&2@-kkQdySZ!Jur zl?Wc6XU)p0kL`5eFGR7F`v6aQ7E;m(PIH6%szsTEr1dZvU8HXi$Pt`EUBmKxJqOxF_I%Pl<(d1YctVkFG--rg zfLC6>RYe1Q_I0CjC;e-ABTLWaPg=%H#=Arj&XBzNSgzqS1*4!T3W<8Dblt2Vw<6?q zLDBzVZ2!YR3)g%r2ZLoCLbt^#_}{e@psF;>p1_7O|6>J4YB!c^kLCPoM+ebV=c=gw zP9R&?A5p{*n#>xo0F?#>W7^3Jupme%l#%Tl+7$M;CF%V={TVS+j#D74ny$JQ?F(~L zC=(6ad=a{Uo4*D~&x^}2RYH(d$gnFDSGh5w%fAWEal@2|a^LqAu%@$`wHdX%ToZW3ni^f*ff$kYLOWtg!>=5_O|nQYws67Pw(`CSg{qli!z(o z=X<{Bl(K-V#OR>|rJ5NUg3^15Nq#)eix(-J%)x^1AK$^0f?e<_;E{?0&E!bE{0ZAX^BJ~mS*}FqBUx&FxBC9DOwfFY(tEt-o*x^BV|OBEAbj2 zCICd@>&~^C!*JGUs^iUVL|r5Cbuc$`u&P_X)+6m>LwAV;WP_zaHd^+h>?4hwnn@u0 zU=*c3DL;iZz4XOP=wFeV;CTN6>ID+*$QOzNc&}&s8H$*@iipUB0SJK2Td{7jBInHE{M7^CzmWabOEA*|*=btVB>QL>` zkBv@b%C%#~otSh+T$g=JY4K_WOc|U2igJOS~XUb`yM60FGPf(rZck&zutW1O zzsfzM`?DtDvOwau>@$3@WqRJc70blZwk^Jv{?)?v*`S2cffL5T30+_(#9~E=ZhA z<~vBDO`X4ux7)wpv%MXH%uSK^F$FB6<0}GO&wp7W_>Jnd5(gnr_Fn~j?Z1YYY;Xa9 zTg$cE;lza~W-h&Y$*_RS7KBu?Z4o^N?!i?cE{y|&vBs?tq~0~=(7(06P&?9r?Qo7z zPGJy-25|3C%0Jmh!};#rMv$@0a-c_TF}kv5SJesIjut?ro_* z7UH7OYfpd@V`QGg%>gmeg|ve@h21J=JEtxYQy&9FEbM49^5*p4>SxzRn7`g)3I*Wa zo#=oG+HZ?}dqN$#NOj_6Bh0@yLIQc@UU;!2Ou-fCu1fj|QVYl^1UUZ{&qDUz{;w`` zy|JkZDQ|epOEU4miU7X*qN03A$>i>&!i&#q6z38#Te?_omDuOndUFL`>Z);+Y?MOe zR?G_L3_px*zKSq;r-?&)JG0r^LA4fT$Go7rM>055Z*szmD9SC?M~-AzT8I*5{NGc#ZA@q(M=8b zqMhJ7E@ckTZ-vYU^4;7{xvw~VGR4~~Ut-~*U6_A4to&Ai14Gpt)lCFg}AK_I+5zNmD&P$dbHAG7bL`11&3h zP}Mj}5#3Pzg~4Y*eN2}mfLrh%am5pDb@*K8NpXnt{U@b7K&*Q;CRMZugqHx--6Syo zI>BI~X(XUvet@cOiS!haWsxn#wo?OZTv=~Vzh*E_i+>KEq|gg z-PbM*ED**B>MKjh&q}Z<`qIH6>btn25=W{xD@37#rf`9tF3VWFa`-{;iud(QKMw^X z1x+=`(HqK8i|8dm``9h-MsQ82cQHfe;oe?^uUHq#j?lr)3B#ZZ>E-{XO_6@!A%!2f z?mrfIQ1%G*U328Zn}G3AC0X4>koQs1j_cIK<@e<{kfwg|i!|)70O`)^0sFVg8R_mQ zS7+b{vIlGeG(~6fqvKjSQ#>XHZ#;lUVV3%gj)aMfbjxSMADec?QeuVxl7q&DVQRU}(oUISeuyyq$9bwO9$RRye{ zI>4`;NnEc<+v7k6a*1g{!99OIB<#8XZc&VlH6-1szosiizp_Lk0=};&Ltk5PV-&vG z&syTvXduqkWN+vo-11p9z7~KJxHOGZ;PW49>4HhjSO5po$g;R!O@w8zYd~~sbse6Mj>GssFtqd8Z+}($Djp!2=T%vdH9r- z6Q!&JiLG{`)`#TGE2JiQoHHu-9O2Zl#Wr;aPY+~Y9Oar^{i~VX^{l)Hvee&f#F6-* z#zIV56+rOQ|JCu9!dTUwezh2L!?@B>@}O887S*r$L^|KJv!P$qCH_3a9$+8I-(;5_ zV|}fIG48Beaph?i@DTE~S`6n7gVDW3ii+kl|Gp*!b-S1hQGTZ-KDrv6YdaP8Y$=;| z5k<{n9^0u`sZ_zN!0O?n@F-O9|PjFW8{(2q=eF@?%+`KCA4YE)af0h+7M zxHI0_?*wb#SQaFpb-=1r9%R-STM>Ym?@b?8z)t`I`^w+$Sh||ci2jO2(xGj_&e8;cM+{-m!NjRtVqxU( z=YOQ@fXA-85)ZAt7tUz@vX6bWDt!991GaT~Eb8@{6y1~5N2lYDxb=GgXP5ex2>RBs3b)se?~Bu}&1K-dnm)8}C=vNV5B!kH zbgC;MuUQ)eUrW2q&4_9e2x#q_M3<^lxPOmjEMXF+^hJ?Q0L$fJg4#E=kr!+ z!L(RK7J!X74Y(~oYekbQTOg}aGCQlqZ`9weYp(r(b>> z>}DECLl@5?8=@AKqJSSKsC5fNP|z$xy~KB{5)(##`7Ra|>DkuEovGLB(czN0qQ)qOEOL&VfLC@`4kxtWsd2?6P(k zCak>-td<7~Q?zYXa2=Udz{H1OTlUe)0jx`Jy;jQK|E%8?Yi%cGEj3v)ZA?7FpVsmO z@JMOcmG2>AB&D3bZbKsQ0`#e0uGKi#&)!2z+wjG9+0Qe^m2mvb3O4Z1 zEND@nca2UI+uf1}I$zSkYu#aHW{{_u!_+gUH|7X1(Si60{Q3c7MgLg(iZ6#}&7FqV z4)*(dC6OKyeKAd`t(0)t%`!nHu7un;T-oSsSOAvieUf!M#^-it0yN=6x<#H_^Y*q$ z4w!N}Hr+UfbX!x@VC>6&WwshA_D_5GF!1(**zzO1Ch{RKf4{+i{%Hvhu+JnSib}6O8dUc&8Q*m6adXHpQ!-C+w>6~+$KFP{B&10+5*>1<+Rx`5t z>V1?ix#u}?bjcrnwW+1CwEa#O$Q0kJR=~n2(T7y{PJlPAbK|iz^wN>i6kbs(5M!*& zzQcSI#_G|vBL8IAsX|z;a~HIUf*7V4ovw;6O8YKI|E24uu!-)$hg!{2Bqaxb|KH3B zZP=gD4+jzfi9~6Cu9|s11RCqty7dt0Lneu5o*)^5%Vg^EdWB zSFq9wI)Fn__;RyoL{{d)PGA2Y@Y?Y&iWNN9By@bFh!`2WQ~e8Z&Wj{YQGM(8xxW-2 zg<1&TJsuMSCG8saj+JR+{Dcx*dUEw;^>{EBXIAltSo2>JM@3uc8W_H|NiGrKYkA5~ zIaWX!s~PrQd2WuLJSAw@5ho`g@0rKW$Ay1$dO{4lKnewcFuj>zYTG?zT zB+pouL{t1{8QFDFZgpP7=))TyuaReZ<=y8f3c&mqo-}?5*M73@k`g0KQ~qG__vj9h zo@9g!XHEHZr2wY*+;R+m4I2~evWns#Gu!@CW%@UhSu12)k);^gNR3%wl>6irk-;D4 zi1N-;#^EVfBX~((K$y!&*ePwMR7Zf+(0NJz;4BSXF#NvBlyB@4rFWg3XqRk`YQyLRlG|Tb zNcXHR#48aH*2fgDRjPQDy7c_pY9Z&PO}OxBBem=Y@3Kyv7M79qnnV&~hDw7!s<_V4 zye^+KyX8dvKFb||`Y41Ov(%!iAD*3cBIAoPZfw)5us2uPkohZWOo{8umzV#0vbY7V z(gqR#_6)SI0p7hPzeKg}_>;ooe8<8R8*sBP|3~-IId1__Jg4?221#|?{`OGiDWb7` z0(1tpiIsmZ(6q5Ic%(gE%w9E|tSOi6;M!r1jg5<*p`%eV6S+rWbZ`FzOzui)YPNGa zABipfPQdr^#tpMgLRBK%-%mC%vF3*rC_N2~*Q8uc=!^28=H{DVq(~F}ik8x3T_Abg zrDh4UDzsMJBi~e+dvGh#oNWunhAU6AS+pyK=|st~lltwX7t~^yh);!7O7;g;`KHk{ z3vZ3)=UMdY&F(CHzZo(-9xPeeQQ9SiA(Cbj^8UWK=zcN2oJK|ESq#1P-JD%KUHI7$ z%(mhWiPpl+!(gO48P|>W-fB}`x&EO!wr+#)@vLH2B>{7|S^!n?)>WigwS-eni8yPon=Nn}s0epcU*!xz8J7kl*i zrU-&OR9Uu2R7Bv8$?;VRU5~bjl##a33F9{gF)N*Y{ZlCp>_63GrI4E_?u;b}lDING zE@5qwBBMc@CEZEHN<46$PvGg+I zNpeinm8A){j!HUXBH#Y|NM6LVGwI4TQ451@ZN6wtccx-WjvEj+OggAcP^4iYcu~Q< z-R~mnptm>L@!Hs0@8s(9P5AZr)U7xUbgWZ~hjHqcy}`(a*6s6Mxj}w%wOIBx$8iU$Cv2O|@nV z9F-AuPybnlyA+D0u0tSJjp@0G6+J)qF@EMPnyLh_`g~pfpzVuYV$eaAN5lAhw~;d# z%36YN?%tv8&R&Bk=5dhp4P97Y$KXb7tkg)Z-O7g#7W` zTI%sCZU#v|XTL13IV#3RF74VE$M^9;lH@&s6xMyedB3rp?i%}R7S`*0_2;=6>mhY* z4&EAj*<8EAnL~8loU%qyQ{@$j8dZ|0y0l+O(A}iEC$>kS+QhRe7K68Y zvMU(GtYaK6=c`CT7YAQJLG|^bz^M3URM02y{->-#GQQNS$H37yBSe68$=23f2y_2G za|(rV{>sNWvDq~_BYdpPG>h*F&V@C2B_cq-z3*1hN-HgICF3a$jN@|aKU67Th`o-B z<*V8XiIwB&+sJ;MCpy|qCT?N+g|kZoW;Gtnv$J&Zzz$FGx&*JsX2jl?ol9*=h3ZWn zqD}=1I2OO5Zn+BFxzVw5Fd1|_%w{3U&}=T@#4)HJ7Wj%B<}y?N90xR0N$z_(}Hzq1|ihJO*X6D*_RnaGUoAz(W5B! zf@_x1ZazIC#_**QKLP)%HHV>E?;-6U12Z-5_+kLOYB@spL_Fn?IZMND z1z8NCAPsGS<`7v!l{bImL)K7KrOz#6i?MoILFZ{2J>rTSsugm($X@LmU@zSj==8z3 zYEnQ#k`a`Y?d3$_^qW3CmUv7UqYQZZE2R_G{$T!BjNi+Y%mc^gf>W|Xa6zC#Y#K@Xxd_9AjQLsNR=%Jp>jk z=h|%_N`kGZsGp%`X!P614sV>Gg(A)}s$5PB77{Y_Mq^Uw%g^TOV|WVs&%jX)0Onj( zAyUp37ZglpE4h5JXDDFPr^4EogDlHX;W%qY1Wy37>v+?b5;b1>skmGqw@;Sj09=~R zpJ1aj1k(w>8>{7qU_8H`t^f)THLM}g-wm4sS0`~*&$EwU@HsaZ(L`FMq4(?8Y7FCsGp+ZnG#blT5?- z%Gs5~@F8kMNW+gt3eKJ!CO}bjU?4*0lz^_!=nt$OKE!~78@|*}f%h^u)@_(wL zF#DP-kQ!g>qK~;~es55cdr>ZxCf)LlPo+_cR!|#e4%OY`@u%t*P^)dSOLMgZ5o>lZ z6O>7BO%|z=Ly<Hqo~~0fL^(UtwmZ zlN+e!YLoalk?3&M7LaSr%0?Zwig~n2v{Yr2u~-H$D?!tIQZh$4tQwA;1k3)~XUI?27C_eQNUPMb8L3$7y!Z2xjF>wT`RlA*bCV zxW>5S>`v&foOOI}B1?T~6Q`u<;=o%h5i$DN^j(A5hOmb{7%N^dAKW1+ zZvgz4QIV0z88`Tf>P35vH+!|K~zel zyOEI2MJ^hoVbQ3xbayw>Al+RH7TulS<+H#0dG0;#z2EQs{$O0la4-a~InQ|>^EiIz z>z)S?NuC%t;|m9dn2|)O)fX7Za|Jy1B|caB6H;fPbz`Q+Va~^0k8jQW39Kt;D~$5( zG^d#*X-9M_3RH3q$_Qm#S7QLRzQuB79)C+BIrh{!GLN(;uIZ;^@q)?ur|3nsY8AP+w zeE=y(r2ICkRkh#jl4h~}aNM^;W}1~ezsy7yE%#ZEb4jN+*Zc)fxxWS*y_q&uD{3$! z#r#y!t^5(qw3(q;jnfO~5R!LDq@R0-_{hqLx&^I=6m zRF0oQ=t@^laO<|JqZ?QhvXvL)$LTnY^Lb|p1q$cnucDP@v!5ZJ#qo12dA730TCGKS<(m4E(lfBY z9^*>1Z6{>E^5E;6P(nwckFIpgEv)5VF}#9|eddL*(Pfj4Hjl)}p3^DNbdxYCe;|5- z4nmq%+5)bnH@LI!7M}l}aThrp2>q~=%`z%pyC6{MGx!>~9+A3bPx)SyC`0*aY@K7q zY%+e7U{;ri;4!&fB)_c)bAuRXw2HahYRsP>;LD#oug{l#`vR}f%}0BP3bZQUn<8Ev zZ?=>Cd(d*V3NzsfCKsIX)YFeOMZ_~?Xs*sy?DHM*kU=4-oNZ@H zvvGP``?Nj1L|SB+aFZ9w{Na~)ZgNEo%%FjR4yRwAOulS@lI&74j*DmUCq_-)a5Ti& zq)%VS`BM;tN{s{*(S7>%;v;a$6`8}VC^BWJul}vzK*kf3 zl74KUiaA1_#$`^a;gk~@n$cO}`2&_1z_(P@=B_@WI`nHRCr|IqwfvLvq}_K!kx=9Y zeMFgvk1W}V!ajQNWqU`3IZ4rE`i+ZRWvpco=MwQ*F-_678pFo5cn@=q z!7{?<9&$O7t?5yHMN12(R!`{%T63Ka#F}X4Oxg*H{)FT`~aG0A3DGo^z>`8 zcok^n6h8V}i!Aaj@oj;QGLUq6qil(u{s-)sZXotrbupKzEX4U{;ySKkKSE}N+rb)amZcU+fa@qKjkVJF&2I1 z9M0M+(B8fUN}`hxu`GK8=QJEpDEEZjfiewh%HAsRVv|)R5a9&8I|n+-Z-(cJrwu+3 zf%eFfpx5J?9;KCKA*{8Q#@8NeQ#tFZ1@RDjld8b9yskU(Ml)voD1z}bRbsTdCZ|43 z4H_3ElOGNCuq4Br93Y1rHEHcRk|*WkL%H9*?0N^0jk^jvYRpe5Q1r)F&|!>rAknSN zLg9%BAY%z^nY#7rT_}E1b1L)6iZG^bAxXUBC<5@W2Ci?ZE9Xp5#J|9wga2sKy7mfp zm&lrBEy~Vg{?+1Wq!MG=3VPHU`5GNLq&2-%APZwa^W)ljywtj@u~W2!!Qj5V`k#uK zt)QT-O<&Y7z~ID$Ku$2UqPMTk%A^0PPGt7oJ?~Dg%HF>3q;FKRGZ`kuwG$dxU+bFL zbuPGUr=X@x-4{X9QI`x366swifn zVL4ccQWMM#jrTjuUR;w2L8v#>No9-Kp-@qW3a*4clIr4ZZR^>n69Ji7<-2tCd~R&` za`VYw{n1Z8-6rw9w6!G4uBP)c$^O@!vWfD3WowJ{mpab+0f-N-HlK-Ek$pDOkoi#b zMW_xzg5F3aLM6F7KPGQc0a5kGdd(kEHN`^lS&O1G)-x$ax-~&;v*~`EAKd#BQ@;-j zz~JPM=U@R|O^aYHbDA^8Yi#)Gqvqh@N-Zm72|u}N6(oFOGB&J|h#y0YF=xyA4g}Ke zDL6o9hTx9wryD&SlYP&tw;u)q4GHlb3>$iW4P&nzvK}}5WA*yc`lkT=`5-_v3zr_n zokkZY%$-IDzMgHK!N5GR8N@VUd6Y-x%{!#`F5OR)Tj%8oU25LR(}Aka-Jry>33wceVDjpmqd{>!Qf@Um{G4oM&Om+ zHos?TUuEX*F43&}@P-cR;cbrP5g`%EYrPGxIH*K>;-2_Y7h!xg9&^^1RW0n(u7kY# zp-2$ODOJzFqMu9ZjXAYl^0wL*03@ipC94QOBUe3*KlvO?Baef^M~7ItFgz=fOJ)(M zJ2_Lyw!Nnvs}kbTtq)bxJME=2(|jwe;f(8C3?gC_k^PQhP{#^gNMS;#XiG#Qnz7mM zJ$81;EyT*FPJ3BituEmm6WR7G{E#ycx$dTl(j{1Z0HNx-Xd2;`)|Y*^lJ@?NTGm(E z+qSb5b?L2CLNWq}a?o7q{3f|539N59uldSqpJ-J10Qz8wgwKQkA(SM7cGueM2x zPZzi~70O7QwQs{~_mrlh-?UisxFsC-_xo6wu*@5)g~2%I&Wci>nTHoyT+9ZusJcqY zY$IXtzJ0q!{gyHi3cnR&mp1)L+z}>4bxz)*HyW$PRp`M&d*;qvpLJQ0RAP3?_fv+U z;psHJ!PqJ*gRT8BntYSTG$OSDb_Rzkqj`w4)xK|BKJAv9A~?PHbw_(6@arXn#wy|Z zJ^KR1B!;jG@wPb9=lciHs@ohqbA9Q3YvGyX9V<&eoT;DX4J^&>Fk+;Rk)+;GCmK4g8p+6TF~(NdJOx6%doNB`?>b2^a8T zVd5M`ejyg@UsyWg8R=qrxndQ`7ux{6_L<58HKB|u>gUMEHXV;7wwdFkP=;*yWr*9U zdom|RY|S}jk_QOL@aQ`pbD1!&(PS2LF3N6ihHQMcv7#FYqlxjgi`mkC|L&S``*q_)f*Q5VW6DCs@YNL7P@Sqpj&Z7ixUclB zzkPy7=#utVdG}ILr&G!`o#f9Nr$Q`P6y-unE7!X8*M%;H z1g<@dT=b-^NZUy~`~oy+gNO;f;}&;Auwbu=B88ePbSoqxLJIcG`+$Z_9s(4?|8l(i z{V6}`V8QAp6aDL+^+<#YMoe1qa`%r@4UWrvhmXx+=PWS*U5sN3F4EbmARj{Bd)hkR zq*D60OB9`WjXSWQ9bpy&dcn-&>t+~p{iR8Ibt9RKq!X5w_+ID3Nd4C~dW@^!eMx=c z5D^@X|M+aoScy-^x7{p7IJaT4?Ox~p%>y%w{7~mRPCY4i>aoUfwDY@o6}P-!PurjL z`1v+M`gs}Wrl$s>{4x0--qL|OZfq@fk*MXCiF-)kSKC?)C zVdkW&nbxUbNqu%(RdHtVU4_=~1|8C$l*1P&_Bh-IA*-^p8MNzt|4apN^)cg=3dfJ^ z!^gR#F=yTtej_~U`*Zb@mgEM}pb)N$pK86%jBK7_DvR;~c*gTImyQcS# z&C-;1b6K?yq>7Wr7OKr}W*VK-IPChV?~@=!!@#Wys>qwqU<@M!QQCa0Sl3@IU>tn_ z<((u$bZL~%H|NmJ!a#Oi_Fi4}!T*e>km^-&&J^}X4@XO$3wh=2$R<`sq<2ar#FQnb zd36Jh5g`EqF`rYyGKOJ+c{n zq|6uTZeL-Sq?nY(mEhj4*J_gu%xvcT4km-Ds&PZ_fdoZR4;T)l1)VW@f({ah2_@;9 zQpAj?0!Rljgt~Bc^i&qK+l|e*C)#%dh-RfoQC8me@{*kr932GFp{dg%n6n}POV4(3 zA^!!#9cIo^1__WJH3%Ev_>hU_^n`Pap&`Gk=KVtJt57(g3eIV1>uR$QmP!i_C6{&h ze_u9Q#)dv=JtZwu+?WN|lDt%7hEgnFhiuqzSrnMu&2o*TgoO^Rf~yb-}35%0%IddqV z;Evs*XF_>T85W|-R_1bE(HG##vz_(cG@e{-)NIEm<`kzQ5ifw38=S76G-y|t^=+OX z9>%`RVjcAAcr*G#5zc0S@hoZ@es;U_q@hc`PsN*A9D~*uh|RPwN1q#Ei%gW=OWb<# zk0y&*aK_;I$9xmyrXD`P<(M3O)-cE9A(wMpw7tgQtL2IX5$|a)BXFA1sH?d$61Io3 zBY-!7h${JTw9qGNvOONC=5{&q6D~o|n-d2cdr8&9cY4Ti{PmT9BT(8Jvtj72T6j4F*Pqu1O zL2kCM!{cM}zG_8TKo?D;Y8i zPMn*j9u}FCQbcvrfW1Kr!yaxw>V{rjf6y>Aoi|n|pdBaC;#};{8VI!8gM1rKR+Fbn zB7BEl2c$FK>uI|f-iry5-Zuh@+K7c7h4{f|uN2G=a54wp<*5dDfy>|=vxIkM35h+d z?G;+{G}g*W(JJ1g&F8L&1O4SE`>{G7RbiV)1)X`?=gh!L!Sw=a)G!1%4odkVROD z$01sLy`EoTz>D8?dX+sY!B~26JHvak*t|Wr7F-?_!ZqdTdkw=|xxlJI;4obNTv4fx zwK!ME?*yaNe|X=Xbbol>woH+&VAqD&P z#lI_nfIWSq=p9@~x0fbbiC5PR=OneO+Wmd8j4MkXeMij; zTzwfXm`3dyv0w_CaFPdmq2=N={M}bwy)3#?*s{?iW&6Qn;JU*s`+O(yrbDq$ z2@QW6FDmKe#LZj4=-eS%(9#n;+aLXEK`*G+0OpeNQo$(v-&iwn#8flMW_`7BrQYsb z%&j~VdrGpB&oE^JOjg#SBK=3FY~fZz+w@ht_ThQf%3K**2HLC(C?(eA>pI$wYU8DaZ@9j{6jzc$xxIgeyGkqEu_~}9DBVx z|4Q%UxXP2C%AhoT5HAoXE5cKp+{fa#N~*yYK2q@4eN6iOkkcz54k(;_bqh{N_x8PRjvSClWw zQgubi-5iS>WDgfZGq1$EKUIfFR+Q`bWD*a0LgM!&bb)|tzY=E_rN8#ouaIk4o;kmG zazW?%Hle(BuBr+L6}{+A<(bl0lwWI(A$>x@4o>$y(h1A3m>n_9bYlFRrp&sLAJe)j zl8JF&YS;s3{hoWDg6dN4zI;uz_ROYS)-&e`;IT$(&MFwNu`G6&o3T8g{sX8i4*vtF z^cxN_mlYt7io;SI^Xgb;V>PMrsOadC`*bSuelZ&SW4SwyGi}fohxl5}@+dP`@q?Pu zgt`$)+qeTwUHot3x4ofssqK^1{x(tlS}3a}71V5J{o$Yu@~Jf2#xVTj^V9# z)pNzEFHXuT(lWA>Cy#glY1QshwS(`NiyW}1yfSa$4g zuD*msjPw`^N${8yXv%lu6HO+XDx%3ix`>~QM>PROd$e^P2GiXPZ-RQAiHXKseIEL*vv|6t0EG4cJ;0q%-KUOS%8?&Fdd=|Fxr@%`^>rH_-R3)b@k*^6Sfeg~+$ z#&DlHIa*%co{f^J-|=cS{8qba@enSa=WpPS+c;3kk`^>32b>;!&VKa1F zVPMN2(}wB_-y4Enb$tXOnx`VYV~cFly^R-hRSX%k`I~d$DB`x8%e_M zP{LU<)1c-y=+pPBv71(Biu)z_avOxZVn0Cq{SrCOxSBJIqA0kNnaiG*z`@Qp(^ix` zTsfs{a?LsM5xaF!80Aor*^^?%0zI^RVbe3dwqjk3^Hi23kK;x_=G#(u09ooifQGzo z?Yw@~OI9`zRaDPld$pW+@h1qFQd^C@Ct)Y*OG76p=#*~Yo77tDs5)H;&xCrWS{Xn8 z0$ZdH#!QOF2XP*By7%fSOmPrep8B5MJpz6uE^XQ`Iw^rNYHFI7-$r3=*)nORt4TYR z);kOEWl_VOEC0F8F>z<8j3y*(y0>WXFD6;1v*kB zPt8+G_jCO*ZCD)Dy->lMXlpjaSRZ=nFVW`3U7Up~d?6{O&JJY>x|zO3dZh8Any7dyT- zd}SYUA!M;q?U!F6jbingPsz{9`|C33qeE-A*j!SI+a!5S+m2(cl4D^%3#~vj*fz{% z4NJotF+DzgVfcO6d~f==Ha*%^ktr%OYMPk_E1uCCv~MwX`|7Y}f8jFpE{!Fs1*9>4 zCyQPqB<8Hi7~qhK^WlYDrAlP+RPwzUWw!ldAZS_Nq{?EftTX6$mWWe)Xp17)!op3! z(RUIf&7ZTHHsCCx-ejn1M;qWx>kCeL%UcdWG)RX5s!c(hBGS^z5EG%)Na$8br{kHtvX^5I)42 zYg{mt2)qNw*H(+(*G$BExs7|hm^xsH(0lwH!u_Ph>ni1Gk(s%|0X5O*-y!IFUPlig zKEzH_F^^K^4Z%qldXJ~kHRVafuj(v;%>njUWj(=xpZ;3O2s zlF!A`XtZ?;Ebg}p+MXU3BD-pOCwn}1H{7T%-`%8dO~jI~4lXWkV`eU(eKyFn+_^uW1Tj{(Pjkr z&J5+*M^9_Nem8WAgdt;rJg#nC3c&?+CG@X2V}>wiJbh!$cljKci^ z)6{lB_f81y)NXR=qLCrFhRYqNb`?QRd2sL9Y|WsM0@@- z^lMlLP*x(a7x-LR)uVl!i{%y&sYBc?>K_G*yE8tf7hq0bi?q+Kw**zHI}*M4a$6Wh zh-GLuAuGZ@M&;*|FkbTdQh+6Z)qrN#P}M)~@iJZ`E_z<;tv}R*%YBhKI!jev3)nS9 zz5kX3_>&4i>bY!ab<(9evWRDA3o>CubP#S>}h|5k6kq=`V`-1|GX5K4O?1k_-Gac|p^f z&TFl}@d&BamanH3Ytc;- z3gdQaJL`QdR>kIjC^p3f9w;x9%Q|hhGZ+~`WR6_>t`&VP@e+Xg>LYf9+4;I7s<^OI z;hpbcS2yx_T(2-9VSenMb-UC`>xR@KQ+e)tZ}3d*RWKZS2-=y;dcn6Iud1by@BktZ z&&Cv6I~^H(b$Ai1K*hes z7*IOIIK%#2hW+1s98m@}69Dmn7n64RKzzU{(-epg5bM3gq4T6ABG~Yxsy?xtv~~S@ zEF{``?{a6cErMqnbs6}!Zfu$<>YD@GQXAE}tgWp88N}BDAsyws5^MX2tE@TSy8d15@3X8d_AdjU+1n;kl z5(ZV7b0H!|Fk5CkRAyjT{KIr!(j$2IlI+WGP~(Y|PrzYNK_fBjzmY-3tq>r%W9W% zbD^;28S%;IN+Dkce)6ol1?K?^Eb7auE963+jeUwyWVD_cQE)_&{Zun*`vHVrl%*x3 zxtUEJwHmHP8!z7(z<=s^yE6v%y_5n_xLH$oULyBCr@oK!02h;7B*r$-Lv1aKaTC%%LMY6Vbgo zFQP@87&QO>{g*i*|148_1-~5h5-8g=6p0TYE3Z?FDJ6|&%F!vYVHSsFe6Hu98=^?< zV>sR*CaxF7M3$<1ve`@J*l((U^q=;}O7(yBTK@I-z+Ik9mtHp_nlh>wM^pJ%E0m7t5-rJl5pHFUV^{Ynb_m3TmFU5c{I+>krPbz0YlWS=!9@n?sQ2 z0V*Dw`}WUthAh1xV5UL46cM)Anit<*sGnqgK~CEgq1L>hAmbZoTn@XNK^HxvTv1*d zE=ex!i3)QxA-dz#0${y$2x&|Ok*`C4i?r6?y=hg~vg|Pe2-mh78 z8*UgOx}|3EB#y6A;%7j+n+Q}3(AG7`{oM|((B}aqmf>E!Hh;5fL8AY?3FV^QH+?9M zT=$iSMRIY%$DU)m3dQ7a?+x3h9tm`RUBfLQg8(q3$pMF|U*dcCp)_P1k*(LybzrIP z-zM9}BVL2ZJ#a3SRbVwdZ7Q>!Jwmc z75zI|ZWJYAYUNGUO< zF5Rjj@3InBvPk8KyplY5Kc_<53+kIQ7)lC(z`XTSJFp}{s-3(XX38=a(k^qH=vn2M zywrgKX`*TIvT2rf-lm)1Hgk4rrkKITyG6?;WP+}dU9fma@s~KCF~8^-&9vg7m14iU zqE)emIEYow>K8=ek1KR}sP95|zaIbx0_YNU;)2|+Ok%j)E2_49R5Z^XpAw7w$b`8& zO33`YL~0p<*iy`2E3_|NJZR`Liy|s@H@qy}x!F?p&%&{s-3-)?Cl?E&3Cr zGKonj(0mkdJr0Bv?XG0+7XQ`=Wv37Br8}0PmqNjT@V}?vVP9jYFAyNI%y{DcqWd@8 z-s^gCV(DXO-lmHqGgDHyW?6H5mluby^5G`*b#e557iLFC!HJXOO5mFI#9nN?lBK8e z-6T1Yf~ueIlrnyS&VuP+*Gom0c*$vw*F7|;+KvHP+Fb_yq0q3 zt5w)u%zF3Q*je#oCDRM0)t?F$8Lt#n=qFeVx^`mu?hymG1;TpIf3dG?H9!xj?QXY~ z>8?pWHu3Hc)Ps}dp8uvZINww%Me!8@ye*b9OYc3NyGSjs#MOt``@vRobdx1aD<*~U zc7D^>jj&l00q&j&qOeDtP2#S-J-lSL-7v^@+=+AGnfQp~4fzK`Oq8_dPLxJI=ws@A$wj5AXm>|MjIaudgHlJ2KKTUxF+hlV6AkSd*%s z!Jp>Mg)go)rq|nm%)_%TyL#3e8{%)ce3Wt9E_IvG_A=L!aDFB5mBrp=5x?4bzhnwX zryD@K3GkKv-;Z1iXrU%UsB_*Y9=gZ1uV^6-Dn01LbXaR)6W1p=p|j~hm3Q<{;#sVX zP-Wi|E4OX=eY*hYWTY#3ZkqY%IBsh=&-8Tr?Psgz8N`Rap$w2v?GlJCdp6f`PrF#G zX6CLgb$~a-Z3(xDTJ@z6P#8wr{qq!lvZ)^2@CE-BH*0u$7G}ulid+fbms7^iDZTtk=yU*pg{MgUAkw*5cUhQHJY4h=(J#&|WrILxw-Kza+0 zc6$=?Ka&q}j%;8yw-PMXBb-`WZUOzC9G9Eg7eYr#SlF3gVoX#?G5HT<8WLKb2}027 zaJQgJoEm7j(tPvHX1i%*?inSt(dgN7sUu>vo2Db-O!gmuElK<*(8{4c{o=*=>D$w) zR1DrhfMcamEM6b+j8-^-Sp1zNaZ>=sy^-209j<1)-maQGR=5NI(^}~-NhJf+t%4pS zK*bnZ*9|;85G_4I4}w>Z8=C2d%w!!;^aTjSiAy3V-?-353de0|if2Z?nn4)g*YtF! zUn7RSf{Rn-#@V=TdTa_!Omo5xU>`)_&m^qBlANv_D6ace2txDnbW#e|>$e=j0LXIQ8xM30qZq#4diC7#S; zSUqLwXtdc*K2b~PVcu@4n6Fh>IzVVrLiu@034yE<_ODDF>|#C~emdf9f7zC;a;NKJ z(#(pD@dUq=(&hT*0d$t{Z#zKG5Vb%!+W;(q%+IQoH!)@&_zk6X0CFy;L~byDM57fcuW_)M569#x2!R&rLLD4vgLV zO)){)K|ebXAt9g1d?h7aArJy=yNB=oS{1+(=NnT&y>5(Gk#49E|4dF&o>iJRh#a8Q z2{-kS?@Vyne7&|RmtZKeV}-ZJhj5m&NyrKt`o$!L9d*h? zC*30JC?S{0XIH#u8H`9ijGG|3nt%0j#rxKV=V?74ScmyAzyXVa|xY7|}5k8&0QJ^x?J0Nu4P`GL#)= z|MqFFLY!IdhYfevKj}(t`hYVy{1EcH`tTdk%Jj0y*4CBs^GlfRQj_l+ zZ3jhV#95&x*v16UXEndZE!hKBU;};k@+F(_n%Dcq5;e5Hxke^oqcd%^NfqLSa(Oi6 zdDuFzTQ=&6`uI&i11*G=mDBW-imx%Z=24_gk$dC}7`{|fG#u)uBBQ)-sd91%`{OxL z4tpknqVmg{fnbxl;P>*jda;Qgh4P>uU#pthyU!2J;n zF=c9WTMoGcJ8Dokl&Yvn{{Th@Mv=ElnxnmS^(#hGzxR>1Hc$ zU&m89ve|oS4on2+x2d>o+AFK++=b=<0%^;=@Y0&ZIa}%jNcaJCdE*sMF-z|up5u7u z4im3B4QJCZk^b>s0{2;elrJK;yQazdeU3(31ls}41C7KifVA=#`>c z;_00@z^7p>C)n0Q_uAkFglduVELSV%md7G+i+Z5x2Ht?-wQQKSeKUpRx%r>v7McEo zrK}CFA6#gc9`6kCPP3O1^0&QS>SSe;vdmN9j z)-c>+?p9jKIbEo}?IN$o! zrPU+r4S9B7KVCoov4Jg!EasEX;kygGO!lyfq$hiS zM>>`}=&%AYp-~f}r~m?tn~d?B$pMH;+(Oij{${O2f)=o|ot8Yk1zYsfHB2H>rhDqN zOuex)!U*ZeC|cM!$0F^)oY09CFpT^pm(u5SBqtJRB`2o}o+R=S!sJ(226*G(#)w`_boa0VA zZW8MGGgs8KQ#oHeK=JW5DPU0G$K}6g=_;m)dlf!?k`JC@ycE@+j4C9_>eCK$jg}&* z=OJ4W$3Wa=M0i#uIv;xO(V+FK`I_LE(gm9HLv(vM{?#=kpfM0n_5alNGOE zD<@ZWEuH;A_h9e2v!$AnYN(1HQ2l_O@K5y<1FD~bp-OQ2#MRWI{ls=Ex9-;1rsRw9CnjbE}>5{A4G*hrRS9D~n8E)7G@hMrz}&b4dut7{2J zI=e>qEEw5?7|8=cNSgvl(R6l(M_dbx7!B1He>Q>upt1I3cmPEysRQpmamy*PsDkzY zEn-1WAx3SI?d&lRT5VkBlcCyNM*ksOG|)N0N8qeoBt>sz<^ttT?5J0^R0l7<%gi9) zW#fO|BfOSyE#4@y5+8GyvD#7Ru<$gR7`A!w67nnT6rHa00R%asSW%9^;Yx7CSNi}g zWX|9J`k(omX7leqAHJg8vH}k4r+}+E{^pbMcigrs>twXOR+A(gz*SvG86-?%^UU;} zmR({$>uA^Oj-J}9NTm5?L%$y6uo$W2T9WNJ9VaLS0l?->6(`68CaSKr*N+1eRa?FQ z6IBUtssBt=-SI_g>ogvDn?*pkdTuhD;1w=k%XG2rd4%D4JsiTgo^VTB{YI|?%vG1!C z{NlTn^A&$m=rl%+C}<~GeA`?}P_Y&9kJP_cFOKfpdu63+L^Om7NzNG!7VWkh?RZyE zDVr(^OypbHrOz7tv;DJ;t*;#nJL*b_yd)Na=oV$TOAK>=2sf#{LG8CStX&aBX2iL$ z8^?;2<+lg_%FU}wb|+w-tuk4lRG9eR$wEnc+~IO^L7m3A3v=RHc9JGWD5rx+JFU0* z;D(*1cM7a~;};yLvlM2Sk^F0nHH;@Y5PxI2L=98NX1=4}DO7QVEW{b3BXvSg+sKE~ zuW>p~ym|APGNF}u;<2uVrD{(_ew+sj9-K7{0+vnJEu8_C+OZ{zLdIEO7svDUx!Bla0P5~O>8Wkd*&Wpba|5a=>kgfsO&q zHk~nWTLSk1ZcEa7ZacX?hvYFU8}sf%2}9GiW>KT(%+7a+W|4c|F1;jPPppIAU>*5r zDk~_vH)g{3FMwZ-&wuLZlld3 z$kFVSxefwT=*Qi&Z_sB9VniD2Z4>QM3NE_k_AkimN}%a+5n^8)W394oeo&Ae-qi$D zI+RvBMed)_N7eVhL9znaAmm|ZK1N*t#fuvgV;{c@w`!)p)OB-=mY%ViN3csH$LqT7 zp|~BI$F>j(ijWnDqWz>Pct%d6{rZep-`1m*d-YU#{vuvOR5&L~=T^VL#k+izDnMqw za*P;>;yaS-)*#SNG+ZXHTKst52|-Qw@9NQ4e0+9wL3Yo(T6@h@*J$fNTd9V)O4ioH z6;vY2-ODWuDSRwZ6M-d!jZa2t@YKnZ{4&qVjm4?{H>Mdt$gv~7o0RoDWMtyUg2SYE z3E*4`cFJuE2{o)uTmToRF>oa!Bw_H>R4tmlrq<@vVDM4LZFJ+g_59ub5S z-YX7ew<$X^bvFG9VXit`Ri7hH!W$%zfV@-VHwGv*vd46ix)RC?6W^DgbT%D5VlLNw z^jqS(iwJpB;9jK2P-k(-jmkBZcW{CX^;!kG$Q3<*!n|h8O8t{=tly40+KhPmf*~V4 zWPH`CNnK4NU8|DBJbs7zgnbi(=nl<`Z{JOaa68 z(`y8NQdRV{zkXB#3%M~HN3AM=)D^#K2#~sLInbmw(InxAV2idzyl^adP zU7;(dgIMq^%DP&(*5uSUkWd;vQrj82XL5b8n0lCD3Xe%{whYl=kr+{v z9Ch7|$n3@qW8%5h-nwh;QiY&(H*ptdNJxl9PZYH)xq^hMp*J&5?hJN%*}mX=8{!W3 z#vCEJKK5GxKJRyrt9R}20P0D-GjB0r>%t+B5`d7Nsz{TIW5Tbn>5y58w?(>&#w#bo zW;qnqf`C(Y$dfW-H(;`Nr>hAkFxlH$LN^VV>^%+p3Q(Z&qzX7^8ebi8^efL<6uPSN znAn{_s?m_6dij*X&emXcRBCR(bnDM6dO`d!?~yqjPF=IPSbTfwjx@vP(Jth|O5J3J zbQ27&yh+vfXOs=M5s>seGiX&nmC-A%ow$@ooz5Up6h{ETdwouIQBXNV{%>_Uy{;}~ zaMU7i$>?>46s>*|6EP!lim&&_6$r$}#CmK&4*yv;j}+KnA;u@#W!nOKtXr43DYl!i zG?RNNh0?Vi1Jqc2*fL9OAO-NYj7Ym8IVpx0a4=Vr!c zzfPd3@?tEIj_8gZI_sZN!|>OFzBqPrC5e?fZVJP%R1tEuri0769J0M{hp?Ku8Rt#H zfOyW+OW(PUdVak*T5)Gr`>q8;TO?YcZYsY`H!^PqyVX*~pGEuOP#{>`CasF$@3E!Qea$U&Y-cK7=W&Nm$ za?VRn`~}?eW34t!$x$)ifkt?QE%dA#q3Jv;({$=9&Jt~l`RWMA>sR*Qt+^#!u2zQO z3i&$_o}9CvU;cE}?U1(WXx+?D~s6 zp1vYVs<=R6UAs0*c-SfCaLE34)JdJcv0_ntKM)junMD1U(*k&+QHD8eaqD4ht43L$ zVCfn#Gpu%E*rq{~Yi}(Ofb*>pxvQDP=BY&qF4oNa7cB$i&uStN#ynkt6bzvwsg!0l z74Vp6?#2#Sx0+KuSjB#X>;h1*PDT99M?SI*B8n8B*x7l=J_%Wv;8Ml@Qt>HoJ%hYC zb>&{1^rb3uD}c>=-orgb{E`(1H|wR6Q)HZ*bScl2;96gp?-hW;Ac1wuC0xtrW=f8>q*`7L5 zpYGh4?@tb{WwU)O7>)kc9OIvsmc>TOh-H_ImS{lgf$qC zh{^#inf>`A7EHKYZS((f=ek|&RwOaiG>*cKbz5)DWdOnCyOedQh2;uk3~_WeK*a~EUZjMAvfj|Gpsg5 z693aKe_QRvg-pHKe1p9tg||_RH!pJOqk2lR%WApj+u;P219#t$Jy+iBE!z3QorUaD z{7UJj2s}Ip!YGWA%2u8^8cI7Gd7xopm!4iB8gOqEd^?39KQQ{b_)`yyRrk-`W{`hh zbvflOzZy-CS4&Zi7H8SDmnxVf`Si_8Ffk)E442O)=W3f(`$~7&2$WHXH zk!MWRK|Xiuy|)99`sDPPMSQv1cKa{{^6f@Fvx2Rf2_18u0QLx+eLHfQ59?!OQyckR z!#%;&6ywhf;Q~^op0~dj+lc@XT0@(_lXj+cX{JR!Jr!S3j#&(sD_Q{cgJ^WeA&7jy z(bk>&h_x%h2DMNUfohc*$x?{T*S74q*+GFr@MDa>H0P*+EFAxTnAjH^{^u^@|1jJ2 zBE#a1=6NFd-PeXyPYgj09>>qq5%;w{R8s?v6PKi&J+0RdqeF{@}H>6Noo+c_+-`1-iLUwU}p9i z=qni&m0e^SPJ0srl-Lm)nsi`%^`jEdMNg4*7=0T*E|N?ss_|9ScBoOs|0?#pM^|h- z7sM6VsH4g(Pgyrs?(!uzb{=SmmVzJ4S*mhN3;UtJpB|I?*9kdl{LiU{V9H(iaP-nohhrXVX2vRB z)SpItsP8L9H@YAN5}6SR++ykWah6z?%eziPfs}RB@$jC0Uf|M0w9;)&Xe%R>TxT>Z z``~>hu>D6Q{ks zRobE)A6rK?1G9aNmD;+MqNW6&Ct+>G4M`ctk@3Yyw2WwYQcSVlilw*bwl?sr8wHgb zlEU)su3$e<$gn1}IEP}jwL%wARmQpISXD=gZ1&P?zdnGlLX*gzM?(F4UCBbK#2(4y zIJgW23BNGc^!gzj`v9`3CKlFZ=YYw{hI^ySN(r*sUc8wibbg9Dj@!mz=wSa-m6(_q zJtd$yC0`R3m1BP73iXeD!AubMDd@30bWce66s~{xQvGyu0|JX97)mgU79;DgRv#HB zTlq%Iy^}(7sbWQ^sJ+R-#gPx!l$?tiA_L>a2|?kw5~8{~ZpOfwrQgIA6F9S2IavQW zv&>U`Rh&L!T75aCyK_ab33N)Lv$rgjFhm!P8Vkwn!ultQ$Goy0rHG?WH%Ir2B5&}l%FS5 zC=AGRt~pV+dW}o@Th+q4g5OE3u6CNr!rWZFN~&BtEiN;Zbxb<0{jUj3;juw8%HBO0 z=1COx%8xwmz{%tL_nijE$S0|B!a28y!@R1Q(^Hz<;g34cYDmJf3U(8WRqr=ZqRzTM zT0*PBimI&T$xJr-A|`9k4Ss6`v&c#@Mw@}X3}6zlfTYdcHGJHV-fwV zwBTx;QW?j`&R=V8ef05Oa-n%#zf3M^;~hsCEaw&+AGPlYPM7uZE8F-aCQNktPCjx96UF?%Z|ee%Nd6wZH7_mGy7$`#isgXc7XH!%ucO z=+9*!_js{cv5#|@uu(Ot_iioEA!Rs3+PK%n#>pS%)#dH}NOytWCfLW^A8O?GELgbz z`>gD4TN~|@oT8(f1{dx?vKwr&%szyd`z~&}&M9r1q zRk{!7RB{JFp`Un`JmU76v1`)X#f~6iOaaj;EQ7#5D#zo~DWI=+HDJ`+T_v z5-P)<4Nhm4Jv1}4a}dn+@xV~HBwW0pA6zvaaMn_D)qW`}!E5j2<2P#k;1%xU5A99q z>z3YnnZNb=r{p_~F3into&GLbDMu$cS++ezopVfluW7OEnx^`oDRF~eJezODp>>I7 z??Kje&Jl`eL*m;W%F{{lma4Ls$X7Y?F9+jUs(f&_Le8W;a=b+wn>H*YRLL*>DwtrO zm9(T&$cNd8Yk*Czl!aMgdc?Nv6KvkU@qR+dtR4K>mq1PWSjA%6si$~rJH@_C;2t(j z0w*l$E~wIr4i%Q#blOPSRu#Q^ywR&wa<{RBK3}?y~hT6G4F%cC$CAb_;1$pcDSP>ip6wuPG#7r z)YwjBgun3gaeH3GXzKI7jnEI(#fb9}+{sFDh(_iKCqa<^E>fBpuo(yzY~w5g@*4yO z8gOP}-6Q;D5L=!!(*Du3pz{q*Pth&IlL-YQO>5=H^X+TcBvYUyYn99_=g z4u4m{=RN6KX)$xr#C+40K~A&t-W|RHZKormynXJU1q+kz`Fh16_Lu@ zl}O&rlT}d)QzUV%CQH_we?KEn&0A1Yj>zFH?nx2Vbz*C8uBPYWP8Ck?{5;~=p`@m^ z`JYZ%v8K;HvnG?GwZ}ha-#+ALip?Ss0k64sv9Je&UO^$mCMOK<6C```CJKN;5QT{C zMX0l!3^G-9F&M<`eYm zb!X-cO!{1fu6bsG*`4GSHy6@Jbhz&W4&`HXYJXuHaU=^}nctD&9)D?BtAKr0&K?u9 zVo9w?KS^wVoz@w(T0(5>vmE-pr!FpsnWCaH6+|-+S$pxDhP;tCjIUOD?8I%fdkgqF zJ0ak3O0>4f=r05DE-W*C($7%DUC(9BO?ks=@Uf6Ih&+1N%*p&2!6=})IB(7Xh#|In z9MuZ(qm5NkSmS6X|AEsQ-4iWQ1K8VM{lxJuWTZop!ANDUGZLN3#lGV2xap?JHP0YQ z&%9B8#Qn5*#^CmIlnR>h(n4rA}cA)NVAy3@a5mdV7X_|o+ON1LSYnw zvnh*nN;gN;GMbSn=kD%Jg8-+PbJGlBKrsK=Rs)IGw>JrTgto9p?bk1;YbCvh4jn9{9;&6Lr(C3?3>LT-PbDjA?2-mc zk#1K(YSTrN6In+CDhLVM1Zc(X6VtRo+bZx@ZT1I1n5jWh0*=rfTYCMh6o+$cQ~NiI zKy7H)I$9R)|IH@8C~oRcP-;wPs=g@Z9-?>cbcV8&*hDhYZm$gbeN*1(c>l&FG)3E> ztQ7tEbCT|e5>f6ScIOK6f=YpwGLgUW%H5tdnmb?lW>Ww9b|acwxmINvL0SPWmn$Z~ zQR@z7jvv-TC+oWLye?Lofy^T?88TD3VJ)o&Gw&Zcfk}OSVB(OF^zjO~yPVMBUdD3x zy-*3&6ZmsI;(oLLsVoceXw*~ zun#!-V4w3+E8yuBT^kw1lBk#$3@Y9dz9@!sAV6KLF2@)-@4kFxqff9+4vK$}@E)zXZ8c`4up>9`>~h;3cIWhhJ)r=2RVe zn5Ol!uc_Xtcm~oT;iK_5dJWiKIb&+{GcFBAX0{(yk3E0r#EU$C0a9*-MCa_0NuJ1u z+PVCef7cVAxok9FSUcKRz_ucD;Sv2OX!Ts5*L2r*&fd$TuZ4z5c|HadXi&mCrm1IF z0<@Mf8}EDdH9i^raev0P+aa6_MV&EuT)10>lF`@KvGbIo#7kaE>s~;$2;BxR`SS&R zU4Xs2rT4EIH?2vhEjIZsmq<3xAfSk-ITg(O@8&${LirKvSP-*3gp*c%$*uc-C{&$>2a&mWSW(+m#6YFST^EIto(D{uG zf>G6~gEl(?jf*bI?4MI7bH5zLb{NJDPDX$6fx;CEYm+jHuUcUgW9r8zf03!KhmF7B_zsFzoU|+9dg7c?vf9XrRkH51-KSRC$ zT8((bF2V6IR0T>r{n?otV4^wkd;pke_kYDed?LySu!0PZ% zj|adXs;c((Fd9g?_jX2#nQJ+ujTP+4$ml2k;TlXB*{)pJqP;3*t8qv(f-Al6=>f-* z^{jF#jzT36zzoP%=jIh_(8=mPq5_WfOSqMa-c%t-9j9=rgH4%lK<{4pf^}Vdg1#En zyf=)Bj`uA1l4q}C$z6e;j1-v7y0;U_FY^J=r54sBL zpAhv48uoPpLbeG_hRSCF$_H0+1p8B@&$UG8F>+Ju<)+-h-XyzbJTAi4w%A-DD^&F1Rjs~|;gDi8qr7UH}Obt9`QC=UQf z9ByLYJ?Td`M5}{3UnlJBMUGoBrqMsWachV-v*KXuKEeEX`^HzmI>R*8tDB_zd#W~jmlZAU+DOWf0b-gJlkd?qCbV%T z{TW{=NET0bwKiU^+M#cE#F*f{P<}d7XIr*9sr2diI518ejD)({*4=bu?Q8xKuZw< zibicD-zu@x0h%l3sy*o3OzS}5lC}N3Ing3*kf~jJkVOj!((8v8_}?jaQo8OR6VzIB zZ*iE}oa3M#BEy5f$0(Y`z?r1v8qCf%*x9lZNuTu*j@3b6z4VVm!Xo@F5mJiIIpZ?Rmw6vcbV{yCZMBxx*Bmg`+xY7;`vddfm4gM>2S)r+Dr@|CYLA=b zOoH8&wPP$61-Mh_Bi@qD?@zggG*WSftr-72F%(laBh_@Fu^=quAAqfsuJ6vmPZNE0*Su7+U}-=ldE z&@wohS!xLHNY7nI{}S03MNF07#g!Ho;pwA`VULqvZWEIR1DjvP`T`p0!S4Z$z^EP4DSgMbgD*bc5578s=kOORH zqu~g!)`6G*(sCgJ?nz_(;RiZK2+y>K;zlaVwjnwzOtLIi!8wPohhj-di164Gk_u&* z215bB{MH@rR3vUA@n-yv$iLB4UF>@!r`fqmyB>aTX?Ex#XbtK9u)EN%<)h_YEkU|@ zM*Gr1^>dq2M?dGLBXuhVZ!*Y}M5Xzvqg!9m-EG?o@o(ER9gzZ>oaG@<~--4(gTWfu!rmy9q3j*8%&co`e zC84hz`WIA+?edMMUa#{CDp6GQ+^K1l&7AZ3U=)T3sdnvC<<YhQ2LoMy|S@Y}L?wJKx{6QByY@R%tbhKt}+oaccJ?4$qL zNBye5|DOv`ASII+4Zho`|5>9)_WemmIGc|k8D5H_s}>xRyj{%Qq18q62J3|$XPEB& zRMFa_1-igI;@Eg?MCVq>%)aN32e3>}eZ8qZ-)eS9vx;)RX^=`3*QKoq9QBOl!VVVS zzS^fUgm?{q{wU)nFv_*~56SL9nQLL?cZZ8&feN`S8uPi zR%+GI-rqIUZTK|*AaP<~QAj2ZTChj@{nE*iT+r!{&SFPt_IoM03yvRo?edLOVYMtO zANL|d;-;0kkI0&ggu2iFg*#HttJ|d9%9~ED||sco3tBtv8)$GVdu+FS0y*!`%e z&TolP?=-`&@>D5|aYx6UM>!If;whqiyC!I0oU=_yk2YbaJxgEml zt;f|4QgQX~R({=e+5szP`dZSGqlKm*606py)Wx!KdB;w(Bs}bcP8flIwqK zj{hwe;q?zn6^l~t|9LN|qz^P>&bZ-E?lh}=F& zxb5=txP)UwUH(b=m-u2;S649N`5IjjQCAJ47tl{hblKYh3Na-Aqb>n@@j-{oo$d~_ zRM2N>dZ)T_{^5%-icZk6?V!mTwOcorShkw3Z6+O1`u({h=Mt0Mf1S`fX964ubqPg5 zO@yGZ?KA)A2tJJ*-172caE0vP@?bc#;Q?W$p3lUA5G!P|9Opt?w}(_SgGR z@N9%4%Oj0vOWsR?x|R_YrCp{kvS7TqCjZt8aP-F)QebxKJdi z7LtZI&LnrSL!KIN#&dgn<$5%;Ac>BD^rl|30JGRDN1gLK(=)>+%$EX9q}Ksr4}P9; z{t05%cb0?t*lyf8nj5rltN3Eif{_hW$8kpcWUV*e^*lA4(oerMn9!SrzgCSlQGd0( zrciag<%_TFb8g$O`fe&3D15M1t+eZzqQIgxYouNll6o1oF$ku#J}qN7bPZ=~ufMEm zW91^}4&h|g;i%nHg*J;srfS(CNU5O4$!a};w(J1$?%z2WM9=4qOU6IPN)UC5Y@#>^ z^0NL&RyRbO7t$Ps5pSVb1%2+VOO?#O2=#R# z2WvNiJYf;jgjS)hEn^*RZ4BWlZUoi7>T=vy3`?Tty*geCI;lS^D(Wy0#*j&oc$MzP z3n#oFM~9LvMc@G&TkpKb%a#VhrhM2OD(FGJx`p0_$h&5Gw1=XO>F`MyM&(bC&#-B` zB6sGDDmavEQ~Af~3A@b7Mwzn>M87)LOrz?DcHDZb@A?>}&>B+wK>B;~fv4qXR#g&k zsEi&qz4nmjX@W2h%Yxk5>h?@SwiY9^fR3+7*4g{v#W}I#NePK)cDyadR?;M~$EpjF zS8FuiJYj*-iw1og5xiv){0U&2kVigQy%(>R4X~Rh9hftmDop#6tbUk7<2Hi^{l_m> zj~iC4yjcDO6$K@9!D!5JmriARl$r6NIo7}X+-aOaSIS-rm^)C&4vS&?JJP-mI>E}H zCCx$*01ud&44;`l|Nq4LW1U(i)WESliKO#fQyS zHBA{do7Ip}Wa!t9I|8MvRp}Qq{7G)>I7m_H4@toCw08Jt z#MN^AuiKm=g3;UNguFTPdGV2JBI1sykJOcaXC9GqeNd@qXOzE|T1xch6Qmgj@w=5D|h-3_q zZs=4FzPAAqC>!TO!>4w>w^l8*nH4c~578ym1^&5|j mvv@x|7l$8HW>)$18^>nD%|AiJp};YU$r~eDSvwExm literal 0 HcmV?d00001 diff --git a/tests/data/sample1/eosin.png b/tests/data/sample1/eosin.png new file mode 100644 index 0000000000000000000000000000000000000000..dbbc43e1f89585ce9131b62cd408bfda68fb7c76 GIT binary patch literal 183161 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

u=Q|CJeHjO9#3ae8&I170Leij+2Yt_Z60Pl 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ðd36n2EjZ-UN$)p6e&HA65pPz`p!x#VGw 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%0D!$GQYU4*;Z`bAoerYjA`A>BV4s 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 IM6N<$g7Ww23IG5A literal 0 HcmV?d00001 diff --git a/tests/data/sample1/original.jpg b/tests/data/sample1/original.jpg new file mode 100644 index 0000000000000000000000000000000000000000..41a0c97bcee644e2ad1ca46101bbf8d14e30cb1c GIT binary patch literal 83398 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 NDArray[np.float64]: + low, high = np.min(x), np.max(x) + + return (x - low) * (up - down) / (high - low) + down + + +def _noise_array( + size: int, params: list[tuple[int | None, float]] +) -> NDArray[np.float64]: + result = np.zeros(shape=(size, size), dtype=np.float64) + + for seed, octaves in params: + noise = PerlinNoise(octaves=octaves, seed=seed) + + arr = np.array( + [[noise([i / size, j / size]) for j in range(size)] for i in range(size)], + dtype=np.float64, + ) + arr = _stretch(arr, -1, 1) + result += arr + + result[result < 0] = 1 + result[(result >= 0) & (result < 0.5)] = 2 + result[(result >= 0.5) & (result < 0.85)] = 3 + result[(result >= 0.85) & (result < 1)] = 4 + + return result + + +def artificial_image(c0: RGBArray, c1: RGBArray, conv: ColorConversion) -> RGBArray: + noise = _noise_array(size=512, params=[(None, 10), (None, 4)]) + + c0_pure = convert_color(c0, conv)[0] + c1_pure = convert_color(c1, conv)[1] + + c0_noise = np.where((noise == 3) | (noise == 4), c0_pure, 0) + c1_noise = np.where((noise >= 2) & (noise < 4), c1_pure, 0) + + img = np.stack([c0_noise, c1_noise, ZEROS], axis=-1) + + return np.asarray(convert_color(img, conv.inverse), dtype=np.uint8) + + +def main() -> None: + h_e = artificial_image(H_RGB, E_RGB, ColorConversion.RGB2HER) + Image.fromarray(h_e).save("tests/data/generated/h_e.jpg") + + h_dab = artificial_image(H_RGB, DAB_RGB, ColorConversion.RGB2HDR) + Image.fromarray(h_dab).save("tests/data/generated/h_dab.jpg") + + +if __name__ == "__main__": + main() diff --git a/tests/test_color_conversion.py b/tests/test_color_conversion.py new file mode 100644 index 0000000..4e842fc --- /dev/null +++ b/tests/test_color_conversion.py @@ -0,0 +1,31 @@ +import numpy as np +import pytest + +from rationai.staining import ColorConversion +from rationai.staining.constants import QUPATH_DAB, QUPATH_E, QUPATH_H +from rationai.staining.typing import StainTuple +from rationai.staining.utils import residual + + +@pytest.mark.parametrize( + "conversion", + ColorConversion, + ids=(conversion.name for conversion in ColorConversion), +) +def test_inverse_conversion(conversion: ColorConversion) -> None: + original = conversion.matrix + inverse = conversion.inverse.matrix + + assert np.all(np.isclose(original, np.linalg.inv(inverse))) + + +STAINS = [ + # Expected values are taken from QuPath + (QUPATH_H, QUPATH_E, (0.316, -0.598, 0.737)), + (QUPATH_H, QUPATH_DAB, (0.633, -0.713, 0.302)), +] + + +@pytest.mark.parametrize("s0,s1,expected", STAINS, ids=["H&E", "H&DAB"]) +def test_residual(s0: StainTuple, s1: StainTuple, expected: StainTuple) -> None: + assert np.all(np.isclose(residual(s0, s1), expected, atol=0.001)) diff --git a/tests/test_stain_estimator.py b/tests/test_stain_estimator.py new file mode 100644 index 0000000..8104b59 --- /dev/null +++ b/tests/test_stain_estimator.py @@ -0,0 +1,105 @@ +from pathlib import Path +from typing import Any + +import numpy as np +import pytest +from numpy.typing import NDArray +from PIL import Image +from skimage.color import deltaE_ciede2000 +from skimage.data import immunohistochemistry +from skimage.filters import gaussian + +from rationai.staining import ColorConversion, convert_color, estimate_stain_vectors +from rationai.staining.constants import QUPATH_DAB, QUPATH_E, QUPATH_H +from rationai.staining.typing import StainArray, Tile + + +ZEROS = np.zeros(shape=(512, 512), dtype=np.float64) + + +H_RGB = np.array([88, 76, 180], dtype=np.uint8) # from QuPath +E_RGB = np.array([199, 50, 112], dtype=np.uint8) # from QuPath +DAB_RGB = np.array([186, 110, 56], dtype=np.uint8) # from QuPath + + +NANS = np.full(shape=(3), fill_value=np.nan) + +GENERATED_DIR = Path("tests/data/generated") + + +def _blur(img: NDArray[np.uint8], sigma: float) -> NDArray[np.uint8]: + result = np.empty_like(img) + channels = [img[..., 0], img[..., 1], img[..., 2]] + + for i, c in enumerate(channels): + result[..., i] = (255 * gaussian(c, sigma=sigma)).astype(np.uint8) + + return result + + +# Input, first expected stain, second expected stain +DATA = [ + ( + immunohistochemistry(), + QUPATH_H, + QUPATH_DAB, + ), + ( + np.asarray(Image.open(GENERATED_DIR / "h_e.jpg")), + QUPATH_H, + QUPATH_E, + ), + ( + np.asarray(Image.open(GENERATED_DIR / "h_dab.jpg")), + QUPATH_H, + QUPATH_DAB, + ), + ( + _blur(np.asarray(Image.open(GENERATED_DIR / "h_e.jpg")), sigma=5), + QUPATH_H, + QUPATH_E, + ), + ( + _blur(np.asarray(Image.open(GENERATED_DIR / "h_dab.jpg")), sigma=5), + QUPATH_H, + QUPATH_DAB, + ), + ( + convert_color(np.stack([ZEROS] * 3, axis=-1), ColorConversion.HER2RGB), + NANS, + NANS, + ), +] + +IDS = [ + "Default skimage image H&DAB", + "Artificial H&E", + "Artificial H&DAB", + "Blurred Artificial H&E", + "Blurred Artificial H&DAB", + "Empty Image", +] + + +@pytest.mark.parametrize("img,expected1,expected2", DATA, ids=IDS) +class TestStainEstimator: + @staticmethod + def _both_nan(x: NDArray[Any], y: NDArray[Any]) -> bool: + return bool(np.isnan(x).all()) and bool(np.isnan(y).all()) + + def test_estimation_vector_similarity( + self, img: Tile, expected1: StainArray, expected2: StainArray + ) -> None: + stain1, stain2 = estimate_stain_vectors(img) + + assert np.dot(stain1, expected1) > 0.97 or self._both_nan(stain1, expected1) + assert np.dot(stain2, expected2) > 0.97 or self._both_nan(stain2, expected2) + + def test_estimation_color_difference( + self, img: Tile, expected1: StainArray, expected2: StainArray + ) -> None: + stain1, stain2 = estimate_stain_vectors(img) + delta = deltaE_ciede2000 + + assert delta(stain1, expected1) < 1 or self._both_nan(stain1, expected1) + assert delta(stain2, expected2) < 1 or self._both_nan(stain2, expected2) diff --git a/tests/test_stain_reconstruction.py b/tests/test_stain_reconstruction.py new file mode 100644 index 0000000..4a5f53b --- /dev/null +++ b/tests/test_stain_reconstruction.py @@ -0,0 +1,40 @@ +import numpy as np +import pytest +from skimage.data import immunohistochemistry +from skimage.metrics import structural_similarity + +from rationai.staining import ColorConversion, convert_color +from rationai.staining.typing import RGBArray + + +IMAGES_AND_CONVS = [ + ( + immunohistochemistry(), + ColorConversion.RGB2HDR, + ), + ( + "sample1_original", + ColorConversion.RGB2HER, + ), +] + +TEST_IDS = ["skimage-default; H&DAB", "Sample1; H&E"] + + +@pytest.mark.parametrize("original,conv", IMAGES_AND_CONVS, ids=TEST_IDS) +def test_structural_similarity( + original: RGBArray | str, conv: ColorConversion, request: pytest.FixtureRequest +) -> None: + """For reconstruction, only structural similarity is tested. + + Due to implementation details, the reconsturction is not idempotent, + and some bigger color shifts can occur in some conversions. + Therefore, the color similarity cannot be reliably tested. + """ + if isinstance(original, str): + original = request.getfixturevalue(original) + + c0, c1, c2 = convert_color(original, conv) # type: ignore[arg-type] + reconstructed = convert_color(np.stack([c0, c1, c2], axis=-1), conv.inverse) + + assert structural_similarity(original, reconstructed, channel_axis=-1) > 0.95 diff --git a/tests/test_stain_separation.py b/tests/test_stain_separation.py new file mode 100644 index 0000000..e02357e --- /dev/null +++ b/tests/test_stain_separation.py @@ -0,0 +1,52 @@ +import numpy as np +import pytest +from numpy.typing import NDArray +from skimage.metrics import structural_similarity + +from rationai.staining import ColorConversion, convert_color +from rationai.staining.typing import RGBArray, StainArray + + +RGB_CONVERSIONS = [ + ColorConversion.RGB2HER, + ColorConversion.RGB2HDR, +] +RGB_NAMES = [c.name for c in RGB_CONVERSIONS] + + +@pytest.mark.parametrize("conversion", RGB_CONVERSIONS, ids=RGB_NAMES) +def test_black_image(black: RGBArray, conversion: ColorConversion) -> None: + c0, c1, c2 = convert_color(black, conversion) + + rgb2stain = np.stack(conversion.value[0]) + expected_values = np.sum(rgb2stain, axis=0) + + assert np.all(c0 == expected_values[0]) + assert np.all(c1 == expected_values[1]) + assert np.all(c2 == expected_values[2]) + + +@pytest.mark.parametrize("conversion", RGB_CONVERSIONS, ids=RGB_NAMES) +def test_white_image(white: RGBArray, conversion: ColorConversion) -> None: + c0, c1, c2 = convert_color(white, conversion) + + assert np.all(c0 == 0) + assert np.all(c1 == 0) + assert np.all(c2 == 0) + + +def test_real_sample1_image( + sample1_original: RGBArray, + sample1_hematoxylin: StainArray, + sample1_eosin: StainArray, + sample1_residual: StainArray, +) -> None: + h, e, r = convert_color(sample1_original, ColorConversion.RGB2HER) + + assert structural_similarity(_prepare(h), sample1_hematoxylin) > 0.95 + assert structural_similarity(_prepare(e), sample1_eosin) > 0.95 + assert structural_similarity(_prepare(r), sample1_residual) > 0.95 + + +def _prepare(c: NDArray[np.float64]) -> NDArray[np.uint8]: + return (255 * (c / np.max(c))).astype(np.uint8) From 4b5b907953251107cb5cea3ff66a8aedd244a1e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Tue, 27 Aug 2024 18:01:12 +0200 Subject: [PATCH 22/85] feat: convert_color with sequence --- .pre-commit-config.yaml | 2 +- rationai/staining/convert_color.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 83fa055..ba49995 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: args: [] # optional: list of Conventional Commits types to allow e.g. [feat, fix, ci, chore, test] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.1 + rev: v0.6.2 hooks: # Run the linter. - id: ruff diff --git a/rationai/staining/convert_color.py b/rationai/staining/convert_color.py index 5ae45da..acb6da2 100644 --- a/rationai/staining/convert_color.py +++ b/rationai/staining/convert_color.py @@ -1,3 +1,4 @@ +from collections.abc import Sequence from enum import Enum, auto, unique from typing import overload @@ -96,11 +97,14 @@ def convert_color( @overload -def convert_color(tile: StainArray, conversion: ColorConversion) -> RGBArray: ... +def convert_color( + tile: StainArray | Sequence[StainArray], conversion: ColorConversion +) -> RGBArray: ... def convert_color( - tile: RGBArray | StainArray | Image, conversion: ColorConversion + tile: RGBArray | Image | StainArray | Sequence[StainArray], + conversion: ColorConversion, ) -> RGBArray | tuple[StainArray, ...]: """Converts a tile into the specified color space. @@ -131,5 +135,7 @@ def convert_color( return tuple(np.moveaxis(separate_stains(tile, conversion.matrix), -1, 0)) case ConversionType.STAIN2RGB: + if isinstance(tile, Sequence): + tile = np.stack(tile, axis=-1) tile = np.asarray(tile, dtype=np.float64) return (255 * combine_stains(tile, conversion.matrix)).astype(np.uint8) From 3ff13a4ddd2b14dbf079514644e751bc02aea040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Tue, 27 Aug 2024 18:12:19 +0200 Subject: [PATCH 23/85] docs: reorganize --- docs/{reference => api}/color-conversion.md | 0 docs/{reference => api}/constants.md | 0 .../estimate-stain-vectors.md | 0 docs/{reference => api}/typing.md | 0 docs/{installation.md => getting-started.md} | 12 +- docs/reference/index.md | 4 - mkdocs.yml | 29 +- pdm.lock | 715 ++++++++++++++++-- pyproject.toml | 8 +- 9 files changed, 660 insertions(+), 108 deletions(-) rename docs/{reference => api}/color-conversion.md (100%) rename docs/{reference => api}/constants.md (100%) rename docs/{reference => api}/estimate-stain-vectors.md (100%) rename docs/{reference => api}/typing.md (100%) rename docs/{installation.md => getting-started.md} (88%) delete mode 100644 docs/reference/index.md diff --git a/docs/reference/color-conversion.md b/docs/api/color-conversion.md similarity index 100% rename from docs/reference/color-conversion.md rename to docs/api/color-conversion.md diff --git a/docs/reference/constants.md b/docs/api/constants.md similarity index 100% rename from docs/reference/constants.md rename to docs/api/constants.md diff --git a/docs/reference/estimate-stain-vectors.md b/docs/api/estimate-stain-vectors.md similarity index 100% rename from docs/reference/estimate-stain-vectors.md rename to docs/api/estimate-stain-vectors.md diff --git a/docs/reference/typing.md b/docs/api/typing.md similarity index 100% rename from docs/reference/typing.md rename to docs/api/typing.md diff --git a/docs/installation.md b/docs/getting-started.md similarity index 88% rename from docs/installation.md rename to docs/getting-started.md index 15a16f7..325f7c4 100644 --- a/docs/installation.md +++ b/docs/getting-started.md @@ -1,13 +1,15 @@ -# Installation +# Getting Started -=== "pip" +## Installation + +=== "pdm" ```bash - pip install git+ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git + pdm add git+ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git ``` -=== "pdm" +=== "pip" ```bash - pdm add git+ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git + pip install git+ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git ``` diff --git a/docs/reference/index.md b/docs/reference/index.md deleted file mode 100644 index 47a1bf1..0000000 --- a/docs/reference/index.md +++ /dev/null @@ -1,4 +0,0 @@ -# Reference - -Here's the reference or code API, the classes, functions, parameters, attributes, and -all the `rationai-staining` parts you can use in your applications. \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 7df5f07..59419fd 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,15 +1,13 @@ site_name: Staining nav: - - Home: index.md - - Getting Started: - - Installation: installation.md - - Reference: - - reference/index.md - - reference/color-conversion.md - - reference/estimate-stain-vectors.md - - reference/constants.md - - reference/typing.md + - Introduction: index.md + - Getting Started: getting-started.md + - API Reference: + - api/color-conversion.md + - estimate_stain_vectors: api/estimate-stain-vectors.md + - api/constants.md + - api/typing.md theme: name: material @@ -19,11 +17,11 @@ theme: - content.action.view - content.code.annotate - content.code.copy + - content.code.select - content.tooltips - navigation.indexes - navigation.sections - navigation.top - - navigation.tabs - navigation.footer - navigation.tracking - search.highlight @@ -52,7 +50,11 @@ theme: plugins: - search - - mkdocstrings + - mkdocstrings: + handlers: + python: + options: + show_if_no_docstring: true markdown_extensions: - pymdownx.superfences: @@ -66,3 +68,8 @@ markdown_extensions: slugify: !!python/object/apply:pymdownx.slugs.slugify kwds: case: lower + - admonition + - pymdownx.details + - pymdownx.superfences + - pymdownx.highlight + - pymdownx.inlinehilite diff --git a/pdm.lock b/pdm.lock index e698408..6420616 100644 --- a/pdm.lock +++ b/pdm.lock @@ -2,14 +2,39 @@ # It is not intended for manual editing. [metadata] -groups = ["default", "dev", "test"] +groups = ["default", "dev", "docs", "test"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:a7dd9cc2a5c546a0337b5d1b9ab80d4197f0e484eeb9bbd3c3dcad0709af7758" +content_hash = "sha256:3f01d03ad0d47f602c987ef86d96b65b42cd6b8199430d0f41138b17f05995fc" [[metadata.targets]] requires_python = ">=3.11" +[[package]] +name = "babel" +version = "2.16.0" +requires_python = ">=3.8" +summary = "Internationalization utilities" +groups = ["docs"] +dependencies = [ + "pytz>=2015.7; python_version < \"3.9\"", +] +files = [ + {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, + {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, +] + +[[package]] +name = "certifi" +version = "2024.7.4" +requires_python = ">=3.6" +summary = "Python package for providing Mozilla's CA Bundle." +groups = ["docs"] +files = [ + {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, + {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, +] + [[package]] name = "cfgv" version = "3.4.0" @@ -21,13 +46,68 @@ files = [ {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, ] +[[package]] +name = "charset-normalizer" +version = "3.3.2" +requires_python = ">=3.7.0" +summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +groups = ["docs"] +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +requires_python = ">=3.7" +summary = "Composable command line interface toolkit" +groups = ["docs"] +dependencies = [ + "colorama; platform_system == \"Windows\"", + "importlib-metadata; python_version < \"3.8\"", +] +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + [[package]] name = "colorama" version = "0.4.6" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" summary = "Cross-platform colored terminal text." -groups = ["test"] -marker = "sys_platform == \"win32\"" +groups = ["docs", "test"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -159,6 +239,34 @@ files = [ {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, ] +[[package]] +name = "ghp-import" +version = "2.1.0" +summary = "Copy your docs directly to the gh-pages branch." +groups = ["docs"] +dependencies = [ + "python-dateutil>=2.8.1", +] +files = [ + {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, + {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, +] + +[[package]] +name = "griffe" +version = "1.2.0" +requires_python = ">=3.8" +summary = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." +groups = ["docs"] +dependencies = [ + "astunparse>=1.6; python_version < \"3.9\"", + "colorama>=0.4", +] +files = [ + {file = "griffe-1.2.0-py3-none-any.whl", hash = "sha256:a8b2fcb1ecdc5a412e646b0b4375eb20a5d2eac3a11dd8c10c56967a4097663c"}, + {file = "griffe-1.2.0.tar.gz", hash = "sha256:1c9f6ef7455930f3f9b0c4145a961c90385d1e2cbc496f7796fbff560ec60d31"}, +] + [[package]] name = "identify" version = "2.6.0" @@ -170,19 +278,30 @@ files = [ {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, ] +[[package]] +name = "idna" +version = "3.8" +requires_python = ">=3.6" +summary = "Internationalized Domain Names in Applications (IDNA)" +groups = ["docs"] +files = [ + {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, + {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, +] + [[package]] name = "imageio" -version = "2.35.0" +version = "2.35.1" requires_python = ">=3.8" summary = "Library for reading and writing a wide range of image, video, scientific, and volumetric data formats." groups = ["default"] dependencies = [ - "numpy<2.0.0", + "numpy", "pillow>=8.3.2", ] files = [ - {file = "imageio-2.35.0-py3-none-any.whl", hash = "sha256:ebfb39c0ad7acf93750d7ee151664238aaa9208f202288ea6f3ca7cb0ed719c3"}, - {file = "imageio-2.35.0.tar.gz", hash = "sha256:e88f759803d2968e19a55fa8372e9fbe12967a8598fe042d8719ca46d03f4256"}, + {file = "imageio-2.35.1-py3-none-any.whl", hash = "sha256:6eb2e5244e7a16b85c10b5c2fe0f7bf961b40fcb9f1a9fd1bd1d2c2f8fb3cd65"}, + {file = "imageio-2.35.1.tar.gz", hash = "sha256:4952dfeef3c3947957f6d5dedb1f4ca31c6e509a476891062396834048aeed2a"}, ] [[package]] @@ -196,6 +315,20 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "jinja2" +version = "3.1.4" +requires_python = ">=3.7" +summary = "A very fast and expressive template engine." +groups = ["docs"] +dependencies = [ + "MarkupSafe>=2.0", +] +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + [[package]] name = "lazy-loader" version = "0.4" @@ -211,9 +344,213 @@ files = [ {file = "lazy_loader-0.4.tar.gz", hash = "sha256:47c75182589b91a4e1a85a136c074285a5ad4d9f39c63e0d7fb76391c4574cd1"}, ] +[[package]] +name = "markdown" +version = "3.7" +requires_python = ">=3.8" +summary = "Python implementation of John Gruber's Markdown." +groups = ["docs"] +dependencies = [ + "importlib-metadata>=4.4; python_version < \"3.10\"", +] +files = [ + {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, + {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, +] + +[[package]] +name = "markupsafe" +version = "2.1.5" +requires_python = ">=3.7" +summary = "Safely add untrusted strings to HTML/XML markup." +groups = ["docs"] +files = [ + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "mergedeep" +version = "1.3.4" +requires_python = ">=3.6" +summary = "A deep merge function for 🐍." +groups = ["docs"] +files = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] + +[[package]] +name = "mkdocs" +version = "1.6.0" +requires_python = ">=3.8" +summary = "Project documentation with Markdown." +groups = ["docs"] +dependencies = [ + "click>=7.0", + "colorama>=0.4; platform_system == \"Windows\"", + "ghp-import>=1.0", + "importlib-metadata>=4.4; python_version < \"3.10\"", + "jinja2>=2.11.1", + "markdown>=3.3.6", + "markupsafe>=2.0.1", + "mergedeep>=1.3.4", + "mkdocs-get-deps>=0.2.0", + "packaging>=20.5", + "pathspec>=0.11.1", + "pyyaml-env-tag>=0.1", + "pyyaml>=5.1", + "watchdog>=2.0", +] +files = [ + {file = "mkdocs-1.6.0-py3-none-any.whl", hash = "sha256:1eb5cb7676b7d89323e62b56235010216319217d4af5ddc543a91beb8d125ea7"}, + {file = "mkdocs-1.6.0.tar.gz", hash = "sha256:a73f735824ef83a4f3bcb7a231dcab23f5a838f88b7efc54a0eef5fbdbc3c512"}, +] + +[[package]] +name = "mkdocs-autorefs" +version = "1.1.0" +requires_python = ">=3.8" +summary = "Automatically link across pages in MkDocs." +groups = ["docs"] +dependencies = [ + "Markdown>=3.3", + "markupsafe>=2.0.1", + "mkdocs>=1.1", +] +files = [ + {file = "mkdocs_autorefs-1.1.0-py3-none-any.whl", hash = "sha256:492ac42f50214e81565e968f8cb0df9aba9d981542b9e7121b8f8ae9407fe6eb"}, + {file = "mkdocs_autorefs-1.1.0.tar.gz", hash = "sha256:f2fd43b11f66284bd014f9b542a05c8ecbfaad4e0d7b30b68584788217b6c656"}, +] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.0" +requires_python = ">=3.8" +summary = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" +groups = ["docs"] +dependencies = [ + "importlib-metadata>=4.3; python_version < \"3.10\"", + "mergedeep>=1.3.4", + "platformdirs>=2.2.0", + "pyyaml>=5.1", +] +files = [ + {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, + {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, +] + +[[package]] +name = "mkdocs-material" +version = "9.5.33" +requires_python = ">=3.8" +summary = "Documentation that simply works" +groups = ["docs"] +dependencies = [ + "babel~=2.10", + "colorama~=0.4", + "jinja2~=3.0", + "markdown~=3.2", + "mkdocs-material-extensions~=1.3", + "mkdocs~=1.6", + "paginate~=0.5", + "pygments~=2.16", + "pymdown-extensions~=10.2", + "regex>=2022.4", + "requests~=2.26", +] +files = [ + {file = "mkdocs_material-9.5.33-py3-none-any.whl", hash = "sha256:dbc79cf0fdc6e2c366aa987de8b0c9d4e2bb9f156e7466786ba2fd0f9bf7ffca"}, + {file = "mkdocs_material-9.5.33.tar.gz", hash = "sha256:d23a8b5e3243c9b2f29cdfe83051104a8024b767312dc8fde05ebe91ad55d89d"}, +] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +requires_python = ">=3.8" +summary = "Extension pack for Python Markdown and MkDocs Material." +groups = ["docs"] +files = [ + {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, + {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, +] + +[[package]] +name = "mkdocstrings" +version = "0.25.2" +requires_python = ">=3.8" +summary = "Automatic documentation from sources, for MkDocs." +groups = ["docs"] +dependencies = [ + "Jinja2>=2.11.1", + "Markdown>=3.3", + "MarkupSafe>=1.1", + "click>=7.0", + "importlib-metadata>=4.6; python_version < \"3.10\"", + "mkdocs-autorefs>=0.3.1", + "mkdocs>=1.4", + "platformdirs>=2.2.0", + "pymdown-extensions>=6.3", + "typing-extensions>=4.1; python_version < \"3.10\"", +] +files = [ + {file = "mkdocstrings-0.25.2-py3-none-any.whl", hash = "sha256:9e2cda5e2e12db8bb98d21e3410f3f27f8faab685a24b03b06ba7daa5b92abfc"}, + {file = "mkdocstrings-0.25.2.tar.gz", hash = "sha256:5cf57ad7f61e8be3111a2458b4e49c2029c9cb35525393b179f9c916ca8042dc"}, +] + +[[package]] +name = "mkdocstrings-python" +version = "1.10.8" +requires_python = ">=3.8" +summary = "A Python handler for mkdocstrings." +groups = ["docs"] +dependencies = [ + "griffe>=0.49", + "mkdocstrings>=0.25", +] +files = [ + {file = "mkdocstrings_python-1.10.8-py3-none-any.whl", hash = "sha256:bb12e76c8b071686617f824029cb1dfe0e9afe89f27fb3ad9a27f95f054dcd89"}, + {file = "mkdocstrings_python-1.10.8.tar.gz", hash = "sha256:5856a59cbebbb8deb133224a540de1ff60bded25e54d8beacc375bb133d39016"}, +] + +[[package]] +name = "mkdocstrings" +version = "0.25.2" +extras = ["python"] +requires_python = ">=3.8" +summary = "Automatic documentation from sources, for MkDocs." +groups = ["docs"] +dependencies = [ + "mkdocstrings-python>=0.5.2", + "mkdocstrings==0.25.2", +] +files = [ + {file = "mkdocstrings-0.25.2-py3-none-any.whl", hash = "sha256:9e2cda5e2e12db8bb98d21e3410f3f27f8faab685a24b03b06ba7daa5b92abfc"}, + {file = "mkdocstrings-0.25.2.tar.gz", hash = "sha256:5cf57ad7f61e8be3111a2458b4e49c2029c9cb35525393b179f9c916ca8042dc"}, +] + [[package]] name = "mypy" -version = "1.11.1" +version = "1.11.2" requires_python = ">=3.8" summary = "Optional static typing for Python" groups = ["dev"] @@ -223,18 +560,18 @@ dependencies = [ "typing-extensions>=4.6.0", ] files = [ - {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, - {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, - {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, - {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, - {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, - {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, - {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, - {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, - {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, + {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, + {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, + {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, + {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, + {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, + {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, + {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, + {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, ] [[package]] @@ -272,28 +609,49 @@ files = [ [[package]] name = "numpy" -version = "1.26.4" -requires_python = ">=3.9" +version = "2.1.0" +requires_python = ">=3.10" summary = "Fundamental package for array computing in Python" groups = ["default"] files = [ - {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, - {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, - {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, - {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, - {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, - {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, + {file = "numpy-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:76368c788ccb4f4782cf9c842b316140142b4cbf22ff8db82724e82fe1205dce"}, + {file = "numpy-2.1.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f8e93a01a35be08d31ae33021e5268f157a2d60ebd643cfc15de6ab8e4722eb1"}, + {file = "numpy-2.1.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:9523f8b46485db6939bd069b28b642fec86c30909cea90ef550373787f79530e"}, + {file = "numpy-2.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54139e0eb219f52f60656d163cbe67c31ede51d13236c950145473504fa208cb"}, + {file = "numpy-2.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5ebbf9fbdabed208d4ecd2e1dfd2c0741af2f876e7ae522c2537d404ca895c3"}, + {file = "numpy-2.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:378cb4f24c7d93066ee4103204f73ed046eb88f9ad5bb2275bb9fa0f6a02bd36"}, + {file = "numpy-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8f699a709120b220dfe173f79c73cb2a2cab2c0b88dd59d7b49407d032b8ebd"}, + {file = "numpy-2.1.0-cp311-cp311-win32.whl", hash = "sha256:ffbd6faeb190aaf2b5e9024bac9622d2ee549b7ec89ef3a9373fa35313d44e0e"}, + {file = "numpy-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0af3a5987f59d9c529c022c8c2a64805b339b7ef506509fba7d0556649b9714b"}, + {file = "numpy-2.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fe76d75b345dc045acdbc006adcb197cc680754afd6c259de60d358d60c93736"}, + {file = "numpy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f358ea9e47eb3c2d6eba121ab512dfff38a88db719c38d1e67349af210bc7529"}, + {file = "numpy-2.1.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:dd94ce596bda40a9618324547cfaaf6650b1a24f5390350142499aa4e34e53d1"}, + {file = "numpy-2.1.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b47c551c6724960479cefd7353656498b86e7232429e3a41ab83be4da1b109e8"}, + {file = "numpy-2.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0756a179afa766ad7cb6f036de622e8a8f16ffdd55aa31f296c870b5679d745"}, + {file = "numpy-2.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24003ba8ff22ea29a8c306e61d316ac74111cebf942afbf692df65509a05f111"}, + {file = "numpy-2.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b34fa5e3b5d6dc7e0a4243fa0f81367027cb6f4a7215a17852979634b5544ee0"}, + {file = "numpy-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c4f982715e65036c34897eb598d64aef15150c447be2cfc6643ec7a11af06574"}, + {file = "numpy-2.1.0-cp312-cp312-win32.whl", hash = "sha256:c4cd94dfefbefec3f8b544f61286584292d740e6e9d4677769bc76b8f41deb02"}, + {file = "numpy-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0cdef204199278f5c461a0bed6ed2e052998276e6d8ab2963d5b5c39a0500bc"}, + {file = "numpy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8ab81ccd753859ab89e67199b9da62c543850f819993761c1e94a75a814ed667"}, + {file = "numpy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:442596f01913656d579309edcd179a2a2f9977d9a14ff41d042475280fc7f34e"}, + {file = "numpy-2.1.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:848c6b5cad9898e4b9ef251b6f934fa34630371f2e916261070a4eb9092ffd33"}, + {file = "numpy-2.1.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:54c6a63e9d81efe64bfb7bcb0ec64332a87d0b87575f6009c8ba67ea6374770b"}, + {file = "numpy-2.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:652e92fc409e278abdd61e9505649e3938f6d04ce7ef1953f2ec598a50e7c195"}, + {file = "numpy-2.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ab32eb9170bf8ffcbb14f11613f4a0b108d3ffee0832457c5d4808233ba8977"}, + {file = "numpy-2.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8fb49a0ba4d8f41198ae2d52118b050fd34dace4b8f3fb0ee34e23eb4ae775b1"}, + {file = "numpy-2.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44e44973262dc3ae79e9063a1284a73e09d01b894b534a769732ccd46c28cc62"}, + {file = "numpy-2.1.0-cp313-cp313-win32.whl", hash = "sha256:ab83adc099ec62e044b1fbb3a05499fa1e99f6d53a1dde102b2d85eff66ed324"}, + {file = "numpy-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:de844aaa4815b78f6023832590d77da0e3b6805c644c33ce94a1e449f16d6ab5"}, + {file = "numpy-2.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:343e3e152bf5a087511cd325e3b7ecfd5b92d369e80e74c12cd87826e263ec06"}, + {file = "numpy-2.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f07fa2f15dabe91259828ce7d71b5ca9e2eb7c8c26baa822c825ce43552f4883"}, + {file = "numpy-2.1.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5474dad8c86ee9ba9bb776f4b99ef2d41b3b8f4e0d199d4f7304728ed34d0300"}, + {file = "numpy-2.1.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:1f817c71683fd1bb5cff1529a1d085a57f02ccd2ebc5cd2c566f9a01118e3b7d"}, + {file = "numpy-2.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a3336fbfa0d38d3deacd3fe7f3d07e13597f29c13abf4d15c3b6dc2291cbbdd"}, + {file = "numpy-2.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a894c51fd8c4e834f00ac742abad73fc485df1062f1b875661a3c1e1fb1c2f6"}, + {file = "numpy-2.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:9156ca1f79fc4acc226696e95bfcc2b486f165a6a59ebe22b2c1f82ab190384a"}, + {file = "numpy-2.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:624884b572dff8ca8f60fab591413f077471de64e376b17d291b19f56504b2bb"}, + {file = "numpy-2.1.0.tar.gz", hash = "sha256:7dc90da0081f7e1da49ec4e398ede6a8e9cc4f5ebe5f9e06b443ed889ee9aaa2"}, ] [[package]] @@ -301,12 +659,33 @@ name = "packaging" version = "24.1" requires_python = ">=3.8" summary = "Core utilities for Python packages" -groups = ["default", "test"] +groups = ["default", "docs", "test"] files = [ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] +[[package]] +name = "paginate" +version = "0.5.7" +summary = "Divides large result sets into pages for easier browsing" +groups = ["docs"] +files = [ + {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, + {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +requires_python = ">=3.8" +summary = "Utility library for gitignore style pattern matching of file paths." +groups = ["docs"] +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + [[package]] name = "perlin-noise" version = "1.13" @@ -365,7 +744,7 @@ name = "platformdirs" version = "4.2.2" requires_python = ">=3.8" summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -groups = ["dev"] +groups = ["dev", "docs"] files = [ {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, @@ -400,6 +779,32 @@ files = [ {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, ] +[[package]] +name = "pygments" +version = "2.18.0" +requires_python = ">=3.8" +summary = "Pygments is a syntax highlighting package written in Python." +groups = ["docs"] +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[[package]] +name = "pymdown-extensions" +version = "10.9" +requires_python = ">=3.8" +summary = "Extension pack for Python Markdown." +groups = ["docs"] +dependencies = [ + "markdown>=3.6", + "pyyaml", +] +files = [ + {file = "pymdown_extensions-10.9-py3-none-any.whl", hash = "sha256:d323f7e90d83c86113ee78f3fe62fc9dee5f56b54d912660703ea1816fed5626"}, + {file = "pymdown_extensions-10.9.tar.gz", hash = "sha256:6ff740bcd99ec4172a938970d42b96128bdc9d4b9bcad72494f29921dc69b753"}, +] + [[package]] name = "pytest" version = "8.3.2" @@ -434,12 +839,26 @@ files = [ {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, ] +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +summary = "Extensions to the standard Python datetime module" +groups = ["docs"] +dependencies = [ + "six>=1.5", +] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + [[package]] name = "pyyaml" version = "6.0.2" requires_python = ">=3.8" summary = "YAML parser and emitter for Python" -groups = ["dev"] +groups = ["dev", "docs"] files = [ {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, @@ -471,31 +890,102 @@ files = [ {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +requires_python = ">=3.6" +summary = "A custom YAML tag for referencing environment variables in YAML files. " +groups = ["docs"] +dependencies = [ + "pyyaml", +] +files = [ + {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, + {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, +] + +[[package]] +name = "regex" +version = "2024.7.24" +requires_python = ">=3.8" +summary = "Alternative regular expression module, to replace re." +groups = ["docs"] +files = [ + {file = "regex-2024.7.24-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:382281306e3adaaa7b8b9ebbb3ffb43358a7bbf585fa93821300a418bb975281"}, + {file = "regex-2024.7.24-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4fdd1384619f406ad9037fe6b6eaa3de2749e2e12084abc80169e8e075377d3b"}, + {file = "regex-2024.7.24-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3d974d24edb231446f708c455fd08f94c41c1ff4f04bcf06e5f36df5ef50b95a"}, + {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2ec4419a3fe6cf8a4795752596dfe0adb4aea40d3683a132bae9c30b81e8d73"}, + {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb563dd3aea54c797adf513eeec819c4213d7dbfc311874eb4fd28d10f2ff0f2"}, + {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:45104baae8b9f67569f0f1dca5e1f1ed77a54ae1cd8b0b07aba89272710db61e"}, + {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:994448ee01864501912abf2bad9203bffc34158e80fe8bfb5b031f4f8e16da51"}, + {file = "regex-2024.7.24-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fac296f99283ac232d8125be932c5cd7644084a30748fda013028c815ba3364"}, + {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7e37e809b9303ec3a179085415cb5f418ecf65ec98cdfe34f6a078b46ef823ee"}, + {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:01b689e887f612610c869421241e075c02f2e3d1ae93a037cb14f88ab6a8934c"}, + {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f6442f0f0ff81775eaa5b05af8a0ffa1dda36e9cf6ec1e0d3d245e8564b684ce"}, + {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:871e3ab2838fbcb4e0865a6e01233975df3a15e6fce93b6f99d75cacbd9862d1"}, + {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c918b7a1e26b4ab40409820ddccc5d49871a82329640f5005f73572d5eaa9b5e"}, + {file = "regex-2024.7.24-cp311-cp311-win32.whl", hash = "sha256:2dfbb8baf8ba2c2b9aa2807f44ed272f0913eeeba002478c4577b8d29cde215c"}, + {file = "regex-2024.7.24-cp311-cp311-win_amd64.whl", hash = "sha256:538d30cd96ed7d1416d3956f94d54e426a8daf7c14527f6e0d6d425fcb4cca52"}, + {file = "regex-2024.7.24-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fe4ebef608553aff8deb845c7f4f1d0740ff76fa672c011cc0bacb2a00fbde86"}, + {file = "regex-2024.7.24-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:74007a5b25b7a678459f06559504f1eec2f0f17bca218c9d56f6a0a12bfffdad"}, + {file = "regex-2024.7.24-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7df9ea48641da022c2a3c9c641650cd09f0cd15e8908bf931ad538f5ca7919c9"}, + {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a1141a1dcc32904c47f6846b040275c6e5de0bf73f17d7a409035d55b76f289"}, + {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80c811cfcb5c331237d9bad3bea2c391114588cf4131707e84d9493064d267f9"}, + {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7214477bf9bd195894cf24005b1e7b496f46833337b5dedb7b2a6e33f66d962c"}, + {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d55588cba7553f0b6ec33130bc3e114b355570b45785cebdc9daed8c637dd440"}, + {file = "regex-2024.7.24-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:558a57cfc32adcf19d3f791f62b5ff564922942e389e3cfdb538a23d65a6b610"}, + {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a512eed9dfd4117110b1881ba9a59b31433caed0c4101b361f768e7bcbaf93c5"}, + {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:86b17ba823ea76256b1885652e3a141a99a5c4422f4a869189db328321b73799"}, + {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5eefee9bfe23f6df09ffb6dfb23809f4d74a78acef004aa904dc7c88b9944b05"}, + {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:731fcd76bbdbf225e2eb85b7c38da9633ad3073822f5ab32379381e8c3c12e94"}, + {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eaef80eac3b4cfbdd6de53c6e108b4c534c21ae055d1dbea2de6b3b8ff3def38"}, + {file = "regex-2024.7.24-cp312-cp312-win32.whl", hash = "sha256:185e029368d6f89f36e526764cf12bf8d6f0e3a2a7737da625a76f594bdfcbfc"}, + {file = "regex-2024.7.24-cp312-cp312-win_amd64.whl", hash = "sha256:2f1baff13cc2521bea83ab2528e7a80cbe0ebb2c6f0bfad15be7da3aed443908"}, + {file = "regex-2024.7.24.tar.gz", hash = "sha256:9cfd009eed1a46b27c14039ad5bbc5e71b6367c5b2e6d5f5da0ea91600817506"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +requires_python = ">=3.8" +summary = "Python HTTP for Humans." +groups = ["docs"] +dependencies = [ + "certifi>=2017.4.17", + "charset-normalizer<4,>=2", + "idna<4,>=2.5", + "urllib3<3,>=1.21.1", +] +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + [[package]] name = "ruff" -version = "0.6.1" +version = "0.6.2" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["dev"] files = [ - {file = "ruff-0.6.1-py3-none-linux_armv6l.whl", hash = "sha256:b4bb7de6a24169dc023f992718a9417380301b0c2da0fe85919f47264fb8add9"}, - {file = "ruff-0.6.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:45efaae53b360c81043e311cdec8a7696420b3d3e8935202c2846e7a97d4edae"}, - {file = "ruff-0.6.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:bc60c7d71b732c8fa73cf995efc0c836a2fd8b9810e115be8babb24ae87e0850"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c7477c3b9da822e2db0b4e0b59e61b8a23e87886e727b327e7dcaf06213c5cf"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a0af7ab3f86e3dc9f157a928e08e26c4b40707d0612b01cd577cc84b8905cc9"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:392688dbb50fecf1bf7126731c90c11a9df1c3a4cdc3f481b53e851da5634fa5"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5278d3e095ccc8c30430bcc9bc550f778790acc211865520f3041910a28d0024"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe6d5f65d6f276ee7a0fc50a0cecaccb362d30ef98a110f99cac1c7872df2f18"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2e0dd11e2ae553ee5c92a81731d88a9883af8db7408db47fc81887c1f8b672e"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d812615525a34ecfc07fd93f906ef5b93656be01dfae9a819e31caa6cfe758a1"}, - {file = "ruff-0.6.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:faaa4060f4064c3b7aaaa27328080c932fa142786f8142aff095b42b6a2eb631"}, - {file = "ruff-0.6.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:99d7ae0df47c62729d58765c593ea54c2546d5de213f2af2a19442d50a10cec9"}, - {file = "ruff-0.6.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9eb18dfd7b613eec000e3738b3f0e4398bf0153cb80bfa3e351b3c1c2f6d7b15"}, - {file = "ruff-0.6.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c62bc04c6723a81e25e71715aa59489f15034d69bf641df88cb38bdc32fd1dbb"}, - {file = "ruff-0.6.1-py3-none-win32.whl", hash = "sha256:9fb4c4e8b83f19c9477a8745e56d2eeef07a7ff50b68a6998f7d9e2e3887bdc4"}, - {file = "ruff-0.6.1-py3-none-win_amd64.whl", hash = "sha256:c2ebfc8f51ef4aca05dad4552bbcf6fe8d1f75b2f6af546cc47cc1c1ca916b5b"}, - {file = "ruff-0.6.1-py3-none-win_arm64.whl", hash = "sha256:3bc81074971b0ffad1bd0c52284b22411f02a11a012082a76ac6da153536e014"}, - {file = "ruff-0.6.1.tar.gz", hash = "sha256:af3ffd8c6563acb8848d33cd19a69b9bfe943667f0419ca083f8ebe4224a3436"}, + {file = "ruff-0.6.2-py3-none-linux_armv6l.whl", hash = "sha256:5c8cbc6252deb3ea840ad6a20b0f8583caab0c5ef4f9cca21adc5a92b8f79f3c"}, + {file = "ruff-0.6.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:17002fe241e76544448a8e1e6118abecbe8cd10cf68fde635dad480dba594570"}, + {file = "ruff-0.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3dbeac76ed13456f8158b8f4fe087bf87882e645c8e8b606dd17b0b66c2c1158"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:094600ee88cda325988d3f54e3588c46de5c18dae09d683ace278b11f9d4d534"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:316d418fe258c036ba05fbf7dfc1f7d3d4096db63431546163b472285668132b"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d72b8b3abf8a2d51b7b9944a41307d2f442558ccb3859bbd87e6ae9be1694a5d"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2aed7e243be68487aa8982e91c6e260982d00da3f38955873aecd5a9204b1d66"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d371f7fc9cec83497fe7cf5eaf5b76e22a8efce463de5f775a1826197feb9df8"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f310d63af08f583363dfb844ba8f9417b558199c58a5999215082036d795a1"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7db6880c53c56addb8638fe444818183385ec85eeada1d48fc5abe045301b2f1"}, + {file = "ruff-0.6.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1175d39faadd9a50718f478d23bfc1d4da5743f1ab56af81a2b6caf0a2394f23"}, + {file = "ruff-0.6.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b939f9c86d51635fe486585389f54582f0d65b8238e08c327c1534844b3bb9a"}, + {file = "ruff-0.6.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d0d62ca91219f906caf9b187dea50d17353f15ec9bb15aae4a606cd697b49b4c"}, + {file = "ruff-0.6.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7438a7288f9d67ed3c8ce4d059e67f7ed65e9fe3aa2ab6f5b4b3610e57e3cb56"}, + {file = "ruff-0.6.2-py3-none-win32.whl", hash = "sha256:279d5f7d86696df5f9549b56b9b6a7f6c72961b619022b5b7999b15db392a4da"}, + {file = "ruff-0.6.2-py3-none-win_amd64.whl", hash = "sha256:d9f3469c7dd43cd22eb1c3fc16926fb8258d50cb1b216658a07be95dd117b0f2"}, + {file = "ruff-0.6.2-py3-none-win_arm64.whl", hash = "sha256:f28fcd2cd0e02bdf739297516d5643a945cc7caf09bd9bcb4d932540a5ea4fa9"}, + {file = "ruff-0.6.2.tar.gz", hash = "sha256:239ee6beb9e91feb8e0ec384204a763f36cb53fb895a1a364618c6abb076b3be"}, ] [[package]] @@ -530,7 +1020,7 @@ files = [ [[package]] name = "scipy" -version = "1.14.0" +version = "1.14.1" requires_python = ">=3.10" summary = "Fundamental algorithms for scientific computing in Python" groups = ["default"] @@ -538,28 +1028,47 @@ dependencies = [ "numpy<2.3,>=1.23.5", ] files = [ - {file = "scipy-1.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6d056a8709ccda6cf36cdd2eac597d13bc03dba38360f418560a93050c76a16e"}, - {file = "scipy-1.14.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f0a50da861a7ec4573b7c716b2ebdcdf142b66b756a0d392c236ae568b3a93fb"}, - {file = "scipy-1.14.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:94c164a9e2498e68308e6e148646e486d979f7fcdb8b4cf34b5441894bdb9caf"}, - {file = "scipy-1.14.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a7d46c3e0aea5c064e734c3eac5cf9eb1f8c4ceee756262f2c7327c4c2691c86"}, - {file = "scipy-1.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9eee2989868e274aae26125345584254d97c56194c072ed96cb433f32f692ed8"}, - {file = "scipy-1.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3154691b9f7ed73778d746da2df67a19d046a6c8087c8b385bc4cdb2cfca74"}, - {file = "scipy-1.14.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c40003d880f39c11c1edbae8144e3813904b10514cd3d3d00c277ae996488cdb"}, - {file = "scipy-1.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:5b083c8940028bb7e0b4172acafda6df762da1927b9091f9611b0bcd8676f2bc"}, - {file = "scipy-1.14.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bff2438ea1330e06e53c424893ec0072640dac00f29c6a43a575cbae4c99b2b9"}, - {file = "scipy-1.14.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:bbc0471b5f22c11c389075d091d3885693fd3f5e9a54ce051b46308bc787e5d4"}, - {file = "scipy-1.14.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:64b2ff514a98cf2bb734a9f90d32dc89dc6ad4a4a36a312cd0d6327170339eb0"}, - {file = "scipy-1.14.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:7d3da42fbbbb860211a811782504f38ae7aaec9de8764a9bef6b262de7a2b50f"}, - {file = "scipy-1.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d91db2c41dd6c20646af280355d41dfa1ec7eead235642178bd57635a3f82209"}, - {file = "scipy-1.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a01cc03bcdc777c9da3cfdcc74b5a75caffb48a6c39c8450a9a05f82c4250a14"}, - {file = "scipy-1.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:65df4da3c12a2bb9ad52b86b4dcf46813e869afb006e58be0f516bc370165159"}, - {file = "scipy-1.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:4c4161597c75043f7154238ef419c29a64ac4a7c889d588ea77690ac4d0d9b20"}, - {file = "scipy-1.14.0.tar.gz", hash = "sha256:b5923f48cb840380f9854339176ef21763118a7300a88203ccd0bdd26e58527b"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8"}, + {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37"}, + {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2"}, + {file = "scipy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2"}, + {file = "scipy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc"}, + {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310"}, + {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066"}, + {file = "scipy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1"}, + {file = "scipy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e"}, + {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d"}, + {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e"}, + {file = "scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06"}, + {file = "scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84"}, + {file = "scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417"}, +] + +[[package]] +name = "six" +version = "1.16.0" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +summary = "Python 2 and 3 compatibility utilities" +groups = ["docs"] +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] [[package]] name = "tifffile" -version = "2024.8.10" +version = "2024.8.24" requires_python = ">=3.9" summary = "Read and write TIFF files" groups = ["default"] @@ -567,8 +1076,8 @@ dependencies = [ "numpy", ] files = [ - {file = "tifffile-2024.8.10-py3-none-any.whl", hash = "sha256:1c224564fa92e7e9f9a0ed65880b2ece97c3f0d10029ffbebfa5e62b3f6b343d"}, - {file = "tifffile-2024.8.10.tar.gz", hash = "sha256:fdc12124f1478a07b1524641dc6b50cf6bde0483011a63fd2a773094090c3dcf"}, + {file = "tifffile-2024.8.24-py3-none-any.whl", hash = "sha256:40faba20cb0af05c0eb500eda63244dd81500360e1518ff4548eb61ce3943099"}, + {file = "tifffile-2024.8.24.tar.gz", hash = "sha256:65a77e50cfcf05e3553b52dd13367caad3f88eae7f533188653ce9b2d88b7464"}, ] [[package]] @@ -582,6 +1091,17 @@ files = [ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] +[[package]] +name = "urllib3" +version = "2.2.2" +requires_python = ">=3.8" +summary = "HTTP library with thread-safe connection pooling, file post, and more." +groups = ["docs"] +files = [ + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, +] + [[package]] name = "virtualenv" version = "20.26.3" @@ -598,3 +1118,32 @@ files = [ {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, ] + +[[package]] +name = "watchdog" +version = "5.0.0" +requires_python = ">=3.9" +summary = "Filesystem events monitoring" +groups = ["docs"] +files = [ + {file = "watchdog-5.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3c177085c3d210d1c73cb4569442bdaef706ebebc423bd7aed9e90fc12b2e553"}, + {file = "watchdog-5.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01ab36cddc836a0f202c66267daaef92ba5c17c7d6436deff0587bb61234c5c9"}, + {file = "watchdog-5.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0834c21efa3e767849b09e667274604c7cdfe30b49eb95d794565c53f4db3c1e"}, + {file = "watchdog-5.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1e26f570dd7f5178656affb24d6f0e22ce66c8daf88d4061a27bfb9ac866b40d"}, + {file = "watchdog-5.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d146331e6b206baa9f6dd40f72b5783ad2302c240df68e7fce196d30588ccf7b"}, + {file = "watchdog-5.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6c96b1706430839872a3e33b9370ee3f7a0079f6b828129d88498ad1f96a0f45"}, + {file = "watchdog-5.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:663b096368ed7831ac42259919fdb9e0a1f0a8994d972675dfbcca0225e74de1"}, + {file = "watchdog-5.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:685931412978d00a91a193d9018fc9e394e565e8e7a0c275512a80e59c6e85f8"}, + {file = "watchdog-5.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:109daafc5b0f2a98d1fa9475ff9737eb3559d57b18129a36495e20c71de0b44f"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d76efab5248aafbf8a2c2a63cd7b9545e6b346ad1397af8b862a3bb3140787d8"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:ff4e957c45c446de34c513eadce01d0b65da7eee47c01dce472dd136124552c9"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:16c1aa3377bb1f82c5e24277fcbf4e2cac3c4ce46aaaf7212d53caa9076eb7b7"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:22fcad6168fc43cf0e709bd854be5b8edbb0b260f0a6f28f1ea9baa53c6907f7"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:0120b2fa65732797ffa65fa8ee5540c288aa861d91447df298626d6385a24658"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2aa59fab7ff75281778c649557275ca3085eccbdf825a0e2a5ca3810e977afe5"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:78db0fe0336958fc0e1269545c980b6f33d04d184ba191b2800a8b71d3e971a9"}, + {file = "watchdog-5.0.0-py3-none-win32.whl", hash = "sha256:d1acef802916083f2ad7988efc7decf07e46e266916c0a09d8fb9d387288ea12"}, + {file = "watchdog-5.0.0-py3-none-win_amd64.whl", hash = "sha256:3c2d50fdb86aa6df3973313272f5a17eb26eab29ff5a0bf54b6d34597b4dc4e4"}, + {file = "watchdog-5.0.0-py3-none-win_ia64.whl", hash = "sha256:1d17ec7e022c34fa7ddc72aa41bf28c9d1207ffb193df18ba4f6fde453725b3c"}, + {file = "watchdog-5.0.0.tar.gz", hash = "sha256:990aedb9e2f336b45a70aed9c014450e7c4a70fd99c5f5b1834d57e1453a177e"}, +] diff --git a/pyproject.toml b/pyproject.toml index 2291c91..aa64ea2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,15 +6,12 @@ authors = [{ name = "Tomáš Balák" }] requires-python = ">=3.11" readme = "README.md" license = { file = "LICENSE" } -dependencies = [ - "numpy>=1.26.4", - "pillow>=10.3.0", - "scikit-image>=0.23.2", -] +dependencies = ["numpy>=1.26.4", "pillow>=10.3.0", "scikit-image>=0.23.2"] [tool.pdm.dev-dependencies] dev = ["mypy", "pre-commit", "ruff"] test = ["pytest", "pytest-cov", "perlin-noise>=1.12"] +docs = ["mkdocs-material>=9.5.32", "mkdocstrings[python]>=0.25.2"] [tool.pdm.scripts] l = { composite = ["lint", "format", "mypy"] } @@ -22,6 +19,7 @@ test = "pytest tests" lint = "ruff check --fix" format = "ruff format" mypy = "mypy ." +docs = "mkdocs serve" post_install = { composite = [ "pre-commit autoupdate", "pre-commit install", From a10335ae2956d01806d4c68319baa8a7d9e4a95d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Pek=C3=A1r?= <525077@mail.muni.cz> Date: Tue, 10 Sep 2024 00:18:34 +0200 Subject: [PATCH 24/85] fix: py.typed marker file --- rationai/staining/py.typed | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 rationai/staining/py.typed diff --git a/rationai/staining/py.typed b/rationai/staining/py.typed new file mode 100644 index 0000000..e69de29 From bd1d704d86468a42d2321e9041c746215255c099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Bal=C3=A1k?= <525126@mail.muni.cz> Date: Fri, 28 Feb 2025 08:34:27 +0100 Subject: [PATCH 25/85] feat: light H&DAB protocol, color conversion fixes, modify and reconstruct --- .pre-commit-config.yaml | 6 +- docs/api/modify-and-reconstruct.md | 1 + mkdocs.yml | 1 + rationai/staining/__init__.py | 2 + rationai/staining/constants.py | 13 +++ rationai/staining/convert_color.py | 80 +++++++++++++++--- rationai/staining/modify_and_reconstruct.py | 73 ++++++++++++++++ rationai/staining/scripts/remove_red_stain.py | 38 +++++++++ rationai/staining/typing.py | 11 +++ tests/conftest.py | 11 ++- tests/data/generated/h_dab.jpg | Bin 43338 -> 0 bytes tests/data/generated/h_dab.png | Bin 0 -> 14855 bytes tests/data/generated/h_dab_legacy.png | Bin 0 -> 14366 bytes tests/data/generated/h_e.jpg | Bin 42565 -> 0 bytes tests/data/generated/h_e.png | Bin 0 -> 14366 bytes tests/scripts/generate_artifical_tissue.py | 10 ++- tests/test_color_conversion.py | 5 +- tests/test_modify_and_reconstruct.py | 61 +++++++++++++ tests/test_stain_estimator.py | 32 +++++-- tests/test_stain_reconstruction.py | 6 +- tests/test_stain_separation.py | 1 + 21 files changed, 321 insertions(+), 30 deletions(-) create mode 100644 docs/api/modify-and-reconstruct.md create mode 100644 rationai/staining/modify_and_reconstruct.py create mode 100644 rationai/staining/scripts/remove_red_stain.py delete mode 100644 tests/data/generated/h_dab.jpg create mode 100644 tests/data/generated/h_dab.png create mode 100644 tests/data/generated/h_dab_legacy.png delete mode 100644 tests/data/generated/h_e.jpg create mode 100644 tests/data/generated/h_e.png create mode 100644 tests/test_modify_and_reconstruct.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ba49995..9bbe87c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,20 +2,20 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: check-yaml args: [--unsafe] - repo: https://github.com/compilerla/conventional-pre-commit - rev: v3.4.0 + rev: v3.6.0 hooks: - id: conventional-pre-commit stages: [commit-msg] args: [] # optional: list of Conventional Commits types to allow e.g. [feat, fix, ci, chore, test] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.2 + rev: v0.7.1 hooks: # Run the linter. - id: ruff diff --git a/docs/api/modify-and-reconstruct.md b/docs/api/modify-and-reconstruct.md new file mode 100644 index 0000000..ef9f38d --- /dev/null +++ b/docs/api/modify-and-reconstruct.md @@ -0,0 +1 @@ +::: rationai.staining.modify_and_reconstruct \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 59419fd..d3e70d5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -6,6 +6,7 @@ nav: - API Reference: - api/color-conversion.md - estimate_stain_vectors: api/estimate-stain-vectors.md + - modify_and_reconstruct: api/modify-and-reconstruct.md - api/constants.md - api/typing.md diff --git a/rationai/staining/__init__.py b/rationai/staining/__init__.py index e71fe94..12ba09e 100644 --- a/rationai/staining/__init__.py +++ b/rationai/staining/__init__.py @@ -4,6 +4,7 @@ convert_color, ) from rationai.staining.estimate_stain_vectors import estimate_stain_vectors +from rationai.staining.modify_and_reconstruct import modify_and_reconstruct __all__ = [ @@ -11,4 +12,5 @@ "ConversionType", "convert_color", "estimate_stain_vectors", + "modify_and_reconstruct", ] diff --git a/rationai/staining/constants.py b/rationai/staining/constants.py index 6e26e51..46b81eb 100644 --- a/rationai/staining/constants.py +++ b/rationai/staining/constants.py @@ -1,6 +1,19 @@ QUPATH_H = (0.651, 0.701, 0.290) """ QuPath value for default Hematoxylin staining vector. + +Note: + QuPath uses this value by default, even in the H&DAB protocol. + However, this value does not resemble the actual color + of Hematoxylin present in H&DAB-stained slides. + It is therefore advised to use the `LIGHT_H` value when working with + the H&DAB protocol. +""" + +LIGHT_H = (0.849, 0.514, 0.124) +""" +Value for a light blue Hematoxylin staining vector +present in the H&DAB protocol. """ QUPATH_E = (0.216, 0.801, 0.558) diff --git a/rationai/staining/convert_color.py b/rationai/staining/convert_color.py index acb6da2..21ac72b 100644 --- a/rationai/staining/convert_color.py +++ b/rationai/staining/convert_color.py @@ -4,9 +4,9 @@ import numpy as np from PIL.Image import Image -from skimage.color import combine_stains, separate_stains +from skimage.color import combine_stains -from rationai.staining.constants import QUPATH_DAB, QUPATH_E, QUPATH_H +from rationai.staining.constants import LIGHT_H, QUPATH_DAB, QUPATH_E, QUPATH_H from rationai.staining.typing import ( RGBArray, StainArray, @@ -42,17 +42,36 @@ class ColorConversion(Enum): """**Combines** `Hematoxylin`, `Eosin` and `Residual` channels **to RGB**.""" HDR2RGB = ( - (QUPATH_H, QUPATH_DAB, residual(QUPATH_H, QUPATH_DAB)), + (LIGHT_H, QUPATH_DAB, residual(LIGHT_H, QUPATH_DAB)), ConversionType.STAIN2RGB, ) """**Combines** `Hematoxylin`, `DAB` and `Residual` channels **to RGB**.""" - RGB2HER = (inv_mat(HER2RGB[0]), ConversionType.RGB2STAIN) - """**Separates RGB image** into `Hematoxylin`, `Eosin` and `Residual` channels.""" + HDR2RGB_LEGACY = ( + (QUPATH_H, QUPATH_DAB, residual(QUPATH_H, QUPATH_DAB)), + ConversionType.STAIN2RGB, + ) + """**Combines** `Hematoxylin`, `DAB` and `Residual` channels **to RGB**. + + This conversion uses the Hematoxylin vector from QuPath, and it is no longer + recommended and is marked as legacy as it did not + provide expected results for staining detection and separation. + """ RGB2HDR = (inv_mat(HDR2RGB[0]), ConversionType.RGB2STAIN) """**Separates RGB image** into `Hematoxylin`, `DAB` and `Residual` channels.""" + RGB2HER = (inv_mat(HER2RGB[0]), ConversionType.RGB2STAIN) + """**Separates RGB image** into `Hematoxylin`, `Eosin` and `Residual` channels.""" + + RGB2HDR_LEGACY = (inv_mat(HDR2RGB_LEGACY[0]), ConversionType.RGB2STAIN) + """**Separates RGB image** into `Hematoxylin`, `DAB` and `Residual` channels. + + This conversion uses the Hematoxylin vector from QuPath, and it is no longer + recommended and is marked as legacy as it did not + provide expected results for staining detection and separation. + """ + @property def conv_type(self) -> ConversionType: """Returns conversion type associated with the color conversion. @@ -80,31 +99,57 @@ def inverse(self) -> "ColorConversion": Inverse color conversion (i. e., RGB2HER is inverse to HER2RGB). """ match self: + # H&E Protocol case ColorConversion.RGB2HER: return ColorConversion.HER2RGB - case ColorConversion.RGB2HDR: - return ColorConversion.HDR2RGB case ColorConversion.HER2RGB: return ColorConversion.RGB2HER + + # H&DAB Protocol + case ColorConversion.RGB2HDR: + return ColorConversion.HDR2RGB case ColorConversion.HDR2RGB: return ColorConversion.RGB2HDR + # Legacy H&DAB Protocol + case ColorConversion.RGB2HDR_LEGACY: + return ColorConversion.HDR2RGB_LEGACY + case ColorConversion.HDR2RGB_LEGACY: + return ColorConversion.RGB2HDR_LEGACY + + +def _separate_stains( + img: RGBArray | Image, rgb2stain: StainArray, keep_negative_values: bool = False +) -> StainArray: + values = np.maximum(np.asarray(img).astype(np.float64) / 255, 1e-6) + stains = (np.log(values) / np.log(1e-6)) @ rgb2stain + + if not keep_negative_values: + stains = np.maximum(stains, 0) + + return stains + @overload def convert_color( - tile: RGBArray | Image, conversion: ColorConversion + tile: RGBArray | Image, + conversion: ColorConversion, + keep_negative_values: bool = False, ) -> tuple[StainArray, ...]: ... @overload def convert_color( - tile: StainArray | Sequence[StainArray], conversion: ColorConversion + tile: StainArray | Sequence[StainArray], + conversion: ColorConversion, + keep_negative_values: bool = False, ) -> RGBArray: ... def convert_color( tile: RGBArray | Image | StainArray | Sequence[StainArray], conversion: ColorConversion, + keep_negative_values: bool = False, ) -> RGBArray | tuple[StainArray, ...]: """Converts a tile into the specified color space. @@ -120,22 +165,33 @@ def convert_color( tile: Tile that should be converted. Can either be a RGB image or three stain channels stacked along the last axis. Stain channels can be stacked using the following code: - >>> stacked_stains = np.stack([c0, c1, c2], axis=-1) - + ```python + stacked_stains = np.stack([c0, c1, c2], axis=-1) + ``` Here, `c[0-2]` are the individual stain channels. conversion: Desired color conversion. + keep_negative_values: Decides if the negative values produced by color separation + should be kept. + Returns: Depending on the conversion, the result can either be a RGB image, or a tuple of the separated stain channels. """ match conversion.conv_type: case ConversionType.RGB2STAIN: - return tuple(np.moveaxis(separate_stains(tile, conversion.matrix), -1, 0)) + return tuple( + np.moveaxis( + _separate_stains(tile, conversion.matrix, keep_negative_values), # type: ignore[arg-type] + -1, + 0, + ) + ) case ConversionType.STAIN2RGB: if isinstance(tile, Sequence): tile = np.stack(tile, axis=-1) + tile = np.asarray(tile, dtype=np.float64) return (255 * combine_stains(tile, conversion.matrix)).astype(np.uint8) diff --git a/rationai/staining/modify_and_reconstruct.py b/rationai/staining/modify_and_reconstruct.py new file mode 100644 index 0000000..10d3bab --- /dev/null +++ b/rationai/staining/modify_and_reconstruct.py @@ -0,0 +1,73 @@ +import numpy as np +from skimage.color import combine_stains + +from rationai.staining.typing import ModifyFunction, RGBArray, StainTuple +from rationai.staining.utils.inv_mat import inv_mat +from rationai.staining.utils.residual import residual + + +def modify_and_reconstruct( + tile: RGBArray, + modify: ModifyFunction, + stain0: StainTuple, + stain1: StainTuple, + stain2: StainTuple | None = None, +) -> RGBArray: + """Modifies RGB tile in stain space and converts back to RGB. + + Args: + tile: Input RGB representation of the region. + modify: Function that takes three channels in stain space + and returns their modified versions. + stain0: First default color vector. + stain1: Second default color vector. + stain2: Third default color vector. If not provided, + a residual vector is computed from the first two. + + Returns: + Modified region in RGB space. + + Note: + - To ensure as precise reconstruction as possible, + **clipping of negative values is ommited** from stain separation. + + References: + Stain separation is adapted from skimage.color.separate_stains. + + Example: + ```python + import numpy as np + from skimage.data import immunohistochemistry + + from rationai.staining import modify_and_reconstruct + from rationai.staining.constants import QUPATH_DAB, QUPATH_H + + + def modify(c0, c1, c2): + # Remove second channel from the image + c1 = np.zeros_like(c1) + + return c0, c1, c2 + + + original = immunohistochemistry() + modified = modify_and_reconstruct(original, modify, QUPATH_H, QUPATH_DAB) + ``` + """ + if stain2 is None: + stain2 = residual(stain0, stain1) + + mat = (stain0, stain1, stain2) + + stain2rgb = np.stack(mat) + rgb2stain = np.stack(inv_mat(mat)) + + values = np.maximum(tile.astype(np.float64) / 255, 1e-6) + log_adjust = np.log(1e-6) + + stains = (np.log(values) / log_adjust) @ rgb2stain + c0, c1, c2 = tuple(np.moveaxis(stains, -1, 0)) + + modified_stains = np.stack(modify(c0, c1, c2), axis=-1) + + return (255 * combine_stains(modified_stains, stain2rgb)).astype(np.uint8) diff --git a/rationai/staining/scripts/remove_red_stain.py b/rationai/staining/scripts/remove_red_stain.py new file mode 100644 index 0000000..f413265 --- /dev/null +++ b/rationai/staining/scripts/remove_red_stain.py @@ -0,0 +1,38 @@ +from pathlib import Path +from typing import TypeAlias + +import numpy as np +from numpy.typing import NDArray +from PIL import Image + +from rationai.staining.modify_and_reconstruct import modify_and_reconstruct + + +Channel: TypeAlias = NDArray[np.float64] + + +DIRECTORY_PATH = "/mnt/data/Projects/MOU/Bad_scans/stain separation/" +FILES = Path(DIRECTORY_PATH).glob("*.JPG") + +# STAIN DEFINITIONS +HEMATOXYLIN = (0.849, 0.514, 0.124) +DAB = (0.262, 0.631, 0.730) +RED = (0.128, 0.875, 0.466) + + +def modify(c0: Channel, c1: Channel, c2: Channel) -> tuple[Channel, Channel, Channel]: + c2 = np.zeros_like(c2) + + return c0, c1, c2 + + +def main() -> None: + for file in FILES: + img = np.asarray(Image.open(file).convert("RGB")) + modified = modify_and_reconstruct(img, modify, HEMATOXYLIN, DAB, RED) + + Image.fromarray(modified).save(f"{file.stem}.png") + + +if __name__ == "__main__": + main() diff --git a/rationai/staining/typing.py b/rationai/staining/typing.py index e57a567..66f443f 100644 --- a/rationai/staining/typing.py +++ b/rationai/staining/typing.py @@ -1,3 +1,4 @@ +from collections.abc import Callable from typing import TypeAlias from numpy import float64, uint8 @@ -5,6 +6,16 @@ from PIL.Image import Image +ModifyFunction: TypeAlias = Callable[ + [NDArray[float64], NDArray[float64], NDArray[float64]], + tuple[NDArray[float64], NDArray[float64], NDArray[float64]], +] +""" +Type definition for function that modifies stain channels. + +The function takes three arrays in stain space and returns their modified versions. +""" + Tile: TypeAlias = NDArray[float64 | uint8] | Image """ Type definition for a single tile. diff --git a/tests/conftest.py b/tests/conftest.py index d560f2e..6479e1f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,8 +2,17 @@ import pytest from numpy.typing import NDArray from PIL import Image +from skimage.color import rgb2lab -from rationai.staining.typing import RGBArray +from rationai.staining.typing import RGBArray, StainArray + + +def stain2lab(stain: StainArray) -> NDArray[np.float64]: + """Converts a pixel in stain space to LAB space.""" + stain = np.maximum(stain, 1e-6) + rgb = np.exp(stain) + + return rgb2lab(rgb) @pytest.fixture diff --git a/tests/data/generated/h_dab.jpg b/tests/data/generated/h_dab.jpg deleted file mode 100644 index 4ceaaf9daaff92df778098148eebda50edcd1dc2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43338 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 ZMrI7Ph{oem}pYi;f_&@KKF>C+; diff --git a/tests/data/generated/h_dab.png b/tests/data/generated/h_dab.png new file mode 100644 index 0000000000000000000000000000000000000000..973134f479e972613496c065dbafc4bf5f59431f GIT binary patch literal 14855 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 literal 0 HcmV?d00001 diff --git a/tests/data/generated/h_dab_legacy.png b/tests/data/generated/h_dab_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..270e45f78e889780ffde33bf52159f12b7a5f2bc GIT binary patch literal 14366 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 literal 0 HcmV?d00001 diff --git a/tests/data/generated/h_e.jpg b/tests/data/generated/h_e.jpg deleted file mode 100644 index d3552c2f1dc4367053bb581629f992bc43dd00e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42565 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 diff --git a/tests/data/generated/h_e.png b/tests/data/generated/h_e.png new file mode 100644 index 0000000000000000000000000000000000000000..951b6697b379c00ca08849f1a59bcad3eb42136b GIT binary patch literal 14366 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 literal 0 HcmV?d00001 diff --git a/tests/scripts/generate_artifical_tissue.py b/tests/scripts/generate_artifical_tissue.py index 15349c6..06cb971 100644 --- a/tests/scripts/generate_artifical_tissue.py +++ b/tests/scripts/generate_artifical_tissue.py @@ -11,6 +11,7 @@ H_RGB = np.array([88, 76, 180], dtype=np.uint8) # from QuPath E_RGB = np.array([199, 50, 112], dtype=np.uint8) # from QuPath DAB_RGB = np.array([186, 110, 56], dtype=np.uint8) # from QuPath +LIGHT_H_RGB = np.array([38, 123, 223], dtype=np.uint8) def _stretch(x: NDArray[np.float64], down: float, up: float) -> NDArray[np.float64]: @@ -58,10 +59,13 @@ def artificial_image(c0: RGBArray, c1: RGBArray, conv: ColorConversion) -> RGBAr def main() -> None: h_e = artificial_image(H_RGB, E_RGB, ColorConversion.RGB2HER) - Image.fromarray(h_e).save("tests/data/generated/h_e.jpg") + Image.fromarray(h_e).save("tests/data/generated/h_e.png") - h_dab = artificial_image(H_RGB, DAB_RGB, ColorConversion.RGB2HDR) - Image.fromarray(h_dab).save("tests/data/generated/h_dab.jpg") + h_dab_legacy = artificial_image(H_RGB, DAB_RGB, ColorConversion.RGB2HDR_LEGACY) + Image.fromarray(h_dab_legacy).save("tests/data/generated/h_dab_legacy.png") + + h_dab = artificial_image(LIGHT_H_RGB, DAB_RGB, ColorConversion.RGB2HDR) + Image.fromarray(h_dab).save("tests/data/generated/h_dab.png") if __name__ == "__main__": diff --git a/tests/test_color_conversion.py b/tests/test_color_conversion.py index 4e842fc..302d421 100644 --- a/tests/test_color_conversion.py +++ b/tests/test_color_conversion.py @@ -2,7 +2,7 @@ import pytest from rationai.staining import ColorConversion -from rationai.staining.constants import QUPATH_DAB, QUPATH_E, QUPATH_H +from rationai.staining.constants import LIGHT_H, QUPATH_DAB, QUPATH_E, QUPATH_H from rationai.staining.typing import StainTuple from rationai.staining.utils import residual @@ -22,10 +22,11 @@ def test_inverse_conversion(conversion: ColorConversion) -> None: STAINS = [ # Expected values are taken from QuPath (QUPATH_H, QUPATH_E, (0.316, -0.598, 0.737)), + (LIGHT_H, QUPATH_DAB, (0.418, -0.796, 0.437)), (QUPATH_H, QUPATH_DAB, (0.633, -0.713, 0.302)), ] -@pytest.mark.parametrize("s0,s1,expected", STAINS, ids=["H&E", "H&DAB"]) +@pytest.mark.parametrize("s0,s1,expected", STAINS, ids=["H&E", "H&DAB", "H&DAB_LEGACY"]) def test_residual(s0: StainTuple, s1: StainTuple, expected: StainTuple) -> None: assert np.all(np.isclose(residual(s0, s1), expected, atol=0.001)) diff --git a/tests/test_modify_and_reconstruct.py b/tests/test_modify_and_reconstruct.py new file mode 100644 index 0000000..f9c2a86 --- /dev/null +++ b/tests/test_modify_and_reconstruct.py @@ -0,0 +1,61 @@ +import numpy as np +import pytest +from numpy.typing import NDArray +from skimage.data import immunohistochemistry +from skimage.metrics import structural_similarity + +from rationai.staining import modify_and_reconstruct +from rationai.staining.constants import LIGHT_H, QUPATH_DAB, QUPATH_E, QUPATH_H +from rationai.staining.typing import ModifyFunction, RGBArray, StainTuple + + +C = NDArray[np.float64] + + +def _no_change(c0: C, c1: C, c2: C) -> tuple[C, C, C]: + return c0, c1, c2 + + +IMAGES_AND_STAINS = [ + ( + immunohistochemistry(), + _no_change, + LIGHT_H, + QUPATH_DAB, + None, + ), + ( + "sample1_original", + _no_change, + QUPATH_H, + QUPATH_E, + None, + ), +] + +TEST_IDS = ["skimage-default; no change", "Sample1; no change"] + + +@pytest.mark.parametrize( + "original,modify,stain0,stain1,stain2", IMAGES_AND_STAINS, ids=TEST_IDS +) +def test_modify_and_reconstruct( + original: RGBArray | str, + modify: ModifyFunction, + stain0: StainTuple, + stain1: StainTuple, + stain2: StainTuple, + request: pytest.FixtureRequest, +) -> None: + if isinstance(original, str): + original = request.getfixturevalue(original) + + reconstructed = modify_and_reconstruct( + tile=original, # type: ignore[arg-type] + modify=modify, + stain0=stain0, + stain1=stain1, + stain2=stain2, + ) + + assert structural_similarity(original, reconstructed, channel_axis=-1) > 0.99 diff --git a/tests/test_stain_estimator.py b/tests/test_stain_estimator.py index 8104b59..83e390e 100644 --- a/tests/test_stain_estimator.py +++ b/tests/test_stain_estimator.py @@ -3,6 +3,7 @@ import numpy as np import pytest +from conftest import stain2lab from numpy.typing import NDArray from PIL import Image from skimage.color import deltaE_ciede2000 @@ -10,7 +11,7 @@ from skimage.filters import gaussian from rationai.staining import ColorConversion, convert_color, estimate_stain_vectors -from rationai.staining.constants import QUPATH_DAB, QUPATH_E, QUPATH_H +from rationai.staining.constants import LIGHT_H, QUPATH_DAB, QUPATH_E, QUPATH_H from rationai.staining.typing import StainArray, Tile @@ -45,22 +46,32 @@ def _blur(img: NDArray[np.uint8], sigma: float) -> NDArray[np.uint8]: QUPATH_DAB, ), ( - np.asarray(Image.open(GENERATED_DIR / "h_e.jpg")), + np.asarray(Image.open(GENERATED_DIR / "h_e.png")), QUPATH_H, QUPATH_E, ), ( - np.asarray(Image.open(GENERATED_DIR / "h_dab.jpg")), + np.asarray(Image.open(GENERATED_DIR / "h_dab.png")), + LIGHT_H, + QUPATH_DAB, + ), + ( + np.asarray(Image.open(GENERATED_DIR / "h_dab_legacy.png")), QUPATH_H, QUPATH_DAB, ), ( - _blur(np.asarray(Image.open(GENERATED_DIR / "h_e.jpg")), sigma=5), + _blur(np.asarray(Image.open(GENERATED_DIR / "h_e.png")), sigma=5), QUPATH_H, QUPATH_E, ), ( - _blur(np.asarray(Image.open(GENERATED_DIR / "h_dab.jpg")), sigma=5), + _blur(np.asarray(Image.open(GENERATED_DIR / "h_dab.png")), sigma=5), + LIGHT_H, + QUPATH_DAB, + ), + ( + _blur(np.asarray(Image.open(GENERATED_DIR / "h_dab_legacy.png")), sigma=5), QUPATH_H, QUPATH_DAB, ), @@ -72,11 +83,13 @@ def _blur(img: NDArray[np.uint8], sigma: float) -> NDArray[np.uint8]: ] IDS = [ - "Default skimage image H&DAB", + "Default skimage image H&DAB_LEGACY", "Artificial H&E", "Artificial H&DAB", + "Artificial H&DAB_LEGACY", "Blurred Artificial H&E", "Blurred Artificial H&DAB", + "Blurred Artificial H&DAB_LEGACY", "Empty Image", ] @@ -101,5 +114,8 @@ def test_estimation_color_difference( stain1, stain2 = estimate_stain_vectors(img) delta = deltaE_ciede2000 - assert delta(stain1, expected1) < 1 or self._both_nan(stain1, expected1) - assert delta(stain2, expected2) < 1 or self._both_nan(stain2, expected2) + stain1, stain2 = stain2lab(stain1), stain2lab(stain2) + expected1, expected2 = stain2lab(expected1), stain2lab(expected2) + + assert delta(stain1, expected1) < 15 or self._both_nan(stain1, expected1) + assert delta(stain2, expected2) < 15 or self._both_nan(stain2, expected2) diff --git a/tests/test_stain_reconstruction.py b/tests/test_stain_reconstruction.py index 4a5f53b..323a764 100644 --- a/tests/test_stain_reconstruction.py +++ b/tests/test_stain_reconstruction.py @@ -12,13 +12,17 @@ immunohistochemistry(), ColorConversion.RGB2HDR, ), + ( + immunohistochemistry(), + ColorConversion.RGB2HDR_LEGACY, + ), ( "sample1_original", ColorConversion.RGB2HER, ), ] -TEST_IDS = ["skimage-default; H&DAB", "Sample1; H&E"] +TEST_IDS = ["skimage-default; H&DAB", "skimage-default; H&DAB_LEGACY", "Sample1; H&E"] @pytest.mark.parametrize("original,conv", IMAGES_AND_CONVS, ids=TEST_IDS) diff --git a/tests/test_stain_separation.py b/tests/test_stain_separation.py index e02357e..ecca7d8 100644 --- a/tests/test_stain_separation.py +++ b/tests/test_stain_separation.py @@ -10,6 +10,7 @@ RGB_CONVERSIONS = [ ColorConversion.RGB2HER, ColorConversion.RGB2HDR, + ColorConversion.RGB2HDR_LEGACY, ] RGB_NAMES = [c.name for c in RGB_CONVERSIONS] From b8570a026cbd4717cb50242dec2f0cce255671eb Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni.cz> Date: Tue, 4 Mar 2025 23:46:53 +0100 Subject: [PATCH 26/85] feat: ml init --- .gitignore | 9 +- .gitlab-ci.yml | 7 +- .mypy.ini | 3 +- .pre-commit-config.yaml | 12 +- .pylintrc | 399 --- README.md | 43 +- .../datasets/stain_normalization/predict.yaml | 6 + .../datasets/stain_normalization/test.yaml | 6 + .../datasets/stain_normalization/train.yaml | 6 + .../datasets/stain_normalization/val.yaml | 7 + configs/data/modify/test.yaml | 12 + configs/data/modify/train.yaml | 12 + configs/data/transforms/test.yaml | 4 + configs/data/transforms/train.yaml | 16 + configs/default.yaml | 42 + .../py.typed => configs/experiment/.gitkeep | 0 configs/hydra/default.yaml | 7 + configs/logger/mlflow.yaml | 6 + configs/model/backbone/resnet50.yaml | 2 + configs/model/backbone/vgg16.yaml | 2 + configs/model/decode_head/unet2048.yaml | 5 + configs/model/decode_head/unet512.yaml | 5 + docs/api/color-conversion.md | 1 - docs/api/constants.md | 1 - docs/api/estimate-stain-vectors.md | 1 - docs/api/modify-and-reconstruct.md | 1 - docs/api/typing.md | 1 - docs/getting-started.md | 15 - docs/index.md | 1 - mkdocs.yml | 76 - nohup.out | 2772 +++++++++++++++++ pdm.lock | 2265 ++++++++++---- preprocessing/mask_generator.py | 69 + preprocessing/tiler.py | 120 + pyproject.toml | 32 +- pytest.ini | 6 - rationai/staining/__init__.py | 16 - rationai/staining/constants.py | 27 - rationai/staining/convert_color.py | 197 -- rationai/staining/estimate_stain_vectors.py | 63 - rationai/staining/modify_and_reconstruct.py | 73 - rationai/staining/scripts/remove_red_stain.py | 38 - rationai/staining/typing.py | 47 - rationai/staining/utils/__init__.py | 6 - rationai/staining/utils/inv_mat.py | 8 - rationai/staining/utils/normalize.py | 9 - rationai/staining/utils/residual.py | 12 - stain_normalization/__init__.py | 0 stain_normalization/data/__init__.py | 4 + stain_normalization/data/data_module.py | 51 + stain_normalization/data/datasets/__init__.py | 4 + .../data/datasets/stain_normalization.py | 114 + .../data/transformation/__init__.py | 7 + .../transformation/combiend_modification.py | 23 + .../transformation/exposure_adjustment.py | 17 + .../data/transformation/hed_factor.py | 27 + .../data/transformation/hue_shift.py | 34 + stain_normalization/main.py | 35 + stain_normalization/modeling/__init__.py | 3 + .../modeling/backbone/__init__.py | 5 + .../modeling/backbone/resnet50.py | 7 + .../modeling/backbone/vgg16.py | 6 + .../modeling/decode_head/__init__.py | 4 + .../modeling/decode_head/unet.py | 35 + stain_normalization/modeling/l1ssim_loss.py | 75 + .../stain_normalization_model.py | 68 + stain_normalization/typing.py | 20 + tests/conftest.py | 51 - tests/data/generated/h_dab.png | Bin 14855 -> 0 bytes tests/data/generated/h_dab_legacy.png | Bin 14366 -> 0 bytes tests/data/generated/h_e.png | Bin 14366 -> 0 bytes tests/data/sample1/eosin.png | Bin 183161 -> 0 bytes tests/data/sample1/hematoxylin.png | Bin 203258 -> 0 bytes tests/data/sample1/original.jpg | Bin 83398 -> 0 bytes tests/data/sample1/residual.png | Bin 79895 -> 0 bytes tests/scripts/generate_artifical_tissue.py | 72 - tests/test_color_conversion.py | 32 - tests/test_modify_and_reconstruct.py | 61 - tests/test_stain_estimator.py | 121 - tests/test_stain_reconstruction.py | 44 - tests/test_stain_separation.py | 53 - 81 files changed, 5396 insertions(+), 2045 deletions(-) delete mode 100644 .pylintrc create mode 100644 configs/data/datasets/stain_normalization/predict.yaml create mode 100644 configs/data/datasets/stain_normalization/test.yaml create mode 100644 configs/data/datasets/stain_normalization/train.yaml create mode 100644 configs/data/datasets/stain_normalization/val.yaml create mode 100644 configs/data/modify/test.yaml create mode 100644 configs/data/modify/train.yaml create mode 100644 configs/data/transforms/test.yaml create mode 100644 configs/data/transforms/train.yaml create mode 100644 configs/default.yaml rename rationai/staining/py.typed => configs/experiment/.gitkeep (100%) create mode 100644 configs/hydra/default.yaml create mode 100644 configs/logger/mlflow.yaml create mode 100644 configs/model/backbone/resnet50.yaml create mode 100644 configs/model/backbone/vgg16.yaml create mode 100644 configs/model/decode_head/unet2048.yaml create mode 100644 configs/model/decode_head/unet512.yaml delete mode 100644 docs/api/color-conversion.md delete mode 100644 docs/api/constants.md delete mode 100644 docs/api/estimate-stain-vectors.md delete mode 100644 docs/api/modify-and-reconstruct.md delete mode 100644 docs/api/typing.md delete mode 100644 docs/getting-started.md delete mode 100644 docs/index.md delete mode 100644 mkdocs.yml create mode 100644 nohup.out create mode 100644 preprocessing/mask_generator.py create mode 100644 preprocessing/tiler.py delete mode 100644 pytest.ini delete mode 100644 rationai/staining/__init__.py delete mode 100644 rationai/staining/constants.py delete mode 100644 rationai/staining/convert_color.py delete mode 100644 rationai/staining/estimate_stain_vectors.py delete mode 100644 rationai/staining/modify_and_reconstruct.py delete mode 100644 rationai/staining/scripts/remove_red_stain.py delete mode 100644 rationai/staining/typing.py delete mode 100644 rationai/staining/utils/__init__.py delete mode 100644 rationai/staining/utils/inv_mat.py delete mode 100644 rationai/staining/utils/normalize.py delete mode 100644 rationai/staining/utils/residual.py create mode 100644 stain_normalization/__init__.py create mode 100644 stain_normalization/data/__init__.py create mode 100644 stain_normalization/data/data_module.py create mode 100644 stain_normalization/data/datasets/__init__.py create mode 100644 stain_normalization/data/datasets/stain_normalization.py create mode 100644 stain_normalization/data/transformation/__init__.py create mode 100644 stain_normalization/data/transformation/combiend_modification.py create mode 100644 stain_normalization/data/transformation/exposure_adjustment.py create mode 100644 stain_normalization/data/transformation/hed_factor.py create mode 100644 stain_normalization/data/transformation/hue_shift.py create mode 100644 stain_normalization/main.py create mode 100644 stain_normalization/modeling/__init__.py create mode 100644 stain_normalization/modeling/backbone/__init__.py create mode 100644 stain_normalization/modeling/backbone/resnet50.py create mode 100644 stain_normalization/modeling/backbone/vgg16.py create mode 100644 stain_normalization/modeling/decode_head/__init__.py create mode 100644 stain_normalization/modeling/decode_head/unet.py create mode 100644 stain_normalization/modeling/l1ssim_loss.py create mode 100644 stain_normalization/stain_normalization_model.py create mode 100644 stain_normalization/typing.py delete mode 100644 tests/conftest.py delete mode 100644 tests/data/generated/h_dab.png delete mode 100644 tests/data/generated/h_dab_legacy.png delete mode 100644 tests/data/generated/h_e.png delete mode 100644 tests/data/sample1/eosin.png delete mode 100644 tests/data/sample1/hematoxylin.png delete mode 100644 tests/data/sample1/original.jpg delete mode 100644 tests/data/sample1/residual.png delete mode 100644 tests/scripts/generate_artifical_tissue.py delete mode 100644 tests/test_color_conversion.py delete mode 100644 tests/test_modify_and_reconstruct.py delete mode 100644 tests/test_stain_estimator.py delete mode 100644 tests/test_stain_reconstruction.py delete mode 100644 tests/test_stain_separation.py diff --git a/.gitignore b/.gitignore index f490645..1c12553 100644 --- a/.gitignore +++ b/.gitignore @@ -138,7 +138,7 @@ venv.bak/ .ropeproject # mkdocs documentation -/site +site # mypy .mypy_cache/ @@ -166,6 +166,10 @@ cython_debug/ # Mlflow mlruns/ mlflow_store/ +mlflow_artifacts/ + +# Pytorch Lightning +lightning_logs/ # PyCharm # JetBrains specific template is maintained in a separate JetBrains.gitignore that can @@ -173,3 +177,6 @@ mlflow_store/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. .idea/ + +# Project +/data diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9d40d40..b132c47 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,11 +1,6 @@ include: project: rationai/digital-pathology/templates/ci-templates - file: - - Pytest.gitlab-ci.yml - - Python-Lint.gitlab-ci.yml - - MkDocs.gitlab-ci.yml + file: Python-Lint.gitlab-ci.yml stages: - lint - - test - - deploy diff --git a/.mypy.ini b/.mypy.ini index 8d15f3c..1c6f44e 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -2,5 +2,4 @@ strict = True ignore_missing_imports = True disallow_untyped_calls = False -disable_error_code = no-any-return -explicit_package_bases = True \ No newline at end of file +disable_error_code = no-any-return \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9bbe87c..8b99ad0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,19 +7,17 @@ repos: - id: check-yaml args: [--unsafe] - - repo: https://github.com/compilerla/conventional-pre-commit - rev: v3.6.0 + - repo: https://github.com/commitizen-tools/commitizen + rev: v3.30.1 hooks: - - id: conventional-pre-commit - stages: [commit-msg] - args: [] # optional: list of Conventional Commits types to allow e.g. [feat, fix, ci, chore, test] + - id: commitizen - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.1 + rev: v0.7.3 hooks: # Run the linter. - id: ruff entry: pdm lint --force-exclude - # Run the formatter. + # Run the formatter. - id: ruff-format entry: pdm format --force-exclude diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index c608030..0000000 --- a/.pylintrc +++ /dev/null @@ -1,399 +0,0 @@ -# This Pylint rcfile contains a best-effort configuration to uphold the -# best-practices and style described in the Google Python style guide: -# https://google.github.io/styleguide/pyguide.html -# -# Its canonical open-source location is: -# https://google.github.io/styleguide/pylintrc - -[MAIN] - -# Files or directories to be skipped. They should be base names, not paths. -ignore=third_party - -# Files or directories matching the regex patterns are skipped. The regex -# matches against base names, not paths. -ignore-patterns= - -# Pickle collected data for later comparisons. -persistent=no - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - -# Use multiple processes to speed up Pylint. -jobs=4 - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -#enable= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" -disable=R, - abstract-method, - apply-builtin, - arguments-differ, - attribute-defined-outside-init, - backtick, - bad-option-value, - basestring-builtin, - buffer-builtin, - c-extension-no-member, - consider-using-enumerate, - cmp-builtin, - cmp-method, - coerce-builtin, - coerce-method, - delslice-method, - div-method, - eq-without-hash, - execfile-builtin, - file-builtin, - filter-builtin-not-iterating, - fixme, - getslice-method, - global-statement, - hex-method, - idiv-method, - implicit-str-concat, - import-error, - import-self, - import-star-module-level, - input-builtin, - intern-builtin, - invalid-str-codec, - locally-disabled, - long-builtin, - long-suffix, - map-builtin-not-iterating, - misplaced-comparison-constant, - missing-function-docstring, - metaclass-assignment, - next-method-called, - next-method-defined, - no-absolute-import, - no-init, # added - no-member, - no-name-in-module, - no-self-use, - nonzero-method, - oct-method, - old-division, - old-ne-operator, - old-octal-literal, - old-raise-syntax, - parameter-unpacking, - print-statement, - raising-string, - range-builtin-not-iterating, - raw_input-builtin, - rdiv-method, - reduce-builtin, - relative-import, - reload-builtin, - round-builtin, - setslice-method, - signature-differs, - standarderror-builtin, - suppressed-message, - sys-max-int, - trailing-newlines, - unichr-builtin, - unicode-builtin, - unnecessary-pass, - unpacking-in-except, - useless-else-on-loop, - useless-suppression, - using-cmp-argument, - wrong-import-order, - xrange-builtin, - zip-builtin-not-iterating, - - -[REPORTS] - -# Set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html. You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages -reports=no - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - - -[BASIC] - -# Good variable names which should always be accepted, separated by a comma -good-names=main,_ - -# Bad variable names which should always be refused, separated by a comma -bad-names= - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=no - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl - -# Regular expression matching correct function names -function-rgx=^(?:(?PsetUp|tearDown|setUpModule|tearDownModule)|(?P_?[A-Z][a-zA-Z0-9]*)|(?P_?[a-z][a-z0-9_]*))$ - -# Regular expression matching correct variable names -variable-rgx=^[a-z][a-z0-9_]*$ - -# Regular expression matching correct constant names -const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ - -# Regular expression matching correct attribute names -attr-rgx=^_{0,2}[a-z][a-z0-9_]*$ - -# Regular expression matching correct argument names -argument-rgx=^[a-z][a-z0-9_]*$ - -# Regular expression matching correct class attribute names -class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ - -# Regular expression matching correct inline iteration names -inlinevar-rgx=^[a-z][a-z0-9_]*$ - -# Regular expression matching correct class names -class-rgx=^_?[A-Z][a-zA-Z0-9]*$ - -# Regular expression matching correct module names -module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$ - -# Regular expression matching correct method names -method-rgx=(?x)^(?:(?P_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P_{0,2}[a-z][a-z0-9_]*))$ - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$ - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=12 - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - - -[FORMAT] - -# Maximum number of characters on a single line. -max-line-length=88 - -# TODO(https://github.com/pylint-dev/pylint/issues/3352): Direct pylint to exempt -# lines made too long by directives to pytype. - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=(?x)( - ^\s*(\#\ )??$| - ^\s*(from\s+\S+\s+)?import\s+.+$) - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=yes - -# Maximum number of lines in a module -max-module-lines=99999 - -# String used as indentation unit. The internal Google style guide mandates 2 -# spaces. Google's externaly-published style guide says 4, consistent with -# PEP 8. Here, we use 2 spaces, for conformity with many open-sourced Google -# projects (like TensorFlow). -indent-string=' ' - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=TODO - - -[STRING] - -# This flag controls whether inconsistent-quotes generates a warning when the -# character used as a quote delimiter is used inconsistently within a module. -check-quote-consistency=yes - - -[VARIABLES] - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_) - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_,_cb - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging,absl.logging,tensorflow.io.logging - - -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=4 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - - -[SPELLING] - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub, - TERMIOS, - Bastion, - rexec, - sets - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant, absl - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__, - __new__, - setUp - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict, - _fields, - _replace, - _source, - _make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls, - class_ - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs diff --git a/README.md b/README.md index 1328bc6..71036c8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,42 @@ -# Staining +# Stain Normalization +export MLFLOW_TRACKING_USERNAME= -[[`Documentation`](https://rationai.gitlab-pages.ics.muni.cz/digital-pathology/libraries/staining)] \ No newline at end of file + + +[![PyTorch Lightning](https://img.shields.io/badge/pytorch-lightning-blue.svg?logo=PyTorch%20Lightning)](https://github.com/Lightning-AI/lightning) +[![License](https://img.shields.io/badge/License-MIT-red.svg)](https://gitlab.ics.muni.cz/rationai/digital-pathology/pathology/patch-camelyon/-/blob/master/LICENSE) + + +The main goal ... + + +## Getting Started + +### Installation + +Install [PDM](https://pdm.fming.dev/) package manager and install all the dependencies using the following command: +```bash +pdm install +``` + +### Preprocessing + +```bash +export MLFLOW_TRACKING_USERNAME= +pdm run preprocessing/generate_tissue_mask.py +pdm run preprocessing/tiling.py +``` + +### Training + +```bash +export MLFLOW_TRACKING_USERNAME= +pdm fit model/backbone=(vgg16|resnet18) +``` + +### Testing + +```bash +export MLFLOW_TRACKING_USERNAME= +pdm test model/backbone=(vgg16|resnet18) 'checkpoint=""' +``` diff --git a/configs/data/datasets/stain_normalization/predict.yaml b/configs/data/datasets/stain_normalization/predict.yaml new file mode 100644 index 0000000..7832a66 --- /dev/null +++ b/configs/data/datasets/stain_normalization/predict.yaml @@ -0,0 +1,6 @@ +defaults: + - /data/transforms@transforms: test + - /data/modify@modify: test + +_target_: stain_normalization.data.datasets.StainNormalizationPredict +uris: ["mlflow-artifacts:"] diff --git a/configs/data/datasets/stain_normalization/test.yaml b/configs/data/datasets/stain_normalization/test.yaml new file mode 100644 index 0000000..311cda0 --- /dev/null +++ b/configs/data/datasets/stain_normalization/test.yaml @@ -0,0 +1,6 @@ +defaults: + - /data/transforms@transforms: test + - /data/modify@modify: test + +_target_: stain_normalization.data.datasets.StainNormalization +uris: ["mlflow-artifacts:/79/bc7dfc2258164cd292091c3056609277/artifacts/Stain Normalization - test/tiles.parquet"] diff --git a/configs/data/datasets/stain_normalization/train.yaml b/configs/data/datasets/stain_normalization/train.yaml new file mode 100644 index 0000000..3d99dad --- /dev/null +++ b/configs/data/datasets/stain_normalization/train.yaml @@ -0,0 +1,6 @@ +defaults: + - /data/transforms@transforms: train + - /data/modify@modify: train + +_target_: stain_normalization.data.datasets.StainNormalization +uris: ["mlflow-artifacts:"] diff --git a/configs/data/datasets/stain_normalization/val.yaml b/configs/data/datasets/stain_normalization/val.yaml new file mode 100644 index 0000000..53e608c --- /dev/null +++ b/configs/data/datasets/stain_normalization/val.yaml @@ -0,0 +1,7 @@ +defaults: + - /data/transforms@transforms: test + - /data/modify@modify: test + +_target_: stain_normalization.data.datasets.StainNormalization +uris: ["mlflow-artifacts:"] + diff --git a/configs/data/modify/test.yaml b/configs/data/modify/test.yaml new file mode 100644 index 0000000..c8d3ae7 --- /dev/null +++ b/configs/data/modify/test.yaml @@ -0,0 +1,12 @@ +_target_: albumentations.OneOf +transforms: + - _target_: stain_normalization.data.transformation.HED + h_range: [0.8, 1.2] + e_range: [0.8, 1.2] + - _target_: stain_normalization.data.transformation.ExposureAdjustment + brightness_range: [0.8, 1.2] + - _target_: UnnaturalColorModification + hue_shift_range: [0.2, 1.0] + - _target_: CombinedModifications + brightness_range: [-0.4, 0.4] + intensity_range: [0.65, 1.35] \ No newline at end of file diff --git a/configs/data/modify/train.yaml b/configs/data/modify/train.yaml new file mode 100644 index 0000000..c8d3ae7 --- /dev/null +++ b/configs/data/modify/train.yaml @@ -0,0 +1,12 @@ +_target_: albumentations.OneOf +transforms: + - _target_: stain_normalization.data.transformation.HED + h_range: [0.8, 1.2] + e_range: [0.8, 1.2] + - _target_: stain_normalization.data.transformation.ExposureAdjustment + brightness_range: [0.8, 1.2] + - _target_: UnnaturalColorModification + hue_shift_range: [0.2, 1.0] + - _target_: CombinedModifications + brightness_range: [-0.4, 0.4] + intensity_range: [0.65, 1.35] \ No newline at end of file diff --git a/configs/data/transforms/test.yaml b/configs/data/transforms/test.yaml new file mode 100644 index 0000000..7c0a147 --- /dev/null +++ b/configs/data/transforms/test.yaml @@ -0,0 +1,4 @@ +_target_: albumentations.Normalize +mean: [198.9923, 156.7051, 185.0198] +std: [36.8293, 46.7353, 35.8960] +max_pixel_value: 1 diff --git a/configs/data/transforms/train.yaml b/configs/data/transforms/train.yaml new file mode 100644 index 0000000..81162f1 --- /dev/null +++ b/configs/data/transforms/train.yaml @@ -0,0 +1,16 @@ +_target_: albumentations.Compose +transforms: + - _target_: albumentations.RandomRotate90 + p: 1.0 + - _target_: albumentations.Flip + p: 0.75 + - _target_: albumentations.RandomGamma + p: 0.5 + - _target_: albumentations.RandomBrightnessContrast + p: 0.5 + - _target_: albumentations.HueSaturationValue + p: 0.5 + - _target_: albumentations.Normalize + mean: [198.9923, 156.7051, 185.0198] + std: [36.8293, 46.7353, 35.8960] + max_pixel_value: 1 diff --git a/configs/default.yaml b/configs/default.yaml new file mode 100644 index 0000000..79092e5 --- /dev/null +++ b/configs/default.yaml @@ -0,0 +1,42 @@ +defaults: + - hydra: default + - logger: mlflow + - /data/datasets@data.train: stain_normalization/train + - /data/datasets@data.val: stain_normalization/val + - /data/datasets@data.test: stain_normalization/test + - /data/datasets@data.predict: stain_normalization/predict + - /model/backbone: resnet50 + - /model/decode_head: unet2048 + - _self_ + +seed: ${random_seed:} +mode: ??? +checkpoint: null + +trainer: + enable_checkpointing: True + max_epochs: 30 + log_every_n_steps: 5 + callbacks: + - _target_: lightning.pytorch.callbacks.ModelCheckpoint + save_top_k: 1 + monitor: validation/loss + mode: min + +data: + batch_size: 256 + num_workers: 8 + +metadata: + user: xlopatka + experiment_name: Stain Normalization + run_name: Test Stain Normalization + description: Testing Stain Normalization + hyperparams: {} + + + + + + + diff --git a/rationai/staining/py.typed b/configs/experiment/.gitkeep similarity index 100% rename from rationai/staining/py.typed rename to configs/experiment/.gitkeep diff --git a/configs/hydra/default.yaml b/configs/hydra/default.yaml new file mode 100644 index 0000000..275e331 --- /dev/null +++ b/configs/hydra/default.yaml @@ -0,0 +1,7 @@ +defaults: + - _self_ + - override hydra_logging: disabled + - override job_logging: disabled +output_subdir: null +run: + dir: . diff --git a/configs/logger/mlflow.yaml b/configs/logger/mlflow.yaml new file mode 100644 index 0000000..10355c7 --- /dev/null +++ b/configs/logger/mlflow.yaml @@ -0,0 +1,6 @@ +_target_: rationai.mlkit.lightning.loggers.MLFlowLogger +experiment_name: ${metadata.experiment_name} +run_name: ${metadata.run_name} +tags: + mlflow.user: ${metadata.user} + mlflow.note.content: ${metadata.description} diff --git a/configs/model/backbone/resnet50.yaml b/configs/model/backbone/resnet50.yaml new file mode 100644 index 0000000..4b6b5f2 --- /dev/null +++ b/configs/model/backbone/resnet50.yaml @@ -0,0 +1,2 @@ +_target_: stain_normalization.modeling.backbone.resnet50 +weights: null diff --git a/configs/model/backbone/vgg16.yaml b/configs/model/backbone/vgg16.yaml new file mode 100644 index 0000000..4e72da5 --- /dev/null +++ b/configs/model/backbone/vgg16.yaml @@ -0,0 +1,2 @@ +_target_: stain_normalization.modeling.backbone.vgg16 +weights: null diff --git a/configs/model/decode_head/unet2048.yaml b/configs/model/decode_head/unet2048.yaml new file mode 100644 index 0000000..1936549 --- /dev/null +++ b/configs/model/decode_head/unet2048.yaml @@ -0,0 +1,5 @@ +decode_head: + _target_: stain_normalization.modeling.decode_head.UNetDecoder + in_channels: 2048 + weights: null + diff --git a/configs/model/decode_head/unet512.yaml b/configs/model/decode_head/unet512.yaml new file mode 100644 index 0000000..d3461f1 --- /dev/null +++ b/configs/model/decode_head/unet512.yaml @@ -0,0 +1,5 @@ +decode_head: + _target_: stain_normalization.modeling.decode_head.UNetDecoder + in_channels: 512 + weights: null + diff --git a/docs/api/color-conversion.md b/docs/api/color-conversion.md deleted file mode 100644 index 43b6952..0000000 --- a/docs/api/color-conversion.md +++ /dev/null @@ -1 +0,0 @@ -::: rationai.staining.convert_color \ No newline at end of file diff --git a/docs/api/constants.md b/docs/api/constants.md deleted file mode 100644 index 616707f..0000000 --- a/docs/api/constants.md +++ /dev/null @@ -1 +0,0 @@ -::: rationai.staining.constants diff --git a/docs/api/estimate-stain-vectors.md b/docs/api/estimate-stain-vectors.md deleted file mode 100644 index 520ea02..0000000 --- a/docs/api/estimate-stain-vectors.md +++ /dev/null @@ -1 +0,0 @@ -::: rationai.staining.estimate_stain_vectors \ No newline at end of file diff --git a/docs/api/modify-and-reconstruct.md b/docs/api/modify-and-reconstruct.md deleted file mode 100644 index ef9f38d..0000000 --- a/docs/api/modify-and-reconstruct.md +++ /dev/null @@ -1 +0,0 @@ -::: rationai.staining.modify_and_reconstruct \ No newline at end of file diff --git a/docs/api/typing.md b/docs/api/typing.md deleted file mode 100644 index 36b0bbe..0000000 --- a/docs/api/typing.md +++ /dev/null @@ -1 +0,0 @@ -::: rationai.staining.typing \ No newline at end of file diff --git a/docs/getting-started.md b/docs/getting-started.md deleted file mode 100644 index 325f7c4..0000000 --- a/docs/getting-started.md +++ /dev/null @@ -1,15 +0,0 @@ -# Getting Started - -## Installation - -=== "pdm" - - ```bash - pdm add git+ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git - ``` - -=== "pip" - - ```bash - pip install git+ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git - ``` diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 00dd3b4..0000000 --- a/docs/index.md +++ /dev/null @@ -1 +0,0 @@ -Staining library \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml deleted file mode 100644 index d3e70d5..0000000 --- a/mkdocs.yml +++ /dev/null @@ -1,76 +0,0 @@ -site_name: Staining - -nav: - - Introduction: index.md - - Getting Started: getting-started.md - - API Reference: - - api/color-conversion.md - - estimate_stain_vectors: api/estimate-stain-vectors.md - - modify_and_reconstruct: api/modify-and-reconstruct.md - - api/constants.md - - api/typing.md - -theme: - name: material - features: - - announce.dismiss - - content.action.edit - - content.action.view - - content.code.annotate - - content.code.copy - - content.code.select - - content.tooltips - - navigation.indexes - - navigation.sections - - navigation.top - - navigation.footer - - navigation.tracking - - search.highlight - - search.share - - search.suggest - - toc.follow - palette: - - media: "(prefers-color-scheme)" - toggle: - icon: material/link - name: Switch to light mode - - media: "(prefers-color-scheme: light)" - scheme: default - primary: indigo - accent: indigo - toggle: - icon: material/toggle-switch - name: Switch to dark mode - - media: "(prefers-color-scheme: dark)" - scheme: slate - primary: black - accent: indigo - toggle: - icon: material/toggle-switch-off - name: Switch to system preference - -plugins: - - search - - mkdocstrings: - handlers: - python: - options: - show_if_no_docstring: true - -markdown_extensions: - - pymdownx.superfences: - custom_fences: - - name: mermaid - class: mermaid - format: !!python/name:pymdownx.superfences.fence_code_format - - pymdownx.tabbed: - alternate_style: true - combine_header_slug: true - slugify: !!python/object/apply:pymdownx.slugs.slugify - kwds: - case: lower - - admonition - - pymdownx.details - - pymdownx.superfences - - pymdownx.highlight - - pymdownx.inlinehilite diff --git a/nohup.out b/nohup.out new file mode 100644 index 0000000..1485bf9 --- /dev/null +++ b/nohup.out @@ -0,0 +1,2772 @@ + + 0%| | 0/235 [00:00 + main() + File "/home/jovyan/machine-learning/preprocessing/mask_generator.py", line 63, in main + process_items(slides, process_item=process_slide) + File "/home/jovyan/machine-learning/.venv/lib/python3.11/site-packages/rationai/masks/processing.py", line 56, in process_items + ready, pending = ray.wait(pending) + ^^^^^^^^^^^^^^^^^ + File "/home/jovyan/machine-learning/.venv/lib/python3.11/site-packages/ray/_private/auto_init_hook.py", line 21, in auto_init_wrapper + return fn(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^ + File "/home/jovyan/machine-learning/.venv/lib/python3.11/site-packages/ray/_private/client_mode_hook.py", line 103, in wrapper + return func(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^ + File "/home/jovyan/machine-learning/.venv/lib/python3.11/site-packages/ray/_private/worker.py", line 2976, in wait + ready_ids, remaining_ids = worker.core_worker.wait( + ^^^^^^^^^^^^^^^^^^^^^^^^ + File "python/ray/_raylet.pyx", line 3816, in ray._raylet.CoreWorker.wait + File "python/ray/includes/common.pxi", line 79, in ray._raylet.check_status +KeyboardInterrupt +Exception ignored in atexit callback: +Traceback (most recent call last): + File "/home/jovyan/machine-learning/.venv/lib/python3.11/site-packages/ray/_private/client_mode_hook.py", line 103, in wrapper + return func(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^ + File "/home/jovyan/machine-learning/.venv/lib/python3.11/site-packages/ray/_private/worker.py", line 1905, in shutdown + _global_node.kill_all_processes(check_alive=False, allow_graceful=True) + File "/home/jovyan/machine-learning/.venv/lib/python3.11/site-packages/ray/_private/node.py", line 1637, in kill_all_processes + self._kill_process_type( + File "/home/jovyan/machine-learning/.venv/lib/python3.11/site-packages/ray/_private/node.py", line 1458, in _kill_process_type + self._kill_process_impl( + File "/home/jovyan/machine-learning/.venv/lib/python3.11/site-packages/ray/_private/node.py", line 1514, in _kill_process_impl + process.wait(timeout_seconds) + File "/usr/lib/python3.11/subprocess.py", line 1277, in wait + self._wait(timeout=sigint_timeout) + File "/usr/lib/python3.11/subprocess.py", line 2025, in _wait + if self._waitpid_lock.acquire(False): + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +KeyboardInterrupt: + File "/home/jovyan/machine-learning/preprocessing/mask_generator.py", line 46 + folders = [ + ^ +SyntaxError: '[' was never closed + + 0%| | 0/87 [00:00 + main() + File "/home/jovyan/machine-learning/preprocessing/tiler.py", line 88, in main + train_slides_df, train_tiles_df = tiling( + ^^^^^^^ + File "/home/jovyan/machine-learning/.venv/lib/python3.11/site-packages/rationai/tiling/tiling.py", line 107, in tiling + ready, pending = ray.wait(pending) + ^^^^^^^^^^^^^^^^^ + File "/home/jovyan/machine-learning/.venv/lib/python3.11/site-packages/ray/_private/auto_init_hook.py", line 21, in auto_init_wrapper + return fn(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^ + File "/home/jovyan/machine-learning/.venv/lib/python3.11/site-packages/ray/_private/client_mode_hook.py", line 103, in wrapper + return func(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^ + File "/home/jovyan/machine-learning/.venv/lib/python3.11/site-packages/ray/_private/worker.py", line 2976, in wait +object address : 0x70c88736d240 +object refcount : 4 +object type : 0x9d49e0 +object type name: KeyboardInterrupt +object repr : KeyboardInterrupt() +lost sys.stderr +Exception ignored in atexit callback: +Exception ignored in sys.unraisablehook: +KeyboardInterrupt + + 0%| | 0/655 [00:00`_ for more details. + warnings.warn( +/home/jovyan/machine-learning/.venv/lib/python3.11/site-packages/mlflow/types/utils.py:407: UserWarning: Hint: Inferred schema contains integer column(s). Integer columns in Python cannot represent missing values. If your input data contains missing values at inference time, it will be encoded as floats and will cause a schema enforcement error. The best way to avoid this problem is to infer the model schema based on a realistic data sample (training dataset) that includes missing values. Alternatively, you can declare integer columns as doubles (float64) whenever these columns may have missing values. See `Handling Integers With Missing Values `_ for more details. + warnings.warn( +/home/jovyan/machine-learning/.venv/lib/python3.11/site-packages/mlflow/types/utils.py:407: UserWarning: Hint: Inferred schema contains integer column(s). Integer columns in Python cannot represent missing values. If your input data contains missing values at inference time, it will be encoded as floats and will cause a schema enforcement error. The best way to avoid this problem is to infer the model schema based on a realistic data sample (training dataset) that includes missing values. Alternatively, you can declare integer columns as doubles (float64) whenever these columns may have missing values. See `Handling Integers With Missing Values `_ for more details. + warnings.warn( + 0%| | 0/73 [00:00`_ for more details. + warnings.warn( +/home/jovyan/machine-learning/.venv/lib/python3.11/site-packages/mlflow/types/utils.py:407: UserWarning: Hint: Inferred schema contains integer column(s). Integer columns in Python cannot represent missing values. If your input data contains missing values at inference time, it will be encoded as floats and will cause a schema enforcement error. The best way to avoid this problem is to infer the model schema based on a realistic data sample (training dataset) that includes missing values. Alternatively, you can declare integer columns as doubles (float64) whenever these columns may have missing values. See `Handling Integers With Missing Values `_ for more details. + warnings.warn( +/home/jovyan/machine-learning/.venv/lib/python3.11/site-packages/mlflow/types/utils.py:407: UserWarning: Hint: Inferred schema contains integer column(s). Integer columns in Python cannot represent missing values. If your input data contains missing values at inference time, it will be encoded as floats and will cause a schema enforcement error. The best way to avoid this problem is to infer the model schema based on a realistic data sample (training dataset) that includes missing values. Alternatively, you can declare integer columns as doubles (float64) whenever these columns may have missing values. See `Handling Integers With Missing Values `_ for more details. + warnings.warn( diff --git a/pdm.lock b/pdm.lock index 6420616..395416d 100644 --- a/pdm.lock +++ b/pdm.lock @@ -2,37 +2,208 @@ # It is not intended for manual editing. [metadata] -groups = ["default", "dev", "docs", "test"] -strategy = ["inherit_metadata"] -lock_version = "4.5.0" -content_hash = "sha256:3f01d03ad0d47f602c987ef86d96b65b42cd6b8199430d0f41138b17f05995fc" +groups = ["default", "dev"] +strategy = [] +lock_version = "4.4" +content_hash = "sha256:24e67ec88809826b854439f3edd657a4285070b448d3ae0add27a80782d3b633" -[[metadata.targets]] -requires_python = ">=3.11" +[[package]] +name = "aiohappyeyeballs" +version = "2.3.6" +requires_python = ">=3.8" +summary = "Happy Eyeballs for asyncio" +files = [ + {file = "aiohappyeyeballs-2.3.6-py3-none-any.whl", hash = "sha256:15dca2611fa78442f1cb54cf07ffb998573f2b4fbeab45ca8554c045665c896b"}, + {file = "aiohappyeyeballs-2.3.6.tar.gz", hash = "sha256:88211068d2a40e0436033956d7de3926ff36d54776f8b1022d6b21320cadae79"}, +] [[package]] -name = "babel" -version = "2.16.0" +name = "aiohttp" +version = "3.10.3" +requires_python = ">=3.8" +summary = "Async http client/server framework (asyncio)" +dependencies = [ + "aiohappyeyeballs>=2.3.0", + "aiosignal>=1.1.2", + "attrs>=17.3.0", + "frozenlist>=1.1.1", + "multidict<7.0,>=4.5", + "yarl<2.0,>=1.0", +] +files = [ + {file = "aiohttp-3.10.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e021c4c778644e8cdc09487d65564265e6b149896a17d7c0f52e9a088cc44e1b"}, + {file = "aiohttp-3.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:24fade6dae446b183e2410a8628b80df9b7a42205c6bfc2eff783cbeedc224a2"}, + {file = "aiohttp-3.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bc8e9f15939dacb0e1f2d15f9c41b786051c10472c7a926f5771e99b49a5957f"}, + {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5a9ec959b5381271c8ec9310aae1713b2aec29efa32e232e5ef7dcca0df0279"}, + {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a5d0ea8a6467b15d53b00c4e8ea8811e47c3cc1bdbc62b1aceb3076403d551f"}, + {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9ed607dbbdd0d4d39b597e5bf6b0d40d844dfb0ac6a123ed79042ef08c1f87e"}, + {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e66d5b506832e56add66af88c288c1d5ba0c38b535a1a59e436b300b57b23e"}, + {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fda91ad797e4914cca0afa8b6cccd5d2b3569ccc88731be202f6adce39503189"}, + {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:61ccb867b2f2f53df6598eb2a93329b5eee0b00646ee79ea67d68844747a418e"}, + {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6d881353264e6156f215b3cb778c9ac3184f5465c2ece5e6fce82e68946868ef"}, + {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b031ce229114825f49cec4434fa844ccb5225e266c3e146cb4bdd025a6da52f1"}, + {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5337cc742a03f9e3213b097abff8781f79de7190bbfaa987bd2b7ceb5bb0bdec"}, + {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab3361159fd3dcd0e48bbe804006d5cfb074b382666e6c064112056eb234f1a9"}, + {file = "aiohttp-3.10.3-cp311-cp311-win32.whl", hash = "sha256:05d66203a530209cbe40f102ebaac0b2214aba2a33c075d0bf825987c36f1f0b"}, + {file = "aiohttp-3.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:70b4a4984a70a2322b70e088d654528129783ac1ebbf7dd76627b3bd22db2f17"}, + {file = "aiohttp-3.10.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:166de65e2e4e63357cfa8417cf952a519ac42f1654cb2d43ed76899e2319b1ee"}, + {file = "aiohttp-3.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7084876352ba3833d5d214e02b32d794e3fd9cf21fdba99cff5acabeb90d9806"}, + {file = "aiohttp-3.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d98c604c93403288591d7d6d7d6cc8a63459168f8846aeffd5b3a7f3b3e5e09"}, + {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d73b073a25a0bb8bf014345374fe2d0f63681ab5da4c22f9d2025ca3e3ea54fc"}, + {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8da6b48c20ce78f5721068f383e0e113dde034e868f1b2f5ee7cb1e95f91db57"}, + {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a9dcdccf50284b1b0dc72bc57e5bbd3cc9bf019060dfa0668f63241ccc16aa7"}, + {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56fb94bae2be58f68d000d046172d8b8e6b1b571eb02ceee5535e9633dcd559c"}, + {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf75716377aad2c718cdf66451c5cf02042085d84522aec1f9246d3e4b8641a6"}, + {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6c51ed03e19c885c8e91f574e4bbe7381793f56f93229731597e4a499ffef2a5"}, + {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b84857b66fa6510a163bb083c1199d1ee091a40163cfcbbd0642495fed096204"}, + {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c124b9206b1befe0491f48185fd30a0dd51b0f4e0e7e43ac1236066215aff272"}, + {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3461d9294941937f07bbbaa6227ba799bc71cc3b22c40222568dc1cca5118f68"}, + {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:08bd0754d257b2db27d6bab208c74601df6f21bfe4cb2ec7b258ba691aac64b3"}, + {file = "aiohttp-3.10.3-cp312-cp312-win32.whl", hash = "sha256:7f9159ae530297f61a00116771e57516f89a3de6ba33f314402e41560872b50a"}, + {file = "aiohttp-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:e1128c5d3a466279cb23c4aa32a0f6cb0e7d2961e74e9e421f90e74f75ec1edf"}, + {file = "aiohttp-3.10.3.tar.gz", hash = "sha256:21650e7032cc2d31fc23d353d7123e771354f2a3d5b05a5647fc30fea214e696"}, +] + +[[package]] +name = "aiosignal" +version = "1.3.1" +requires_python = ">=3.7" +summary = "aiosignal: a list of registered asynchronous callbacks" +dependencies = [ + "frozenlist>=1.1.0", +] +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[[package]] +name = "albucore" +version = "0.0.23" +requires_python = ">=3.9" +summary = "High-performance image processing functions for deep learning and computer vision." +dependencies = [ + "numpy>=1.24.4", + "opencv-python-headless>=4.9.0.80", + "simsimd>=5.9.2", + "stringzilla>=3.10.4", +] +files = [ + {file = "albucore-0.0.23-py3-none-any.whl", hash = "sha256:99274ac0c15a1a7d9a726df9d54d5ab70d9d0c189e2a935399dba3d4bafad415"}, + {file = "albucore-0.0.23.tar.gz", hash = "sha256:57823982b954913b84a9e2cf71058c4577b02397a62c41885be2d9b295efa8ab"}, +] + +[[package]] +name = "albumentations" +version = "2.0.4" +requires_python = ">=3.9" +summary = "Fast, flexible, and advanced augmentation library for deep learning, computer vision, and medical imaging. Albumentations offers a wide range of transformations for both 2D (images, masks, bboxes, keypoints) and 3D (volumes, volumetric masks) data, with optimized performance and seamless integration into ML workflows." +dependencies = [ + "PyYAML", + "albucore==0.0.23", + "numpy>=1.24.4", + "opencv-python-headless>=4.9.0.80", + "pydantic>=2.9.2", + "scipy>=1.10.0", +] +files = [ + {file = "albumentations-2.0.4-py3-none-any.whl", hash = "sha256:1d60b62527d34bb8791fad90f6faeeb065ecc5cb4577ff85614f022d56183010"}, + {file = "albumentations-2.0.4.tar.gz", hash = "sha256:4f0cd22576fba8030fc99f1ccacee1452a1880ac2a9ce0882f0d93e5477d5c70"}, +] + +[[package]] +name = "alembic" +version = "1.13.2" requires_python = ">=3.8" -summary = "Internationalization utilities" -groups = ["docs"] +summary = "A database migration tool for SQLAlchemy." dependencies = [ - "pytz>=2015.7; python_version < \"3.9\"", + "Mako", + "SQLAlchemy>=1.3.0", + "typing-extensions>=4", ] files = [ - {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, - {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, + {file = "alembic-1.13.2-py3-none-any.whl", hash = "sha256:6b8733129a6224a9a711e17c99b08462dbf7cc9670ba8f2e2ae9af860ceb1953"}, + {file = "alembic-1.13.2.tar.gz", hash = "sha256:1ff0ae32975f4fd96028c39ed9bb3c867fe3af956bd7bb37343b54c9fe7445ef"}, +] + +[[package]] +name = "aniso8601" +version = "9.0.1" +summary = "A library for parsing ISO 8601 strings." +files = [ + {file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"}, + {file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"}, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +requires_python = ">=3.8" +summary = "Reusable constraint types to use with typing.Annotated" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "antlr4-python3-runtime" +version = "4.9.3" +summary = "ANTLR 4.9.3 runtime for Python 3.7" +files = [ + {file = "antlr4-python3-runtime-4.9.3.tar.gz", hash = "sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b"}, +] + +[[package]] +name = "attrs" +version = "24.2.0" +requires_python = ">=3.7" +summary = "Classes Without Boilerplate" +files = [ + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, +] + +[[package]] +name = "blinker" +version = "1.8.2" +requires_python = ">=3.8" +summary = "Fast, simple object-to-object and broadcast signaling" +files = [ + {file = "blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01"}, + {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"}, +] + +[[package]] +name = "cachetools" +version = "5.5.0" +requires_python = ">=3.7" +summary = "Extensible memoizing collections and decorators" +files = [ + {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, + {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, ] [[package]] name = "certifi" -version = "2024.7.4" +version = "2024.8.30" requires_python = ">=3.6" summary = "Python package for providing Mozilla's CA Bundle." -groups = ["docs"] files = [ - {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, - {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, +] + +[[package]] +name = "cffi" +version = "1.17.1" +requires_python = ">=3.8" +summary = "Foreign Function Interface for Python calling C code." +dependencies = [ + "pycparser", +] +files = [ + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, ] [[package]] @@ -40,7 +211,6 @@ name = "cfgv" version = "3.4.0" requires_python = ">=3.8" summary = "Validate configuration and produce human readable error messages." -groups = ["dev"] files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -51,7 +221,6 @@ name = "charset-normalizer" version = "3.3.2" requires_python = ">=3.7.0" summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -groups = ["docs"] files = [ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, @@ -92,179 +261,388 @@ name = "click" version = "8.1.7" requires_python = ">=3.7" summary = "Composable command line interface toolkit" -groups = ["docs"] -dependencies = [ - "colorama; platform_system == \"Windows\"", - "importlib-metadata; python_version < \"3.8\"", -] files = [ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] [[package]] -name = "colorama" -version = "0.4.6" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -summary = "Cross-platform colored terminal text." -groups = ["docs", "test"] +name = "cloudpickle" +version = "3.0.0" +requires_python = ">=3.8" +summary = "Pickler class to extend the standard pickle.Pickler functionality" files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, + {file = "cloudpickle-3.0.0-py3-none-any.whl", hash = "sha256:246ee7d0c295602a036e86369c77fecda4ab17b506496730f2f576d9016fd9c7"}, + {file = "cloudpickle-3.0.0.tar.gz", hash = "sha256:996d9a482c6fb4f33c1a35335cf8afd065d2a56e973270364840712d9131a882"}, ] [[package]] -name = "coverage" -version = "7.6.1" -requires_python = ">=3.8" -summary = "Code coverage measurement for Python" -groups = ["test"] -files = [ - {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, - {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, - {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, - {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, - {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, - {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, - {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, - {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, - {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, - {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, - {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, - {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, - {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, - {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, - {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, - {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, - {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, -] - -[[package]] -name = "coverage" -version = "7.6.1" -extras = ["toml"] +name = "contourpy" +version = "1.3.0" +requires_python = ">=3.9" +summary = "Python library for calculating contours of 2D quadrilateral grids" +dependencies = [ + "numpy>=1.23", +] +files = [ + {file = "contourpy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fa4c02abe6c446ba70d96ece336e621efa4aecae43eaa9b030ae5fb92b309ad"}, + {file = "contourpy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:834e0cfe17ba12f79963861e0f908556b2cedd52e1f75e6578801febcc6a9f49"}, + {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbc4c3217eee163fa3984fd1567632b48d6dfd29216da3ded3d7b844a8014a66"}, + {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4865cd1d419e0c7a7bf6de1777b185eebdc51470800a9f42b9e9decf17762081"}, + {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:303c252947ab4b14c08afeb52375b26781ccd6a5ccd81abcdfc1fafd14cf93c1"}, + {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d"}, + {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76a896b2f195b57db25d6b44e7e03f221d32fe318d03ede41f8b4d9ba1bff53c"}, + {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e1fd23e9d01591bab45546c089ae89d926917a66dceb3abcf01f6105d927e2cb"}, + {file = "contourpy-1.3.0-cp311-cp311-win32.whl", hash = "sha256:d402880b84df3bec6eab53cd0cf802cae6a2ef9537e70cf75e91618a3801c20c"}, + {file = "contourpy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:6cb6cc968059db9c62cb35fbf70248f40994dfcd7aa10444bbf8b3faeb7c2d67"}, + {file = "contourpy-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:570ef7cf892f0afbe5b2ee410c507ce12e15a5fa91017a0009f79f7d93a1268f"}, + {file = "contourpy-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:da84c537cb8b97d153e9fb208c221c45605f73147bd4cadd23bdae915042aad6"}, + {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0be4d8425bfa755e0fd76ee1e019636ccc7c29f77a7c86b4328a9eb6a26d0639"}, + {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c0da700bf58f6e0b65312d0a5e695179a71d0163957fa381bb3c1f72972537c"}, + {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb8b141bb00fa977d9122636b16aa67d37fd40a3d8b52dd837e536d64b9a4d06"}, + {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3634b5385c6716c258d0419c46d05c8aa7dc8cb70326c9a4fb66b69ad2b52e09"}, + {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0dce35502151b6bd35027ac39ba6e5a44be13a68f55735c3612c568cac3805fd"}, + {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea348f053c645100612b333adc5983d87be69acdc6d77d3169c090d3b01dc35"}, + {file = "contourpy-1.3.0-cp312-cp312-win32.whl", hash = "sha256:90f73a5116ad1ba7174341ef3ea5c3150ddf20b024b98fb0c3b29034752c8aeb"}, + {file = "contourpy-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:b11b39aea6be6764f84360fce6c82211a9db32a7c7de8fa6dd5397cf1d079c3b"}, + {file = "contourpy-1.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3e1c7fa44aaae40a2247e2e8e0627f4bea3dd257014764aa644f319a5f8600e3"}, + {file = "contourpy-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:364174c2a76057feef647c802652f00953b575723062560498dc7930fc9b1cb7"}, + {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32b238b3b3b649e09ce9aaf51f0c261d38644bdfa35cbaf7b263457850957a84"}, + {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d51fca85f9f7ad0b65b4b9fe800406d0d77017d7270d31ec3fb1cc07358fdea0"}, + {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:732896af21716b29ab3e988d4ce14bc5133733b85956316fb0c56355f398099b"}, + {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d73f659398a0904e125280836ae6f88ba9b178b2fed6884f3b1f95b989d2c8da"}, + {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c6c7c2408b7048082932cf4e641fa3b8ca848259212f51c8c59c45aa7ac18f14"}, + {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f317576606de89da6b7e0861cf6061f6146ead3528acabff9236458a6ba467f8"}, + {file = "contourpy-1.3.0-cp313-cp313-win32.whl", hash = "sha256:31cd3a85dbdf1fc002280c65caa7e2b5f65e4a973fcdf70dd2fdcb9868069294"}, + {file = "contourpy-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:4553c421929ec95fb07b3aaca0fae668b2eb5a5203d1217ca7c34c063c53d087"}, + {file = "contourpy-1.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:345af746d7766821d05d72cb8f3845dfd08dd137101a2cb9b24de277d716def8"}, + {file = "contourpy-1.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3bb3808858a9dc68f6f03d319acd5f1b8a337e6cdda197f02f4b8ff67ad2057b"}, + {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:420d39daa61aab1221567b42eecb01112908b2cab7f1b4106a52caaec8d36973"}, + {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d63ee447261e963af02642ffcb864e5a2ee4cbfd78080657a9880b8b1868e18"}, + {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:167d6c890815e1dac9536dca00828b445d5d0df4d6a8c6adb4a7ec3166812fa8"}, + {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:710a26b3dc80c0e4febf04555de66f5fd17e9cf7170a7b08000601a10570bda6"}, + {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:75ee7cb1a14c617f34a51d11fa7524173e56551646828353c4af859c56b766e2"}, + {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927"}, + {file = "contourpy-1.3.0.tar.gz", hash = "sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4"}, +] + +[[package]] +name = "cycler" +version = "0.12.1" requires_python = ">=3.8" -summary = "Code coverage measurement for Python" -groups = ["test"] -dependencies = [ - "coverage==7.6.1", - "tomli; python_full_version <= \"3.11.0a6\"", -] -files = [ - {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, - {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, - {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, - {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, - {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, - {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, - {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, - {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, - {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, - {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, - {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, - {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, - {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, - {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, - {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, - {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, - {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, +summary = "Composable style cycles" +files = [ + {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, + {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, +] + +[[package]] +name = "databricks-sdk" +version = "0.31.1" +requires_python = ">=3.7" +summary = "Databricks SDK for Python (Beta)" +dependencies = [ + "google-auth~=2.0", + "requests<3,>=2.28.1", +] +files = [ + {file = "databricks_sdk-0.31.1-py3-none-any.whl", hash = "sha256:9ab286f87ae1cc98a00ef7d207e40661f4d14a464071425ad169d235919b35f6"}, + {file = "databricks_sdk-0.31.1.tar.gz", hash = "sha256:8609e655d0e5ecb15c2a8a6468e737f8dcb4f28c33239388de3ab386b921d790"}, +] + +[[package]] +name = "deprecated" +version = "1.2.14" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +summary = "Python @deprecated decorator to deprecate old python classes, functions or methods." +dependencies = [ + "wrapt<2,>=1.10", +] +files = [ + {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, + {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, ] [[package]] name = "distlib" version = "0.3.8" summary = "Distribution utilities" -groups = ["dev"] files = [ {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] +[[package]] +name = "docker" +version = "7.1.0" +requires_python = ">=3.8" +summary = "A Python library for the Docker Engine API." +dependencies = [ + "requests>=2.26.0", + "urllib3>=1.26.0", +] +files = [ + {file = "docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0"}, + {file = "docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c"}, +] + +[[package]] +name = "durationpy" +version = "0.9" +summary = "Module for converting between datetime.timedelta and Go's Duration strings." +files = [ + {file = "durationpy-0.9-py3-none-any.whl", hash = "sha256:e65359a7af5cedad07fb77a2dd3f390f8eb0b74cb845589fa6c057086834dd38"}, + {file = "durationpy-0.9.tar.gz", hash = "sha256:fd3feb0a69a0057d582ef643c355c40d2fa1c942191f914d12203b1a01ac722a"}, +] + [[package]] name = "filelock" version = "3.15.4" requires_python = ">=3.8" summary = "A platform independent file lock." -groups = ["dev"] files = [ {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, ] [[package]] -name = "ghp-import" -version = "2.1.0" -summary = "Copy your docs directly to the gh-pages branch." -groups = ["docs"] +name = "flask" +version = "3.0.3" +requires_python = ">=3.8" +summary = "A simple framework for building complex web applications." dependencies = [ - "python-dateutil>=2.8.1", + "Jinja2>=3.1.2", + "Werkzeug>=3.0.0", + "blinker>=1.6.2", + "click>=8.1.3", + "itsdangerous>=2.1.2", +] +files = [ + {file = "flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3"}, + {file = "flask-3.0.3.tar.gz", hash = "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842"}, +] + +[[package]] +name = "fonttools" +version = "4.53.1" +requires_python = ">=3.8" +summary = "Tools to manipulate font files" +files = [ + {file = "fonttools-4.53.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da33440b1413bad53a8674393c5d29ce64d8c1a15ef8a77c642ffd900d07bfe1"}, + {file = "fonttools-4.53.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ff7e5e9bad94e3a70c5cd2fa27f20b9bb9385e10cddab567b85ce5d306ea923"}, + {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6e7170d675d12eac12ad1a981d90f118c06cf680b42a2d74c6c931e54b50719"}, + {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee32ea8765e859670c4447b0817514ca79054463b6b79784b08a8df3a4d78e3"}, + {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6e08f572625a1ee682115223eabebc4c6a2035a6917eac6f60350aba297ccadb"}, + {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b21952c092ffd827504de7e66b62aba26fdb5f9d1e435c52477e6486e9d128b2"}, + {file = "fonttools-4.53.1-cp311-cp311-win32.whl", hash = "sha256:9dfdae43b7996af46ff9da520998a32b105c7f098aeea06b2226b30e74fbba88"}, + {file = "fonttools-4.53.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4d0096cb1ac7a77b3b41cd78c9b6bc4a400550e21dc7a92f2b5ab53ed74eb02"}, + {file = "fonttools-4.53.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d92d3c2a1b39631a6131c2fa25b5406855f97969b068e7e08413325bc0afba58"}, + {file = "fonttools-4.53.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b3c8ebafbee8d9002bd8f1195d09ed2bd9ff134ddec37ee8f6a6375e6a4f0e8"}, + {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32f029c095ad66c425b0ee85553d0dc326d45d7059dbc227330fc29b43e8ba60"}, + {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f5e6c3510b79ea27bb1ebfcc67048cde9ec67afa87c7dd7efa5c700491ac7f"}, + {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f677ce218976496a587ab17140da141557beb91d2a5c1a14212c994093f2eae2"}, + {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9e6ceba2a01b448e36754983d376064730690401da1dd104ddb543519470a15f"}, + {file = "fonttools-4.53.1-cp312-cp312-win32.whl", hash = "sha256:791b31ebbc05197d7aa096bbc7bd76d591f05905d2fd908bf103af4488e60670"}, + {file = "fonttools-4.53.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ed170b5e17da0264b9f6fae86073be3db15fa1bd74061c8331022bca6d09bab"}, + {file = "fonttools-4.53.1-py3-none-any.whl", hash = "sha256:f1f8758a2ad110bd6432203a344269f445a2907dc24ef6bccfd0ac4e14e0d71d"}, + {file = "fonttools-4.53.1.tar.gz", hash = "sha256:e128778a8e9bc11159ce5447f76766cefbd876f44bd79aff030287254e4752c4"}, +] + +[[package]] +name = "frozenlist" +version = "1.4.1" +requires_python = ">=3.8" +summary = "A list-like structure which implements collections.abc.MutableSequence" +files = [ + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, + {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, + {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, + {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, + {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, + {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, + {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, ] + +[[package]] +name = "fsspec" +version = "2024.6.1" +requires_python = ">=3.8" +summary = "File-system specification" files = [ - {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, - {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, + {file = "fsspec-2024.6.1-py3-none-any.whl", hash = "sha256:3cb443f8bcd2efb31295a5b9fdb02aee81d8452c80d28f97a6d0959e6cee101e"}, + {file = "fsspec-2024.6.1.tar.gz", hash = "sha256:fad7d7e209dd4c1208e3bbfda706620e0da5142bebbd9c384afb95b07e798e49"}, ] [[package]] -name = "griffe" -version = "1.2.0" +name = "fsspec" +version = "2024.6.1" +extras = ["http"] requires_python = ">=3.8" -summary = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." -groups = ["docs"] +summary = "File-system specification" dependencies = [ - "astunparse>=1.6; python_version < \"3.9\"", - "colorama>=0.4", + "aiohttp!=4.0.0a0,!=4.0.0a1", + "fsspec==2024.6.1", ] files = [ - {file = "griffe-1.2.0-py3-none-any.whl", hash = "sha256:a8b2fcb1ecdc5a412e646b0b4375eb20a5d2eac3a11dd8c10c56967a4097663c"}, - {file = "griffe-1.2.0.tar.gz", hash = "sha256:1c9f6ef7455930f3f9b0c4145a961c90385d1e2cbc496f7796fbff560ec60d31"}, + {file = "fsspec-2024.6.1-py3-none-any.whl", hash = "sha256:3cb443f8bcd2efb31295a5b9fdb02aee81d8452c80d28f97a6d0959e6cee101e"}, + {file = "fsspec-2024.6.1.tar.gz", hash = "sha256:fad7d7e209dd4c1208e3bbfda706620e0da5142bebbd9c384afb95b07e798e49"}, +] + +[[package]] +name = "gitdb" +version = "4.0.11" +requires_python = ">=3.7" +summary = "Git Object Database" +dependencies = [ + "smmap<6,>=3.0.1", +] +files = [ + {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, + {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, +] + +[[package]] +name = "gitpython" +version = "3.1.43" +requires_python = ">=3.7" +summary = "GitPython is a Python library used to interact with Git repositories" +dependencies = [ + "gitdb<5,>=4.0.1", +] +files = [ + {file = "GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff"}, + {file = "GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c"}, +] + +[[package]] +name = "google-auth" +version = "2.34.0" +requires_python = ">=3.7" +summary = "Google Authentication Library" +dependencies = [ + "cachetools<6.0,>=2.0.0", + "pyasn1-modules>=0.2.1", + "rsa<5,>=3.1.4", +] +files = [ + {file = "google_auth-2.34.0-py2.py3-none-any.whl", hash = "sha256:72fd4733b80b6d777dcde515628a9eb4a577339437012874ea286bca7261ee65"}, + {file = "google_auth-2.34.0.tar.gz", hash = "sha256:8eb87396435c19b20d32abd2f984e31c191a15284af72eb922f10e5bde9c04cc"}, +] + +[[package]] +name = "graphene" +version = "3.3" +summary = "GraphQL Framework for Python" +dependencies = [ + "aniso8601<10,>=8", + "graphql-core<3.3,>=3.1", + "graphql-relay<3.3,>=3.1", +] +files = [ + {file = "graphene-3.3-py2.py3-none-any.whl", hash = "sha256:bb3810be33b54cb3e6969506671eb72319e8d7ba0d5ca9c8066472f75bf35a38"}, + {file = "graphene-3.3.tar.gz", hash = "sha256:529bf40c2a698954217d3713c6041d69d3f719ad0080857d7ee31327112446b0"}, +] + +[[package]] +name = "graphql-core" +version = "3.2.3" +requires_python = ">=3.6,<4" +summary = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL." +files = [ + {file = "graphql-core-3.2.3.tar.gz", hash = "sha256:06d2aad0ac723e35b1cb47885d3e5c45e956a53bc1b209a9fc5369007fe46676"}, + {file = "graphql_core-3.2.3-py3-none-any.whl", hash = "sha256:5766780452bd5ec8ba133f8bf287dc92713e3868ddd83aee4faab9fc3e303dc3"}, +] + +[[package]] +name = "graphql-relay" +version = "3.2.0" +requires_python = ">=3.6,<4" +summary = "Relay library for graphql-core" +dependencies = [ + "graphql-core<3.3,>=3.2", +] +files = [ + {file = "graphql-relay-3.2.0.tar.gz", hash = "sha256:1ff1c51298356e481a0be009ccdff249832ce53f30559c1338f22a0e0d17250c"}, + {file = "graphql_relay-3.2.0-py3-none-any.whl", hash = "sha256:c9b22bd28b170ba1fe674c74384a8ff30a76c8e26f88ac3aa1584dd3179953e5"}, +] + +[[package]] +name = "greenlet" +version = "3.0.3" +requires_python = ">=3.7" +summary = "Lightweight in-process concurrent programming" +files = [ + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[[package]] +name = "gunicorn" +version = "23.0.0" +requires_python = ">=3.7" +summary = "WSGI HTTP Server for UNIX" +dependencies = [ + "packaging", +] +files = [ + {file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"}, + {file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"}, +] + +[[package]] +name = "hydra-core" +version = "1.3.2" +summary = "A framework for elegantly configuring complex applications" +dependencies = [ + "antlr4-python3-runtime==4.9.*", + "omegaconf<2.4,>=2.2", + "packaging", +] +files = [ + {file = "hydra-core-1.3.2.tar.gz", hash = "sha256:8a878ed67216997c3e9d88a8e72e7b4767e81af37afb4ea3334b269a4390a824"}, + {file = "hydra_core-1.3.2-py3-none-any.whl", hash = "sha256:fa0238a9e31df3373b35b0bfb672c34cc92718d21f81311d8996a16de1141d8b"}, ] [[package]] @@ -272,7 +650,6 @@ name = "identify" version = "2.6.0" requires_python = ">=3.8" summary = "File identification library for Python" -groups = ["dev"] files = [ {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, @@ -280,39 +657,35 @@ files = [ [[package]] name = "idna" -version = "3.8" -requires_python = ">=3.6" +version = "3.7" +requires_python = ">=3.5" summary = "Internationalized Domain Names in Applications (IDNA)" -groups = ["docs"] files = [ - {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, - {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] [[package]] -name = "imageio" -version = "2.35.1" +name = "importlib-metadata" +version = "8.4.0" requires_python = ">=3.8" -summary = "Library for reading and writing a wide range of image, video, scientific, and volumetric data formats." -groups = ["default"] +summary = "Read metadata from Python packages" dependencies = [ - "numpy", - "pillow>=8.3.2", + "zipp>=0.5", ] files = [ - {file = "imageio-2.35.1-py3-none-any.whl", hash = "sha256:6eb2e5244e7a16b85c10b5c2fe0f7bf961b40fcb9f1a9fd1bd1d2c2f8fb3cd65"}, - {file = "imageio-2.35.1.tar.gz", hash = "sha256:4952dfeef3c3947957f6d5dedb1f4ca31c6e509a476891062396834048aeed2a"}, + {file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"}, + {file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"}, ] [[package]] -name = "iniconfig" -version = "2.0.0" -requires_python = ">=3.7" -summary = "brain-dead simple config-ini parsing" -groups = ["test"] +name = "itsdangerous" +version = "2.2.0" +requires_python = ">=3.8" +summary = "Safely pass data to untrusted environments and back." files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, + {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, + {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, ] [[package]] @@ -320,7 +693,6 @@ name = "jinja2" version = "3.1.4" requires_python = ">=3.7" summary = "A very fast and expressive template engine." -groups = ["docs"] dependencies = [ "MarkupSafe>=2.0", ] @@ -330,18 +702,156 @@ files = [ ] [[package]] -name = "lazy-loader" -version = "0.4" -requires_python = ">=3.7" -summary = "Makes it easy to load subpackages and functions on demand." -groups = ["default"] +name = "joblib" +version = "1.4.2" +requires_python = ">=3.8" +summary = "Lightweight pipelining with Python functions" +files = [ + {file = "joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6"}, + {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"}, +] + +[[package]] +name = "jsonschema" +version = "4.23.0" +requires_python = ">=3.8" +summary = "An implementation of JSON Schema validation for Python" dependencies = [ - "importlib-metadata; python_version < \"3.8\"", - "packaging", + "attrs>=22.2.0", + "jsonschema-specifications>=2023.03.6", + "referencing>=0.28.4", + "rpds-py>=0.7.1", +] +files = [ + {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, + {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, +] + +[[package]] +name = "jsonschema-specifications" +version = "2024.10.1" +requires_python = ">=3.9" +summary = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +dependencies = [ + "referencing>=0.31.0", ] files = [ - {file = "lazy_loader-0.4-py3-none-any.whl", hash = "sha256:342aa8e14d543a154047afb4ba8ef17f5563baad3fc610d7b15b213b0f119efc"}, - {file = "lazy_loader-0.4.tar.gz", hash = "sha256:47c75182589b91a4e1a85a136c074285a5ad4d9f39c63e0d7fb76391c4574cd1"}, + {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, + {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, +] + +[[package]] +name = "kiwisolver" +version = "1.4.6" +requires_python = ">=3.8" +summary = "A fast implementation of the Cassowary constraint solver" +files = [ + {file = "kiwisolver-1.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:45a5cb5abad1ad9c265eed7e058fefafeb7964565b93b397ba2f480faec8d674"}, + {file = "kiwisolver-1.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e52b2568c47fb4f54d17576954e02b1de156c85152f87283a99db9670fd18c0"}, + {file = "kiwisolver-1.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:00af95204100bc1d0f26e1ed52ec77d6e3da5c9b845c88d31875c164e4ba6c0c"}, + {file = "kiwisolver-1.4.6-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50ab1fedf86f3951a9e90a64edd15f598860ed60cd3664259756f097d527b5ae"}, + {file = "kiwisolver-1.4.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbc985766bf20141ce64baecc39fb9fedbce094b2b8de1bb62676b79328988e4"}, + {file = "kiwisolver-1.4.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1942a155c737a7c3835a957897f0cc9ebc0085b7a75d934d86aecb1b27b8873"}, + {file = "kiwisolver-1.4.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f464403e391724f8e7dff188d3fb77a85bd1273b3fdba182e6671abcc44434f8"}, + {file = "kiwisolver-1.4.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce5efe545eea86f52ec5a1185e5052815ea86778e8268bad71fa46433f7c0bef"}, + {file = "kiwisolver-1.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cb30165f71b7b3378668346e220c81d590593a3a1ff76428a53780310df03f35"}, + {file = "kiwisolver-1.4.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5a987f740e1c9964e614acb87ba1f014b4be760a341effc8dc789913d1840e6"}, + {file = "kiwisolver-1.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f2ceaa6d0450623d108956647ef19a1a28c7e07880f1171c932477308d44d80b"}, + {file = "kiwisolver-1.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:788cbf99738f18ae8a27b9d4d7314502b4b917005cfdacd1d6a59038332ae24d"}, + {file = "kiwisolver-1.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2f6668678a6b9488a7f8a6320e1b1c6396d179a976472dbc08d1600d04119511"}, + {file = "kiwisolver-1.4.6-cp311-cp311-win32.whl", hash = "sha256:10a09a3e4213c2806bcfd2eb4edb756c557973d2cacf06873b18a247fce897da"}, + {file = "kiwisolver-1.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:683ffef2c51fdc54112dc610d06b59b88c21e23fb669b905da6d5bec80da1bde"}, + {file = "kiwisolver-1.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:3b852c7f0ed9a2fd339c228829bca0964233ed45de50aae3e87b72ca37d177f8"}, + {file = "kiwisolver-1.4.6-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:979df7e9334f6a3694ee9be8d42817e519ef6d155a16499714d082cf41296852"}, + {file = "kiwisolver-1.4.6-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:50c9c6c42bb6ca231626d1182b9128e89c5ce3c64456f811ff0280deb42d7bfe"}, + {file = "kiwisolver-1.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ace86489e7951bd26329a589198d3875c3d48380f889c69d3eb254b506a80101"}, + {file = "kiwisolver-1.4.6-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f94771988da902b475f78e85cf63c5c94392773b4a6494234d87c1b363b2fbc5"}, + {file = "kiwisolver-1.4.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a62379eee430b1c477bb0a0bf6858a57c7c0dad9cee8b3144a5cb5d366c66a54"}, + {file = "kiwisolver-1.4.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e99b97d69499a7414572c906fbc7ca312519f2e17999730129f6c4492786e953"}, + {file = "kiwisolver-1.4.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab93f58afe3a02922a343189404f24ed885564e6316649790240124b95ef1d6e"}, + {file = "kiwisolver-1.4.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34105f4460ba50fc18a16a8e77a5122f7affe075628763fda748ad0ec534c3ee"}, + {file = "kiwisolver-1.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0081f85f0222620563409d4804c6567a930a45dafbe9674c7913fde131653992"}, + {file = "kiwisolver-1.4.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:df2a4a7cc2e01991e039a792457751b601bdf30143ab5f23f9a1e58f20c875f4"}, + {file = "kiwisolver-1.4.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1f401784df70ea2870e4e10adade66b5b06cb2c151bc2a8a414a1d10554e9a81"}, + {file = "kiwisolver-1.4.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:b19761c8c613b6d04c44f1a4797a144b44136f17ec009ccfb025e17b5698140c"}, + {file = "kiwisolver-1.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ee7289430ded484cc2eff9d8ffcce58ed7fe2c26919321dbc0580322a49e0120"}, + {file = "kiwisolver-1.4.6-cp312-cp312-win32.whl", hash = "sha256:331b9d9f408e874ecf34bd79b79df8e099f0b1b351b8844609c1bfdc8d2d45b2"}, + {file = "kiwisolver-1.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:a9be95d086578b3ada61a4621c0e7ee5f456820bfdccc3329061fdeae1e31179"}, + {file = "kiwisolver-1.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:773f2d87825779ab69196dfcf63e9d91043273421c6128c8d4ed82bc6316068f"}, + {file = "kiwisolver-1.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:140f376c22b5148453acff768cff19c34ebbd593126617018732ea1d9ce65547"}, + {file = "kiwisolver-1.4.6.tar.gz", hash = "sha256:3cda29d601445e6aa11f80d90a9b8c2ae501650c55d7ad29829bd44499c9e7e0"}, +] + +[[package]] +name = "kubernetes" +version = "32.0.1" +requires_python = ">=3.6" +summary = "Kubernetes python client" +dependencies = [ + "certifi>=14.05.14", + "durationpy>=0.7", + "google-auth>=1.0.1", + "oauthlib>=3.2.2", + "python-dateutil>=2.5.3", + "pyyaml>=5.4.1", + "requests", + "requests-oauthlib", + "six>=1.9.0", + "urllib3>=1.24.2", + "websocket-client!=0.40.0,!=0.41.*,!=0.42.*,>=0.32.0", +] +files = [ + {file = "kubernetes-32.0.1-py2.py3-none-any.whl", hash = "sha256:35282ab8493b938b08ab5526c7ce66588232df00ef5e1dbe88a419107dc10998"}, + {file = "kubernetes-32.0.1.tar.gz", hash = "sha256:42f43d49abd437ada79a79a16bd48a604d3471a117a8347e87db693f2ba0ba28"}, +] + +[[package]] +name = "lightning" +version = "2.4.0" +requires_python = ">=3.9" +summary = "The Deep Learning framework to train, deploy, and ship AI products Lightning fast." +dependencies = [ + "PyYAML<8.0,>=5.4", + "fsspec[http]<2026.0,>=2022.5.0", + "lightning-utilities<2.0,>=0.10.0", + "packaging<25.0,>=20.0", + "pytorch-lightning", + "torch<4.0,>=2.1.0", + "torchmetrics<3.0,>=0.7.0", + "tqdm<6.0,>=4.57.0", + "typing-extensions<6.0,>=4.4.0", +] +files = [ + {file = "lightning-2.4.0-py3-none-any.whl", hash = "sha256:560163af9711cf59055c448232c473150a299089efce0d2be3cc3288082d8768"}, + {file = "lightning-2.4.0.tar.gz", hash = "sha256:9156604cc56e4b2b603f34fa7f0fe5107375c8e6d85e74544b319a15faa9ed0e"}, +] + +[[package]] +name = "lightning-utilities" +version = "0.11.6" +requires_python = ">=3.8" +summary = "Lightning toolbox for across the our ecosystem." +dependencies = [ + "packaging>=17.1", + "setuptools", + "typing-extensions", +] +files = [ + {file = "lightning_utilities-0.11.6-py3-none-any.whl", hash = "sha256:ecd9953c316cbaf56ad820fbe7bd062187b9973c4a23d47b076cd59dc080a310"}, + {file = "lightning_utilities-0.11.6.tar.gz", hash = "sha256:79fc27ef8ec8b8d55a537920f2c7610270c0c9e037fa6efc78f1aa34ec8cdf04"}, +] + +[[package]] +name = "mako" +version = "1.3.5" +requires_python = ">=3.8" +summary = "A super-fast templating language that borrows the best ideas from the existing templating languages." +dependencies = [ + "MarkupSafe>=0.9.2", +] +files = [ + {file = "Mako-1.3.5-py3-none-any.whl", hash = "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a"}, + {file = "Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc"}, ] [[package]] @@ -349,10 +859,6 @@ name = "markdown" version = "3.7" requires_python = ">=3.8" summary = "Python implementation of John Gruber's Markdown." -groups = ["docs"] -dependencies = [ - "importlib-metadata>=4.4; python_version < \"3.10\"", -] files = [ {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, @@ -363,7 +869,6 @@ name = "markupsafe" version = "2.1.5" requires_python = ">=3.7" summary = "Safely add untrusted strings to HTML/XML markup." -groups = ["docs"] files = [ {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, @@ -389,269 +894,464 @@ files = [ ] [[package]] -name = "mergedeep" -version = "1.3.4" -requires_python = ">=3.6" -summary = "A deep merge function for 🐍." -groups = ["docs"] +name = "matplotlib" +version = "3.9.2" +requires_python = ">=3.9" +summary = "Python plotting package" +dependencies = [ + "contourpy>=1.0.1", + "cycler>=0.10", + "fonttools>=4.22.0", + "kiwisolver>=1.3.1", + "numpy>=1.23", + "packaging>=20.0", + "pillow>=8", + "pyparsing>=2.3.1", + "python-dateutil>=2.7", +] files = [ - {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, - {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, + {file = "matplotlib-3.9.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d8dd059447824eec055e829258ab092b56bb0579fc3164fa09c64f3acd478772"}, + {file = "matplotlib-3.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c797dac8bb9c7a3fd3382b16fe8f215b4cf0f22adccea36f1545a6d7be310b41"}, + {file = "matplotlib-3.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d719465db13267bcef19ea8954a971db03b9f48b4647e3860e4bc8e6ed86610f"}, + {file = "matplotlib-3.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8912ef7c2362f7193b5819d17dae8629b34a95c58603d781329712ada83f9447"}, + {file = "matplotlib-3.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7741f26a58a240f43bee74965c4882b6c93df3e7eb3de160126d8c8f53a6ae6e"}, + {file = "matplotlib-3.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:ae82a14dab96fbfad7965403c643cafe6515e386de723e498cf3eeb1e0b70cc7"}, + {file = "matplotlib-3.9.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ac43031375a65c3196bee99f6001e7fa5bdfb00ddf43379d3c0609bdca042df9"}, + {file = "matplotlib-3.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:be0fc24a5e4531ae4d8e858a1a548c1fe33b176bb13eff7f9d0d38ce5112a27d"}, + {file = "matplotlib-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf81de2926c2db243c9b2cbc3917619a0fc85796c6ba4e58f541df814bbf83c7"}, + {file = "matplotlib-3.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ee45bc4245533111ced13f1f2cace1e7f89d1c793390392a80c139d6cf0e6c"}, + {file = "matplotlib-3.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:306c8dfc73239f0e72ac50e5a9cf19cc4e8e331dd0c54f5e69ca8758550f1e1e"}, + {file = "matplotlib-3.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:5413401594cfaff0052f9d8b1aafc6d305b4bd7c4331dccd18f561ff7e1d3bd3"}, + {file = "matplotlib-3.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:18128cc08f0d3cfff10b76baa2f296fc28c4607368a8402de61bb3f2eb33c7d9"}, + {file = "matplotlib-3.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4876d7d40219e8ae8bb70f9263bcbe5714415acfdf781086601211335e24f8aa"}, + {file = "matplotlib-3.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d9f07a80deab4bb0b82858a9e9ad53d1382fd122be8cde11080f4e7dfedb38b"}, + {file = "matplotlib-3.9.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7c0410f181a531ec4e93bbc27692f2c71a15c2da16766f5ba9761e7ae518413"}, + {file = "matplotlib-3.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:909645cce2dc28b735674ce0931a4ac94e12f5b13f6bb0b5a5e65e7cea2c192b"}, + {file = "matplotlib-3.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:f32c7410c7f246838a77d6d1eff0c0f87f3cb0e7c4247aebea71a6d5a68cab49"}, + {file = "matplotlib-3.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:37e51dd1c2db16ede9cfd7b5cabdfc818b2c6397c83f8b10e0e797501c963a03"}, + {file = "matplotlib-3.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b82c5045cebcecd8496a4d694d43f9cc84aeeb49fe2133e036b207abe73f4d30"}, + {file = "matplotlib-3.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f053c40f94bc51bc03832a41b4f153d83f2062d88c72b5e79997072594e97e51"}, + {file = "matplotlib-3.9.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbe196377a8248972f5cede786d4c5508ed5f5ca4a1e09b44bda889958b33f8c"}, + {file = "matplotlib-3.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5816b1e1fe8c192cbc013f8f3e3368ac56fbecf02fb41b8f8559303f24c5015e"}, + {file = "matplotlib-3.9.2.tar.gz", hash = "sha256:96ab43906269ca64a6366934106fa01534454a69e471b7bf3d79083981aaab92"}, ] [[package]] -name = "mkdocs" -version = "1.6.0" +name = "mlflow" +version = "2.16.0" requires_python = ">=3.8" -summary = "Project documentation with Markdown." -groups = ["docs"] +summary = "MLflow is an open source platform for the complete machine learning lifecycle" dependencies = [ - "click>=7.0", - "colorama>=0.4; platform_system == \"Windows\"", - "ghp-import>=1.0", - "importlib-metadata>=4.4; python_version < \"3.10\"", - "jinja2>=2.11.1", - "markdown>=3.3.6", - "markupsafe>=2.0.1", - "mergedeep>=1.3.4", - "mkdocs-get-deps>=0.2.0", - "packaging>=20.5", - "pathspec>=0.11.1", - "pyyaml-env-tag>=0.1", - "pyyaml>=5.1", - "watchdog>=2.0", + "Flask<4", + "Jinja2<4,>=2.11; platform_system != \"Windows\"", + "alembic!=1.10.0,<2", + "docker<8,>=4.0.0", + "graphene<4", + "gunicorn<24; platform_system != \"Windows\"", + "markdown<4,>=3.3", + "matplotlib<4", + "mlflow-skinny==2.16.0", + "numpy<3", + "pandas<3", + "pyarrow<18,>=4.0.0", + "scikit-learn<2", + "scipy<2", + "sqlalchemy<3,>=1.4.0", ] files = [ - {file = "mkdocs-1.6.0-py3-none-any.whl", hash = "sha256:1eb5cb7676b7d89323e62b56235010216319217d4af5ddc543a91beb8d125ea7"}, - {file = "mkdocs-1.6.0.tar.gz", hash = "sha256:a73f735824ef83a4f3bcb7a231dcab23f5a838f88b7efc54a0eef5fbdbc3c512"}, + {file = "mlflow-2.16.0-py3-none-any.whl", hash = "sha256:9f27ef6ae7a82d7ecd67b6b4a4d50637a5e8160639115570fbc689758f9c0b54"}, + {file = "mlflow-2.16.0.tar.gz", hash = "sha256:82ea1a2e800f404f1586783b7636091c0a5754cf9ff45afeadf3a5e467f5168f"}, ] [[package]] -name = "mkdocs-autorefs" -version = "1.1.0" +name = "mlflow-skinny" +version = "2.16.0" requires_python = ">=3.8" -summary = "Automatically link across pages in MkDocs." -groups = ["docs"] +summary = "MLflow is an open source platform for the complete machine learning lifecycle" dependencies = [ - "Markdown>=3.3", - "markupsafe>=2.0.1", - "mkdocs>=1.1", + "cachetools<6,>=5.0.0", + "click<9,>=7.0", + "cloudpickle<4", + "databricks-sdk<1,>=0.20.0", + "gitpython<4,>=3.1.9", + "importlib-metadata!=4.7.0,<9,>=3.7.0", + "opentelemetry-api<3,>=1.9.0", + "opentelemetry-sdk<3,>=1.9.0", + "packaging<25", + "protobuf<6,>=3.12.0", + "pyyaml<7,>=5.1", + "requests<3,>=2.17.3", + "sqlparse<1,>=0.4.0", ] files = [ - {file = "mkdocs_autorefs-1.1.0-py3-none-any.whl", hash = "sha256:492ac42f50214e81565e968f8cb0df9aba9d981542b9e7121b8f8ae9407fe6eb"}, - {file = "mkdocs_autorefs-1.1.0.tar.gz", hash = "sha256:f2fd43b11f66284bd014f9b542a05c8ecbfaad4e0d7b30b68584788217b6c656"}, + {file = "mlflow_skinny-2.16.0-py3-none-any.whl", hash = "sha256:c55541f50efd0f6637377b10e8a654847a3fcd815b8680a95f02e0ca6bd7700c"}, + {file = "mlflow_skinny-2.16.0.tar.gz", hash = "sha256:9b823173063743783b4e7b6c52bdadcc7d9dab48eb883ac454c0d56609df6b2d"}, ] [[package]] -name = "mkdocs-get-deps" -version = "0.2.0" +name = "mpmath" +version = "1.3.0" +summary = "Python library for arbitrary-precision floating-point arithmetic" +files = [ + {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, + {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, +] + +[[package]] +name = "msgpack" +version = "1.1.0" requires_python = ">=3.8" -summary = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" -groups = ["docs"] -dependencies = [ - "importlib-metadata>=4.3; python_version < \"3.10\"", - "mergedeep>=1.3.4", - "platformdirs>=2.2.0", - "pyyaml>=5.1", +summary = "MessagePack serializer" +files = [ + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59"}, + {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"}, ] + +[[package]] +name = "multidict" +version = "6.0.5" +requires_python = ">=3.7" +summary = "multidict implementation" files = [ - {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, - {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, + {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, + {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, + {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, + {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, + {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, + {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, ] [[package]] -name = "mkdocs-material" -version = "9.5.33" +name = "mypy" +version = "1.11.1" requires_python = ">=3.8" -summary = "Documentation that simply works" -groups = ["docs"] +summary = "Optional static typing for Python" dependencies = [ - "babel~=2.10", - "colorama~=0.4", - "jinja2~=3.0", - "markdown~=3.2", - "mkdocs-material-extensions~=1.3", - "mkdocs~=1.6", - "paginate~=0.5", - "pygments~=2.16", - "pymdown-extensions~=10.2", - "regex>=2022.4", - "requests~=2.26", + "mypy-extensions>=1.0.0", + "typing-extensions>=4.6.0", ] files = [ - {file = "mkdocs_material-9.5.33-py3-none-any.whl", hash = "sha256:dbc79cf0fdc6e2c366aa987de8b0c9d4e2bb9f156e7466786ba2fd0f9bf7ffca"}, - {file = "mkdocs_material-9.5.33.tar.gz", hash = "sha256:d23a8b5e3243c9b2f29cdfe83051104a8024b767312dc8fde05ebe91ad55d89d"}, + {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, + {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, + {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, + {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, + {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, + {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, + {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, + {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, + {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, + {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, + {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, + {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, ] [[package]] -name = "mkdocs-material-extensions" -version = "1.3.1" -requires_python = ">=3.8" -summary = "Extension pack for Python Markdown and MkDocs Material." -groups = ["docs"] +name = "mypy-extensions" +version = "1.0.0" +requires_python = ">=3.5" +summary = "Type system extensions for programs checked with the mypy type checker." +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "networkx" +version = "3.3" +requires_python = ">=3.10" +summary = "Python package for creating and manipulating graphs and networks" +files = [ + {file = "networkx-3.3-py3-none-any.whl", hash = "sha256:28575580c6ebdaf4505b22c6256a2b9de86b316dc63ba9e93abde3d78dfdbcf2"}, + {file = "networkx-3.3.tar.gz", hash = "sha256:0c127d8b2f4865f59ae9cb8aafcd60b5c70f3241ebd66f7defad7c4ab90126c9"}, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +summary = "Node.js virtual environment builder" +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +] + +[[package]] +name = "numpy" +version = "1.26.4" +requires_python = ">=3.9" +summary = "Fundamental package for array computing in Python" +files = [ + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.4.5.8" +requires_python = ">=3" +summary = "CUBLAS native runtime libraries" +files = [ + {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2fc8da60df463fdefa81e323eef2e36489e1c94335b5358bcb38360adf75ac9b"}, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.4.127" +requires_python = ">=3" +summary = "CUDA profiling tools runtime libs." +files = [ + {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9dec60f5ac126f7bb551c055072b69d85392b13311fcc1bcda2202d172df30fb"}, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.4.127" +requires_python = ">=3" +summary = "NVRTC native runtime libraries" +files = [ + {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a178759ebb095827bd30ef56598ec182b85547f1508941a3d560eb7ea1fbf338"}, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.4.127" +requires_python = ">=3" +summary = "CUDA Runtime native Libraries" +files = [ + {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:64403288fa2136ee8e467cdc9c9427e0434110899d07c779f25b5c068934faa5"}, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.1.0.70" +requires_python = ">=3" +summary = "cuDNN runtime libraries" +dependencies = [ + "nvidia-cublas-cu12", +] +files = [ + {file = "nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f"}, + {file = "nvidia_cudnn_cu12-9.1.0.70-py3-none-win_amd64.whl", hash = "sha256:6278562929433d68365a07a4a1546c237ba2849852c0d4b2262a486e805b977a"}, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.2.1.3" +requires_python = ">=3" +summary = "CUFFT native runtime libraries" +files = [ + {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f083fc24912aa410be21fa16d157fed2055dab1cc4b6934a0e03cba69eb242b9"}, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.5.147" +requires_python = ">=3" +summary = "CURAND native runtime libraries" +files = [ + {file = "nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a88f583d4e0bb643c49743469964103aa59f7f708d862c3ddb0fc07f851e3b8b"}, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.6.1.9" +requires_python = ">=3" +summary = "CUDA solver native runtime libraries" +dependencies = [ + "nvidia-cublas-cu12", + "nvidia-cusparse-cu12", + "nvidia-nvjitlink-cu12", +] +files = [ + {file = "nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:19e33fa442bcfd085b3086c4ebf7e8debc07cfe01e11513cc6d332fd918ac260"}, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.3.1.170" +requires_python = ">=3" +summary = "CUSPARSE native runtime libraries" +dependencies = [ + "nvidia-nvjitlink-cu12", +] +files = [ + {file = "nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ea4f11a2904e2a8dc4b1833cc1b5181cde564edd0d5cd33e3c168eff2d1863f1"}, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.6.2" +summary = "NVIDIA cuSPARSELt" +files = [ + {file = "nvidia_cusparselt_cu12-0.6.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:df2c24502fd76ebafe7457dbc4716b2fec071aabaed4fb7691a201cde03704d9"}, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.21.5" +requires_python = ">=3" +summary = "NVIDIA Collective Communication Library (NCCL) Runtime" +files = [ + {file = "nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8579076d30a8c24988834445f8d633c697d42397e92ffc3f63fa26766d25e0a0"}, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.4.127" +requires_python = ">=3" +summary = "Nvidia JIT LTO Library" +files = [ + {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57"}, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.4.127" +requires_python = ">=3" +summary = "NVIDIA Tools Extension" +files = [ + {file = "nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a"}, +] + +[[package]] +name = "oauthlib" +version = "3.2.2" +requires_python = ">=3.6" +summary = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" files = [ - {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, - {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, + {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, + {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, ] [[package]] -name = "mkdocstrings" -version = "0.25.2" -requires_python = ">=3.8" -summary = "Automatic documentation from sources, for MkDocs." -groups = ["docs"] +name = "omegaconf" +version = "2.3.0" +requires_python = ">=3.6" +summary = "A flexible configuration library" dependencies = [ - "Jinja2>=2.11.1", - "Markdown>=3.3", - "MarkupSafe>=1.1", - "click>=7.0", - "importlib-metadata>=4.6; python_version < \"3.10\"", - "mkdocs-autorefs>=0.3.1", - "mkdocs>=1.4", - "platformdirs>=2.2.0", - "pymdown-extensions>=6.3", - "typing-extensions>=4.1; python_version < \"3.10\"", + "PyYAML>=5.1.0", + "antlr4-python3-runtime==4.9.*", ] files = [ - {file = "mkdocstrings-0.25.2-py3-none-any.whl", hash = "sha256:9e2cda5e2e12db8bb98d21e3410f3f27f8faab685a24b03b06ba7daa5b92abfc"}, - {file = "mkdocstrings-0.25.2.tar.gz", hash = "sha256:5cf57ad7f61e8be3111a2458b4e49c2029c9cb35525393b179f9c916ca8042dc"}, + {file = "omegaconf-2.3.0-py3-none-any.whl", hash = "sha256:7b4df175cdb08ba400f45cae3bdcae7ba8365db4d165fc65fd04b050ab63b46b"}, + {file = "omegaconf-2.3.0.tar.gz", hash = "sha256:d5d4b6d29955cc50ad50c46dc269bcd92c6e00f5f90d23ab5fee7bfca4ba4cc7"}, ] [[package]] -name = "mkdocstrings-python" -version = "1.10.8" -requires_python = ">=3.8" -summary = "A Python handler for mkdocstrings." -groups = ["docs"] +name = "opencv-python-headless" +version = "4.11.0.86" +requires_python = ">=3.6" +summary = "Wrapper package for OpenCV python bindings." dependencies = [ - "griffe>=0.49", - "mkdocstrings>=0.25", + "numpy>=1.17.0; python_version >= \"3.7\"", + "numpy>=1.17.3; python_version >= \"3.8\"", + "numpy>=1.19.3; python_version >= \"3.9\"", + "numpy>=1.21.2; python_version >= \"3.10\"", + "numpy>=1.23.5; python_version >= \"3.11\"", ] files = [ - {file = "mkdocstrings_python-1.10.8-py3-none-any.whl", hash = "sha256:bb12e76c8b071686617f824029cb1dfe0e9afe89f27fb3ad9a27f95f054dcd89"}, - {file = "mkdocstrings_python-1.10.8.tar.gz", hash = "sha256:5856a59cbebbb8deb133224a540de1ff60bded25e54d8beacc375bb133d39016"}, + {file = "opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798"}, + {file = "opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b"}, ] [[package]] -name = "mkdocstrings" -version = "0.25.2" -extras = ["python"] +name = "openslide-python" +version = "1.3.1" requires_python = ">=3.8" -summary = "Automatic documentation from sources, for MkDocs." -groups = ["docs"] +summary = "Python interface to OpenSlide" dependencies = [ - "mkdocstrings-python>=0.5.2", - "mkdocstrings==0.25.2", + "Pillow", ] files = [ - {file = "mkdocstrings-0.25.2-py3-none-any.whl", hash = "sha256:9e2cda5e2e12db8bb98d21e3410f3f27f8faab685a24b03b06ba7daa5b92abfc"}, - {file = "mkdocstrings-0.25.2.tar.gz", hash = "sha256:5cf57ad7f61e8be3111a2458b4e49c2029c9cb35525393b179f9c916ca8042dc"}, + {file = "openslide-python-1.3.1.tar.gz", hash = "sha256:0909c6257cd8decfbbd0082e8c0cd94bbe3a89ad31e142cfa9accc8bb959294e"}, + {file = "openslide_python-1.3.1-cp311-cp311-win32.whl", hash = "sha256:c4720598ba39e7b879e757eff31195f8b80d4638dcb0fbb297ca9823039724ae"}, + {file = "openslide_python-1.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:53a468cd92bdd17cf5b56592242709519c0c7d7028b2f466d20d75264471cc6d"}, + {file = "openslide_python-1.3.1-cp312-cp312-win32.whl", hash = "sha256:d10caf1a1c1e1f598d80e7a5e1a266979ed9bccf9ba8bf45aa34cf04639d9f9e"}, + {file = "openslide_python-1.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:d834fbca0824b902da9d8541f7c34a3e62496823a42de5ac7bf6c35e4c799678"}, ] [[package]] -name = "mypy" -version = "1.11.2" +name = "opentelemetry-api" +version = "1.27.0" requires_python = ">=3.8" -summary = "Optional static typing for Python" -groups = ["dev"] +summary = "OpenTelemetry Python API" dependencies = [ - "mypy-extensions>=1.0.0", - "tomli>=1.1.0; python_version < \"3.11\"", - "typing-extensions>=4.6.0", + "deprecated>=1.2.6", + "importlib-metadata<=8.4.0,>=6.0", ] files = [ - {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, - {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, - {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, - {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, - {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, - {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, - {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, - {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, - {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, - {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, - {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, - {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, + {file = "opentelemetry_api-1.27.0-py3-none-any.whl", hash = "sha256:953d5871815e7c30c81b56d910c707588000fff7a3ca1c73e6531911d53065e7"}, + {file = "opentelemetry_api-1.27.0.tar.gz", hash = "sha256:ed673583eaa5f81b5ce5e86ef7cdaf622f88ef65f0b9aab40b843dcae5bef342"}, ] [[package]] -name = "mypy-extensions" -version = "1.0.0" -requires_python = ">=3.5" -summary = "Type system extensions for programs checked with the mypy type checker." -groups = ["dev"] -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +name = "opentelemetry-sdk" +version = "1.27.0" +requires_python = ">=3.8" +summary = "OpenTelemetry Python SDK" +dependencies = [ + "opentelemetry-api==1.27.0", + "opentelemetry-semantic-conventions==0.48b0", + "typing-extensions>=3.7.4", ] - -[[package]] -name = "networkx" -version = "3.3" -requires_python = ">=3.10" -summary = "Python package for creating and manipulating graphs and networks" -groups = ["default"] files = [ - {file = "networkx-3.3-py3-none-any.whl", hash = "sha256:28575580c6ebdaf4505b22c6256a2b9de86b316dc63ba9e93abde3d78dfdbcf2"}, - {file = "networkx-3.3.tar.gz", hash = "sha256:0c127d8b2f4865f59ae9cb8aafcd60b5c70f3241ebd66f7defad7c4ab90126c9"}, + {file = "opentelemetry_sdk-1.27.0-py3-none-any.whl", hash = "sha256:365f5e32f920faf0fd9e14fdfd92c086e317eaa5f860edba9cdc17a380d9197d"}, + {file = "opentelemetry_sdk-1.27.0.tar.gz", hash = "sha256:d525017dea0ccce9ba4e0245100ec46ecdc043f2d7b8315d56b19aff0904fa6f"}, ] [[package]] -name = "nodeenv" -version = "1.9.1" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -summary = "Node.js virtual environment builder" -groups = ["dev"] -files = [ - {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, - {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +name = "opentelemetry-semantic-conventions" +version = "0.48b0" +requires_python = ">=3.8" +summary = "OpenTelemetry Semantic Conventions" +dependencies = [ + "deprecated>=1.2.6", + "opentelemetry-api==1.27.0", ] - -[[package]] -name = "numpy" -version = "2.1.0" -requires_python = ">=3.10" -summary = "Fundamental package for array computing in Python" -groups = ["default"] -files = [ - {file = "numpy-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:76368c788ccb4f4782cf9c842b316140142b4cbf22ff8db82724e82fe1205dce"}, - {file = "numpy-2.1.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f8e93a01a35be08d31ae33021e5268f157a2d60ebd643cfc15de6ab8e4722eb1"}, - {file = "numpy-2.1.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:9523f8b46485db6939bd069b28b642fec86c30909cea90ef550373787f79530e"}, - {file = "numpy-2.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54139e0eb219f52f60656d163cbe67c31ede51d13236c950145473504fa208cb"}, - {file = "numpy-2.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5ebbf9fbdabed208d4ecd2e1dfd2c0741af2f876e7ae522c2537d404ca895c3"}, - {file = "numpy-2.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:378cb4f24c7d93066ee4103204f73ed046eb88f9ad5bb2275bb9fa0f6a02bd36"}, - {file = "numpy-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8f699a709120b220dfe173f79c73cb2a2cab2c0b88dd59d7b49407d032b8ebd"}, - {file = "numpy-2.1.0-cp311-cp311-win32.whl", hash = "sha256:ffbd6faeb190aaf2b5e9024bac9622d2ee549b7ec89ef3a9373fa35313d44e0e"}, - {file = "numpy-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0af3a5987f59d9c529c022c8c2a64805b339b7ef506509fba7d0556649b9714b"}, - {file = "numpy-2.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fe76d75b345dc045acdbc006adcb197cc680754afd6c259de60d358d60c93736"}, - {file = "numpy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f358ea9e47eb3c2d6eba121ab512dfff38a88db719c38d1e67349af210bc7529"}, - {file = "numpy-2.1.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:dd94ce596bda40a9618324547cfaaf6650b1a24f5390350142499aa4e34e53d1"}, - {file = "numpy-2.1.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b47c551c6724960479cefd7353656498b86e7232429e3a41ab83be4da1b109e8"}, - {file = "numpy-2.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0756a179afa766ad7cb6f036de622e8a8f16ffdd55aa31f296c870b5679d745"}, - {file = "numpy-2.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24003ba8ff22ea29a8c306e61d316ac74111cebf942afbf692df65509a05f111"}, - {file = "numpy-2.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b34fa5e3b5d6dc7e0a4243fa0f81367027cb6f4a7215a17852979634b5544ee0"}, - {file = "numpy-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c4f982715e65036c34897eb598d64aef15150c447be2cfc6643ec7a11af06574"}, - {file = "numpy-2.1.0-cp312-cp312-win32.whl", hash = "sha256:c4cd94dfefbefec3f8b544f61286584292d740e6e9d4677769bc76b8f41deb02"}, - {file = "numpy-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0cdef204199278f5c461a0bed6ed2e052998276e6d8ab2963d5b5c39a0500bc"}, - {file = "numpy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8ab81ccd753859ab89e67199b9da62c543850f819993761c1e94a75a814ed667"}, - {file = "numpy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:442596f01913656d579309edcd179a2a2f9977d9a14ff41d042475280fc7f34e"}, - {file = "numpy-2.1.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:848c6b5cad9898e4b9ef251b6f934fa34630371f2e916261070a4eb9092ffd33"}, - {file = "numpy-2.1.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:54c6a63e9d81efe64bfb7bcb0ec64332a87d0b87575f6009c8ba67ea6374770b"}, - {file = "numpy-2.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:652e92fc409e278abdd61e9505649e3938f6d04ce7ef1953f2ec598a50e7c195"}, - {file = "numpy-2.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ab32eb9170bf8ffcbb14f11613f4a0b108d3ffee0832457c5d4808233ba8977"}, - {file = "numpy-2.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8fb49a0ba4d8f41198ae2d52118b050fd34dace4b8f3fb0ee34e23eb4ae775b1"}, - {file = "numpy-2.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44e44973262dc3ae79e9063a1284a73e09d01b894b534a769732ccd46c28cc62"}, - {file = "numpy-2.1.0-cp313-cp313-win32.whl", hash = "sha256:ab83adc099ec62e044b1fbb3a05499fa1e99f6d53a1dde102b2d85eff66ed324"}, - {file = "numpy-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:de844aaa4815b78f6023832590d77da0e3b6805c644c33ce94a1e449f16d6ab5"}, - {file = "numpy-2.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:343e3e152bf5a087511cd325e3b7ecfd5b92d369e80e74c12cd87826e263ec06"}, - {file = "numpy-2.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f07fa2f15dabe91259828ce7d71b5ca9e2eb7c8c26baa822c825ce43552f4883"}, - {file = "numpy-2.1.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5474dad8c86ee9ba9bb776f4b99ef2d41b3b8f4e0d199d4f7304728ed34d0300"}, - {file = "numpy-2.1.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:1f817c71683fd1bb5cff1529a1d085a57f02ccd2ebc5cd2c566f9a01118e3b7d"}, - {file = "numpy-2.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a3336fbfa0d38d3deacd3fe7f3d07e13597f29c13abf4d15c3b6dc2291cbbdd"}, - {file = "numpy-2.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a894c51fd8c4e834f00ac742abad73fc485df1062f1b875661a3c1e1fb1c2f6"}, - {file = "numpy-2.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:9156ca1f79fc4acc226696e95bfcc2b486f165a6a59ebe22b2c1f82ab190384a"}, - {file = "numpy-2.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:624884b572dff8ca8f60fab591413f077471de64e376b17d291b19f56504b2bb"}, - {file = "numpy-2.1.0.tar.gz", hash = "sha256:7dc90da0081f7e1da49ec4e398ede6a8e9cc4f5ebe5f9e06b443ed889ee9aaa2"}, +files = [ + {file = "opentelemetry_semantic_conventions-0.48b0-py3-none-any.whl", hash = "sha256:a0de9f45c413a8669788a38569c7e0a11ce6ce97861a628cca785deecdc32a1f"}, + {file = "opentelemetry_semantic_conventions-0.48b0.tar.gz", hash = "sha256:12d74983783b6878162208be57c9effcb89dc88691c64992d70bb89dc00daa1a"}, ] [[package]] @@ -659,41 +1359,38 @@ name = "packaging" version = "24.1" requires_python = ">=3.8" summary = "Core utilities for Python packages" -groups = ["default", "docs", "test"] files = [ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] -name = "paginate" -version = "0.5.7" -summary = "Divides large result sets into pages for easier browsing" -groups = ["docs"] -files = [ - {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, - {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -requires_python = ">=3.8" -summary = "Utility library for gitignore style pattern matching of file paths." -groups = ["docs"] -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +name = "pandas" +version = "2.2.2" +requires_python = ">=3.9" +summary = "Powerful data structures for data analysis, time series, and statistics" +dependencies = [ + "numpy>=1.23.2; python_version == \"3.11\"", + "python-dateutil>=2.8.2", + "pytz>=2020.1", + "tzdata>=2022.7", ] - -[[package]] -name = "perlin-noise" -version = "1.13" -summary = "Python implementation for Perlin Noise with unlimited coordinates space" -groups = ["test"] files = [ - {file = "perlin_noise-1.13-py3-none-any.whl", hash = "sha256:09f274c8da38f5a2a48b1626032a1b7738990df758a3bc7549ff3464869c6ca4"}, - {file = "perlin_noise-1.13.tar.gz", hash = "sha256:0e0f074743336fbe9cf2cdbdeb74c75d43780a8b3cbc3415859cd396ab4da177"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, + {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, + {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, + {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, ] [[package]] @@ -701,7 +1398,6 @@ name = "pillow" version = "10.4.0" requires_python = ">=3.8" summary = "Python Imaging Library (Fork)" -groups = ["default"] files = [ {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, @@ -739,34 +1435,31 @@ files = [ {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, ] +[[package]] +name = "pkgconfig" +version = "1.5.5" +requires_python = ">=3.3,<4.0" +summary = "Interface Python with pkg-config" +files = [ + {file = "pkgconfig-1.5.5-py3-none-any.whl", hash = "sha256:d20023bbeb42ee6d428a0fac6e0904631f545985a10cdd71a20aa58bc47a4209"}, + {file = "pkgconfig-1.5.5.tar.gz", hash = "sha256:deb4163ef11f75b520d822d9505c1f462761b4309b1bb713d08689759ea8b899"}, +] + [[package]] name = "platformdirs" version = "4.2.2" requires_python = ">=3.8" summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -groups = ["dev", "docs"] files = [ {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] -[[package]] -name = "pluggy" -version = "1.5.0" -requires_python = ">=3.8" -summary = "plugin and hook calling mechanisms for python" -groups = ["test"] -files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, -] - [[package]] name = "pre-commit" version = "3.8.0" requires_python = ">=3.9" summary = "A framework for managing and maintaining multi-language pre-commit hooks." -groups = ["dev"] dependencies = [ "cfgv>=2.0.0", "identify>=1.0.0", @@ -780,63 +1473,115 @@ files = [ ] [[package]] -name = "pygments" -version = "2.18.0" +name = "protobuf" +version = "5.28.0" requires_python = ">=3.8" -summary = "Pygments is a syntax highlighting package written in Python." -groups = ["docs"] +summary = "" files = [ - {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, - {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, + {file = "protobuf-5.28.0-cp310-abi3-win32.whl", hash = "sha256:66c3edeedb774a3508ae70d87b3a19786445fe9a068dd3585e0cefa8a77b83d0"}, + {file = "protobuf-5.28.0-cp310-abi3-win_amd64.whl", hash = "sha256:6d7cc9e60f976cf3e873acb9a40fed04afb5d224608ed5c1a105db4a3f09c5b6"}, + {file = "protobuf-5.28.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:532627e8fdd825cf8767a2d2b94d77e874d5ddb0adefb04b237f7cc296748681"}, + {file = "protobuf-5.28.0-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:018db9056b9d75eb93d12a9d35120f97a84d9a919bcab11ed56ad2d399d6e8dd"}, + {file = "protobuf-5.28.0-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:6206afcb2d90181ae8722798dcb56dc76675ab67458ac24c0dd7d75d632ac9bd"}, + {file = "protobuf-5.28.0-py3-none-any.whl", hash = "sha256:510ed78cd0980f6d3218099e874714cdf0d8a95582e7b059b06cabad855ed0a0"}, + {file = "protobuf-5.28.0.tar.gz", hash = "sha256:dde74af0fa774fa98892209992295adbfb91da3fa98c8f67a88afe8f5a349add"}, ] [[package]] -name = "pymdown-extensions" -version = "10.9" +name = "pyarrow" +version = "17.0.0" requires_python = ">=3.8" -summary = "Extension pack for Python Markdown." -groups = ["docs"] +summary = "Python library for Apache Arrow" dependencies = [ - "markdown>=3.6", - "pyyaml", + "numpy>=1.16.6", +] +files = [ + {file = "pyarrow-17.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:1c8856e2ef09eb87ecf937104aacfa0708f22dfeb039c363ec99735190ffb977"}, + {file = "pyarrow-17.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e19f569567efcbbd42084e87f948778eb371d308e137a0f97afe19bb860ccb3"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b244dc8e08a23b3e352899a006a26ae7b4d0da7bb636872fa8f5884e70acf15"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b72e87fe3e1db343995562f7fff8aee354b55ee83d13afba65400c178ab2597"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dc5c31c37409dfbc5d014047817cb4ccd8c1ea25d19576acf1a001fe07f5b420"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e3343cb1e88bc2ea605986d4b94948716edc7a8d14afd4e2c097232f729758b4"}, + {file = "pyarrow-17.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:a27532c38f3de9eb3e90ecab63dfda948a8ca859a66e3a47f5f42d1e403c4d03"}, + {file = "pyarrow-17.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:9b8a823cea605221e61f34859dcc03207e52e409ccf6354634143e23af7c8d22"}, + {file = "pyarrow-17.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1e70de6cb5790a50b01d2b686d54aaf73da01266850b05e3af2a1bc89e16053"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0071ce35788c6f9077ff9ecba4858108eebe2ea5a3f7cf2cf55ebc1dbc6ee24a"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:757074882f844411fcca735e39aae74248a1531367a7c80799b4266390ae51cc"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9ba11c4f16976e89146781a83833df7f82077cdab7dc6232c897789343f7891a"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b0c6ac301093b42d34410b187bba560b17c0330f64907bfa4f7f7f2444b0cf9b"}, + {file = "pyarrow-17.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7"}, + {file = "pyarrow-17.0.0.tar.gz", hash = "sha256:4beca9521ed2c0921c1023e68d097d0299b62c362639ea315572a58f3f50fd28"}, ] + +[[package]] +name = "pyasn1" +version = "0.6.0" +requires_python = ">=3.8" +summary = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" files = [ - {file = "pymdown_extensions-10.9-py3-none-any.whl", hash = "sha256:d323f7e90d83c86113ee78f3fe62fc9dee5f56b54d912660703ea1816fed5626"}, - {file = "pymdown_extensions-10.9.tar.gz", hash = "sha256:6ff740bcd99ec4172a938970d42b96128bdc9d4b9bcad72494f29921dc69b753"}, + {file = "pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473"}, + {file = "pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c"}, ] [[package]] -name = "pytest" -version = "8.3.2" +name = "pyasn1-modules" +version = "0.4.0" requires_python = ">=3.8" -summary = "pytest: simple powerful testing with Python" -groups = ["test"] +summary = "A collection of ASN.1-based protocols modules" dependencies = [ - "colorama; sys_platform == \"win32\"", - "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", - "iniconfig", - "packaging", - "pluggy<2,>=1.5", - "tomli>=1; python_version < \"3.11\"", + "pyasn1<0.7.0,>=0.4.6", +] +files = [ + {file = "pyasn1_modules-0.4.0-py3-none-any.whl", hash = "sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b"}, + {file = "pyasn1_modules-0.4.0.tar.gz", hash = "sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6"}, +] + +[[package]] +name = "pycparser" +version = "2.22" +requires_python = ">=3.8" +summary = "C parser in Python" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + +[[package]] +name = "pydantic" +version = "2.10.6" +requires_python = ">=3.8" +summary = "Data validation using Python type hints" +dependencies = [ + "annotated-types>=0.6.0", + "pydantic-core==2.27.2", + "typing-extensions>=4.12.2", ] files = [ - {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, - {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, + {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, + {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, ] [[package]] -name = "pytest-cov" -version = "5.0.0" +name = "pydantic-core" +version = "2.27.2" requires_python = ">=3.8" -summary = "Pytest plugin for measuring coverage." -groups = ["test"] +summary = "Core functionality for Pydantic validation and serialization" dependencies = [ - "coverage[toml]>=5.2.1", - "pytest>=4.6", + "typing-extensions!=4.7.0,>=4.6.0", +] +files = [ + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, + {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, ] + +[[package]] +name = "pyparsing" +version = "3.1.4" +requires_python = ">=3.6.8" +summary = "pyparsing module - Classes and methods to define and execute parsing grammars" files = [ - {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, - {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, + {file = "pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c"}, + {file = "pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032"}, ] [[package]] @@ -844,7 +1589,6 @@ name = "python-dateutil" version = "2.9.0.post0" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" summary = "Extensions to the standard Python datetime module" -groups = ["docs"] dependencies = [ "six>=1.5", ] @@ -853,12 +1597,52 @@ files = [ {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, ] +[[package]] +name = "pytorch-lightning" +version = "2.4.0" +requires_python = ">=3.8" +summary = "PyTorch Lightning is the lightweight PyTorch wrapper for ML researchers. Scale your models. Write less boilerplate." +dependencies = [ + "PyYAML>=5.4", + "fsspec[http]>=2022.5.0", + "lightning-utilities>=0.10.0", + "packaging>=20.0", + "torch>=2.1.0", + "torchmetrics>=0.7.0", + "tqdm>=4.57.0", + "typing-extensions>=4.4.0", +] +files = [ + {file = "pytorch-lightning-2.4.0.tar.gz", hash = "sha256:6aa897fd9d6dfa7b7b49f37c2f04e13592861831d08deae584dfda423fdb71c8"}, + {file = "pytorch_lightning-2.4.0-py3-none-any.whl", hash = "sha256:9ac7935229ac022ef06994c928217ed37f525ac6700f7d4fc57009624570e655"}, +] + +[[package]] +name = "pytz" +version = "2024.1" +summary = "World timezone definitions, modern and historical" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "pyvips" +version = "2.2.3" +summary = "binding for the libvips image processing library, API mode" +dependencies = [ + "cffi>=1.0.0", + "pkgconfig", +] +files = [ + {file = "pyvips-2.2.3.tar.gz", hash = "sha256:43bceced0db492654c93008246a58a508e0373ae1621116b87b322f2ac72212f"}, +] + [[package]] name = "pyyaml" version = "6.0.2" requires_python = ">=3.8" summary = "YAML parser and emitter for Python" -groups = ["dev", "docs"] files = [ {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, @@ -891,57 +1675,100 @@ files = [ ] [[package]] -name = "pyyaml-env-tag" -version = "0.1" -requires_python = ">=3.6" -summary = "A custom YAML tag for referencing environment variables in YAML files. " -groups = ["docs"] +name = "rationai-kube-jobs" +version = "0.1.0" +requires_python = ">=3.11" +git = "ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/kube-jobs.git" +revision = "7ce565cc35bfd1abe30db2e1e4e606e65dda8c84" +summary = "A simple CLI tool for managing Kubernetes jobs." +dependencies = [ + "click>=8.1.7", + "kubernetes>=31.0.0", +] + +[[package]] +name = "rationai-masks" +version = "1.0.0" +requires_python = ">=3.11" +git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git" +revision = "7d56f2ba3c7b411bdf6043ae1224676c745317d2" +summary = "" +dependencies = [ + "numpy>=1.9.0", + "openslide-python>=1.3.1", + "pandas>=2.2.2", + "pillow>=10.4.0", + "pyvips>=2.2.3", + "ray>=2.34.0", + "tqdm>=4.66.5", +] + +[[package]] +name = "rationai-mlkit" +version = "0.1.15" +requires_python = ">=3.11" +git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/mlkit.git" +revision = "19712cca6931f11d25292cd35c5678b53592b5e7" +summary = "" +dependencies = [ + "GitPython>=3.1.43", + "hydra-core>=1.3.2", + "lightning>=2.2.4", + "mlflow>=2.12.1", + "openslide-python>=1.3.1", + "torch>=2.2.0", +] + +[[package]] +name = "rationai-tiling" +version = "1.0.0" +requires_python = ">=3.11" +git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git" +revision = "5889fc70093e097684f6014dab8ad5d0616e14fd" +summary = "" +dependencies = [ + "mlflow>=2.15.1", + "numpy>=1.9.0", + "openslide-python>=1.3.1", + "pandas>=2.2.2", + "pyvips>=2.2.3", + "ray>=2.34.0", + "tqdm>=4.66.5", +] + +[[package]] +name = "ray" +version = "2.38.0" +requires_python = ">=3.9" +summary = "Ray provides a simple, universal API for building distributed applications." dependencies = [ + "aiosignal", + "click>=7.0", + "filelock", + "frozenlist", + "jsonschema", + "msgpack<2.0.0,>=1.0.0", + "packaging", + "protobuf!=3.19.5,>=3.15.3", "pyyaml", + "requests", ] files = [ - {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, - {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, + {file = "ray-2.38.0-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:ece802cf3a1c102b53f63b8bc90d947971c4b387deaf233c224ed8ef34a1f3cb"}, ] [[package]] -name = "regex" -version = "2024.7.24" +name = "referencing" +version = "0.35.1" requires_python = ">=3.8" -summary = "Alternative regular expression module, to replace re." -groups = ["docs"] -files = [ - {file = "regex-2024.7.24-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:382281306e3adaaa7b8b9ebbb3ffb43358a7bbf585fa93821300a418bb975281"}, - {file = "regex-2024.7.24-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4fdd1384619f406ad9037fe6b6eaa3de2749e2e12084abc80169e8e075377d3b"}, - {file = "regex-2024.7.24-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3d974d24edb231446f708c455fd08f94c41c1ff4f04bcf06e5f36df5ef50b95a"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2ec4419a3fe6cf8a4795752596dfe0adb4aea40d3683a132bae9c30b81e8d73"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb563dd3aea54c797adf513eeec819c4213d7dbfc311874eb4fd28d10f2ff0f2"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:45104baae8b9f67569f0f1dca5e1f1ed77a54ae1cd8b0b07aba89272710db61e"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:994448ee01864501912abf2bad9203bffc34158e80fe8bfb5b031f4f8e16da51"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fac296f99283ac232d8125be932c5cd7644084a30748fda013028c815ba3364"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7e37e809b9303ec3a179085415cb5f418ecf65ec98cdfe34f6a078b46ef823ee"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:01b689e887f612610c869421241e075c02f2e3d1ae93a037cb14f88ab6a8934c"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f6442f0f0ff81775eaa5b05af8a0ffa1dda36e9cf6ec1e0d3d245e8564b684ce"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:871e3ab2838fbcb4e0865a6e01233975df3a15e6fce93b6f99d75cacbd9862d1"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c918b7a1e26b4ab40409820ddccc5d49871a82329640f5005f73572d5eaa9b5e"}, - {file = "regex-2024.7.24-cp311-cp311-win32.whl", hash = "sha256:2dfbb8baf8ba2c2b9aa2807f44ed272f0913eeeba002478c4577b8d29cde215c"}, - {file = "regex-2024.7.24-cp311-cp311-win_amd64.whl", hash = "sha256:538d30cd96ed7d1416d3956f94d54e426a8daf7c14527f6e0d6d425fcb4cca52"}, - {file = "regex-2024.7.24-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fe4ebef608553aff8deb845c7f4f1d0740ff76fa672c011cc0bacb2a00fbde86"}, - {file = "regex-2024.7.24-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:74007a5b25b7a678459f06559504f1eec2f0f17bca218c9d56f6a0a12bfffdad"}, - {file = "regex-2024.7.24-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7df9ea48641da022c2a3c9c641650cd09f0cd15e8908bf931ad538f5ca7919c9"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a1141a1dcc32904c47f6846b040275c6e5de0bf73f17d7a409035d55b76f289"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80c811cfcb5c331237d9bad3bea2c391114588cf4131707e84d9493064d267f9"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7214477bf9bd195894cf24005b1e7b496f46833337b5dedb7b2a6e33f66d962c"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d55588cba7553f0b6ec33130bc3e114b355570b45785cebdc9daed8c637dd440"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:558a57cfc32adcf19d3f791f62b5ff564922942e389e3cfdb538a23d65a6b610"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a512eed9dfd4117110b1881ba9a59b31433caed0c4101b361f768e7bcbaf93c5"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:86b17ba823ea76256b1885652e3a141a99a5c4422f4a869189db328321b73799"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5eefee9bfe23f6df09ffb6dfb23809f4d74a78acef004aa904dc7c88b9944b05"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:731fcd76bbdbf225e2eb85b7c38da9633ad3073822f5ab32379381e8c3c12e94"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eaef80eac3b4cfbdd6de53c6e108b4c534c21ae055d1dbea2de6b3b8ff3def38"}, - {file = "regex-2024.7.24-cp312-cp312-win32.whl", hash = "sha256:185e029368d6f89f36e526764cf12bf8d6f0e3a2a7737da625a76f594bdfcbfc"}, - {file = "regex-2024.7.24-cp312-cp312-win_amd64.whl", hash = "sha256:2f1baff13cc2521bea83ab2528e7a80cbe0ebb2c6f0bfad15be7da3aed443908"}, - {file = "regex-2024.7.24.tar.gz", hash = "sha256:9cfd009eed1a46b27c14039ad5bbc5e71b6367c5b2e6d5f5da0ea91600817506"}, +summary = "JSON Referencing + Python" +dependencies = [ + "attrs>=22.2.0", + "rpds-py>=0.7.0", +] +files = [ + {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, + {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, ] [[package]] @@ -949,7 +1776,6 @@ name = "requests" version = "2.32.3" requires_python = ">=3.8" summary = "Python HTTP for Humans." -groups = ["docs"] dependencies = [ "certifi>=2017.4.17", "charset-normalizer<4,>=2", @@ -961,61 +1787,83 @@ files = [ {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] +[[package]] +name = "requests-oauthlib" +version = "2.0.0" +requires_python = ">=3.4" +summary = "OAuthlib authentication support for Requests." +dependencies = [ + "oauthlib>=3.0.0", + "requests>=2.0.0", +] +files = [ + {file = "requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9"}, + {file = "requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36"}, +] + +[[package]] +name = "rpds-py" +version = "0.21.0" +requires_python = ">=3.9" +summary = "Python bindings to Rust's persistent data structures (rpds)" +files = [ + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de609a6f1b682f70bb7163da745ee815d8f230d97276db049ab447767466a09d"}, + {file = "rpds_py-0.21.0.tar.gz", hash = "sha256:ed6378c9d66d0de903763e7706383d60c33829581f0adff47b6535f1802fa6db"}, +] + +[[package]] +name = "rsa" +version = "4.9" +requires_python = ">=3.6,<4" +summary = "Pure-Python RSA implementation" +dependencies = [ + "pyasn1>=0.1.3", +] +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + [[package]] name = "ruff" -version = "0.6.2" +version = "0.6.0" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." -groups = ["dev"] -files = [ - {file = "ruff-0.6.2-py3-none-linux_armv6l.whl", hash = "sha256:5c8cbc6252deb3ea840ad6a20b0f8583caab0c5ef4f9cca21adc5a92b8f79f3c"}, - {file = "ruff-0.6.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:17002fe241e76544448a8e1e6118abecbe8cd10cf68fde635dad480dba594570"}, - {file = "ruff-0.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3dbeac76ed13456f8158b8f4fe087bf87882e645c8e8b606dd17b0b66c2c1158"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:094600ee88cda325988d3f54e3588c46de5c18dae09d683ace278b11f9d4d534"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:316d418fe258c036ba05fbf7dfc1f7d3d4096db63431546163b472285668132b"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d72b8b3abf8a2d51b7b9944a41307d2f442558ccb3859bbd87e6ae9be1694a5d"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2aed7e243be68487aa8982e91c6e260982d00da3f38955873aecd5a9204b1d66"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d371f7fc9cec83497fe7cf5eaf5b76e22a8efce463de5f775a1826197feb9df8"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f310d63af08f583363dfb844ba8f9417b558199c58a5999215082036d795a1"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7db6880c53c56addb8638fe444818183385ec85eeada1d48fc5abe045301b2f1"}, - {file = "ruff-0.6.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1175d39faadd9a50718f478d23bfc1d4da5743f1ab56af81a2b6caf0a2394f23"}, - {file = "ruff-0.6.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b939f9c86d51635fe486585389f54582f0d65b8238e08c327c1534844b3bb9a"}, - {file = "ruff-0.6.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d0d62ca91219f906caf9b187dea50d17353f15ec9bb15aae4a606cd697b49b4c"}, - {file = "ruff-0.6.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7438a7288f9d67ed3c8ce4d059e67f7ed65e9fe3aa2ab6f5b4b3610e57e3cb56"}, - {file = "ruff-0.6.2-py3-none-win32.whl", hash = "sha256:279d5f7d86696df5f9549b56b9b6a7f6c72961b619022b5b7999b15db392a4da"}, - {file = "ruff-0.6.2-py3-none-win_amd64.whl", hash = "sha256:d9f3469c7dd43cd22eb1c3fc16926fb8258d50cb1b216658a07be95dd117b0f2"}, - {file = "ruff-0.6.2-py3-none-win_arm64.whl", hash = "sha256:f28fcd2cd0e02bdf739297516d5643a945cc7caf09bd9bcb4d932540a5ea4fa9"}, - {file = "ruff-0.6.2.tar.gz", hash = "sha256:239ee6beb9e91feb8e0ec384204a763f36cb53fb895a1a364618c6abb076b3be"}, -] - -[[package]] -name = "scikit-image" -version = "0.24.0" +files = [ + {file = "ruff-0.6.0-py3-none-linux_armv6l.whl", hash = "sha256:92dcce923e5df265781e5fc76f9a1edad52201a7aafe56e586b90988d5239013"}, + {file = "ruff-0.6.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:31b90ff9dc79ed476c04e957ba7e2b95c3fceb76148f2079d0d68a908d2cfae7"}, + {file = "ruff-0.6.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6d834a9ec9f8287dd6c3297058b3a265ed6b59233db22593379ee38ebc4b9768"}, + {file = "ruff-0.6.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2089267692696aba342179471831a085043f218706e642564812145df8b8d0d"}, + {file = "ruff-0.6.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aa62b423ee4bbd8765f2c1dbe8f6aac203e0583993a91453dc0a449d465c84da"}, + {file = "ruff-0.6.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7344e1a964b16b1137ea361d6516ce4ee61a0403fa94252a1913ecc1311adcae"}, + {file = "ruff-0.6.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:487f3a35c3f33bf82be212ce15dc6278ea854e35573a3f809442f73bec8b2760"}, + {file = "ruff-0.6.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75db409984077a793cf344d499165298a6f65449e905747ac65983b12e3e64b1"}, + {file = "ruff-0.6.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84908bd603533ecf1db456d8fc2665d1f4335d722e84bc871d3bbd2d1116c272"}, + {file = "ruff-0.6.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f1749a0aef3ec41ed91a0e2127a6ae97d2e2853af16dbd4f3c00d7a3af726c5"}, + {file = "ruff-0.6.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:016fea751e2bcfbbd2f8cb19b97b37b3fd33148e4df45b526e87096f4e17354f"}, + {file = "ruff-0.6.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6ae80f141b53b2e36e230017e64f5ea2def18fac14334ffceaae1b780d70c4f7"}, + {file = "ruff-0.6.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:eaaaf33ea4b3f63fd264d6a6f4a73fa224bbfda4b438ffea59a5340f4afa2bb5"}, + {file = "ruff-0.6.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7667ddd1fc688150a7ca4137140867584c63309695a30016880caf20831503a0"}, + {file = "ruff-0.6.0-py3-none-win32.whl", hash = "sha256:ae48365aae60d40865a412356f8c6f2c0be1c928591168111eaf07eaefa6bea3"}, + {file = "ruff-0.6.0-py3-none-win_amd64.whl", hash = "sha256:774032b507c96f0c803c8237ce7d2ef3934df208a09c40fa809c2931f957fe5e"}, + {file = "ruff-0.6.0-py3-none-win_arm64.whl", hash = "sha256:a5366e8c3ae6b2dc32821749b532606c42e609a99b0ae1472cf601da931a048c"}, + {file = "ruff-0.6.0.tar.gz", hash = "sha256:272a81830f68f9bd19d49eaf7fa01a5545c5a2e86f32a9935bb0e4bb9a1db5b8"}, +] + +[[package]] +name = "scikit-learn" +version = "1.6.1" requires_python = ">=3.9" -summary = "Image processing in Python" -groups = ["default"] +summary = "A set of python modules for machine learning and data mining" dependencies = [ - "imageio>=2.33", - "lazy-loader>=0.4", - "networkx>=2.8", - "numpy>=1.23", - "packaging>=21", - "pillow>=9.1", - "scipy>=1.9", - "tifffile>=2022.8.12", + "joblib>=1.2.0", + "numpy>=1.19.5", + "scipy>=1.6.0", + "threadpoolctl>=3.1.0", ] files = [ - {file = "scikit_image-0.24.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:272909e02a59cea3ed4aa03739bb88df2625daa809f633f40b5053cf09241831"}, - {file = "scikit_image-0.24.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:190ebde80b4470fe8838764b9b15f232a964f1a20391663e31008d76f0c696f7"}, - {file = "scikit_image-0.24.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59c98cc695005faf2b79904e4663796c977af22586ddf1b12d6af2fa22842dc2"}, - {file = "scikit_image-0.24.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa27b3a0dbad807b966b8db2d78da734cb812ca4787f7fbb143764800ce2fa9c"}, - {file = "scikit_image-0.24.0-cp311-cp311-win_amd64.whl", hash = "sha256:dacf591ac0c272a111181afad4b788a27fe70d213cfddd631d151cbc34f8ca2c"}, - {file = "scikit_image-0.24.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6fccceb54c9574590abcddc8caf6cefa57c13b5b8b4260ab3ff88ad8f3c252b3"}, - {file = "scikit_image-0.24.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ccc01e4760d655aab7601c1ba7aa4ddd8b46f494ac46ec9c268df6f33ccddf4c"}, - {file = "scikit_image-0.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18836a18d3a7b6aca5376a2d805f0045826bc6c9fc85331659c33b4813e0b563"}, - {file = "scikit_image-0.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8579bda9c3f78cb3b3ed8b9425213c53a25fa7e994b7ac01f2440b395babf660"}, - {file = "scikit_image-0.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:82ab903afa60b2da1da2e6f0c8c65e7c8868c60a869464c41971da929b3e82bc"}, - {file = "scikit_image-0.24.0.tar.gz", hash = "sha256:5d16efe95da8edbeb363e0c4157b99becbd650a60b77f6e3af5768b66cf007ab"}, + {file = "scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25fc636bdaf1cc2f4a124a116312d837148b5e10872147bdaf4887926b8c03d8"}, + {file = "scikit_learn-1.6.1.tar.gz", hash = "sha256:b4fc2525eca2c69a59260f583c56a7557c6ccdf8deafdba6e060f94c1c59738e"}, ] [[package]] @@ -1023,7 +1871,6 @@ name = "scipy" version = "1.14.1" requires_python = ">=3.10" summary = "Fundamental algorithms for scientific computing in Python" -groups = ["default"] dependencies = [ "numpy<2.3,>=1.23.5", ] @@ -1055,29 +1902,189 @@ files = [ {file = "scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417"}, ] +[[package]] +name = "setuptools" +version = "72.2.0" +requires_python = ">=3.8" +summary = "Easily download, build, install, upgrade, and uninstall Python packages" +files = [ + {file = "setuptools-72.2.0-py3-none-any.whl", hash = "sha256:f11dd94b7bae3a156a95ec151f24e4637fb4fa19c878e4d191bfb8b2d82728c4"}, + {file = "setuptools-72.2.0.tar.gz", hash = "sha256:80aacbf633704e9c8bfa1d99fa5dd4dc59573efcf9e4042c13d3bcef91ac2ef9"}, +] + +[[package]] +name = "simsimd" +version = "6.2.1" +summary = "Portable mixed-precision BLAS-like vector math library for x86 and ARM" +files = [ + {file = "simsimd-6.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:173e66699597a4fcf6fa50b52cced40216fdcfba15f60b761a2bd9cb1d98a444"}, + {file = "simsimd-6.2.1.tar.gz", hash = "sha256:5e202c5386a4141946b7aee05faac8ebc2e36bca0a360b24080e57b59bc4ef6a"}, +] + [[package]] name = "six" version = "1.16.0" requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" summary = "Python 2 and 3 compatibility utilities" -groups = ["docs"] files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] [[package]] -name = "tifffile" -version = "2024.8.24" +name = "smmap" +version = "5.0.1" +requires_python = ">=3.7" +summary = "A pure Python implementation of a sliding window memory map manager" +files = [ + {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, + {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.33" +requires_python = ">=3.7" +summary = "Database Abstraction Library" +dependencies = [ + "greenlet!=0.4.17; python_version < \"3.13\" and (platform_machine == \"aarch64\" or (platform_machine == \"ppc64le\" or (platform_machine == \"x86_64\" or (platform_machine == \"amd64\" or (platform_machine == \"AMD64\" or (platform_machine == \"win32\" or platform_machine == \"WIN32\"))))))", + "typing-extensions>=4.6.0", +] +files = [ + {file = "SQLAlchemy-2.0.33-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:570ec43e8c3c020abac4f0720baa5fe5187334e3f1e8e1777183c041962b61cc"}, + {file = "SQLAlchemy-2.0.33-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81759e77a4985abdbac068762a0eaf0f11860fe041ad6da170aae7615ea72531"}, + {file = "SQLAlchemy-2.0.33-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82c72da5be489c8d150deba70d5732398695418df5232bceb52ee323ddd9753b"}, + {file = "SQLAlchemy-2.0.33-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:30a3f55be76364b64c83788728faaba782ab282a24909e1994404c2146d39982"}, + {file = "SQLAlchemy-2.0.33-cp311-cp311-win32.whl", hash = "sha256:17d0c69f66392ad2db1609373a74d1f834b2e632f3f52d446747b8ec220aea53"}, + {file = "SQLAlchemy-2.0.33-cp311-cp311-win_amd64.whl", hash = "sha256:c5d5a733c6af7f392435e673d1b136f6bdf2366033abb35eed680400dc730840"}, + {file = "SQLAlchemy-2.0.33-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1d81e3aeab456fe24c3f0dcfd4f952a3a5ee45e9c14fc66d34c1d7a60cf7b698"}, + {file = "SQLAlchemy-2.0.33-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca8788dc1baee100f09110f33a01d928cf9df4483d2bfb25a37be31a659d46bb"}, + {file = "SQLAlchemy-2.0.33-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684aee5fd811091b2f48006fb3fe6c7f2de4a716ef8d294a2aab762099753133"}, + {file = "SQLAlchemy-2.0.33-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1109cc6dc5c9d1223c42186391e6a5509e6d4ab2c30fa629573c10184f742f2e"}, + {file = "SQLAlchemy-2.0.33-cp312-cp312-win32.whl", hash = "sha256:c633e2d2f8a7b88c06e276bbe16cb7e62fed815fcbeb69cd9752cea166ecb8e8"}, + {file = "SQLAlchemy-2.0.33-cp312-cp312-win_amd64.whl", hash = "sha256:77eaf8fdf305266b806a91ae4633edbf86ad37e13bd92ac85e305e7f654c19a5"}, + {file = "SQLAlchemy-2.0.33-py3-none-any.whl", hash = "sha256:ae294808afde1b14a1a69aa86a69cadfe391848bbb233a5332a8065e4081cabc"}, + {file = "sqlalchemy-2.0.33.tar.gz", hash = "sha256:91c93333c2b37ff721dc83b37e28c29de4c502b5612f2d093468037b86aa2be0"}, +] + +[[package]] +name = "sqlparse" +version = "0.5.1" +requires_python = ">=3.8" +summary = "A non-validating SQL parser." +files = [ + {file = "sqlparse-0.5.1-py3-none-any.whl", hash = "sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4"}, + {file = "sqlparse-0.5.1.tar.gz", hash = "sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e"}, +] + +[[package]] +name = "stringzilla" +version = "3.12.1" +summary = "SIMD-accelerated string search, sort, hashes, fingerprints, & edit distances" +files = [ + {file = "stringzilla-3.12.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef30bd15638f83aa12978a0d184d1830caf55a7f80ccbbab0528f73873d4a017"}, + {file = "stringzilla-3.12.1.tar.gz", hash = "sha256:b812c9fc9a0ff36594e847443443895be91d38aa460a7b02b4a6e3048b76e2c8"}, +] + +[[package]] +name = "sympy" +version = "1.13.1" +requires_python = ">=3.8" +summary = "Computer algebra system (CAS) in Python" +dependencies = [ + "mpmath<1.4,>=1.1.0", +] +files = [ + {file = "sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8"}, + {file = "sympy-1.13.1.tar.gz", hash = "sha256:9cebf7e04ff162015ce31c9c6c9144daa34a93bd082f54fd8f12deca4f47515f"}, +] + +[[package]] +name = "threadpoolctl" +version = "3.5.0" +requires_python = ">=3.8" +summary = "threadpoolctl" +files = [ + {file = "threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467"}, + {file = "threadpoolctl-3.5.0.tar.gz", hash = "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107"}, +] + +[[package]] +name = "torch" +version = "2.6.0" +requires_python = ">=3.9.0" +summary = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" +dependencies = [ + "filelock", + "fsspec", + "jinja2", + "networkx", + "nvidia-cublas-cu12==12.4.5.8; platform_system == \"Linux\" and platform_machine == \"x86_64\"", + "nvidia-cuda-cupti-cu12==12.4.127; platform_system == \"Linux\" and platform_machine == \"x86_64\"", + "nvidia-cuda-nvrtc-cu12==12.4.127; platform_system == \"Linux\" and platform_machine == \"x86_64\"", + "nvidia-cuda-runtime-cu12==12.4.127; platform_system == \"Linux\" and platform_machine == \"x86_64\"", + "nvidia-cudnn-cu12==9.1.0.70; platform_system == \"Linux\" and platform_machine == \"x86_64\"", + "nvidia-cufft-cu12==11.2.1.3; platform_system == \"Linux\" and platform_machine == \"x86_64\"", + "nvidia-curand-cu12==10.3.5.147; platform_system == \"Linux\" and platform_machine == \"x86_64\"", + "nvidia-cusolver-cu12==11.6.1.9; platform_system == \"Linux\" and platform_machine == \"x86_64\"", + "nvidia-cusparse-cu12==12.3.1.170; platform_system == \"Linux\" and platform_machine == \"x86_64\"", + "nvidia-cusparselt-cu12==0.6.2; platform_system == \"Linux\" and platform_machine == \"x86_64\"", + "nvidia-nccl-cu12==2.21.5; platform_system == \"Linux\" and platform_machine == \"x86_64\"", + "nvidia-nvjitlink-cu12==12.4.127; platform_system == \"Linux\" and platform_machine == \"x86_64\"", + "nvidia-nvtx-cu12==12.4.127; platform_system == \"Linux\" and platform_machine == \"x86_64\"", + "sympy==1.13.1; python_version >= \"3.9\"", + "triton==3.2.0; platform_system == \"Linux\" and platform_machine == \"x86_64\"", + "typing-extensions>=4.10.0", +] +files = [ + {file = "torch-2.6.0-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:7979834102cd5b7a43cc64e87f2f3b14bd0e1458f06e9f88ffa386d07c7446e1"}, +] + +[[package]] +name = "torchmetrics" +version = "1.4.1" +requires_python = ">=3.8" +summary = "PyTorch native Metrics" +dependencies = [ + "lightning-utilities>=0.8.0", + "numpy>1.20.0", + "packaging>17.1", + "torch>=1.10.0", +] +files = [ + {file = "torchmetrics-1.4.1-py3-none-any.whl", hash = "sha256:c2e7cd56dd8bdc60ae63d712f3bdce649f23bd174d9180bdd0b746e0230b865a"}, +] + +[[package]] +name = "torchvision" +version = "0.21.0" requires_python = ">=3.9" -summary = "Read and write TIFF files" -groups = ["default"] +summary = "image and video datasets and models for torch deep learning" dependencies = [ "numpy", + "pillow!=8.3.*,>=5.3.0", + "torch==2.6.0", +] +files = [ + {file = "torchvision-0.21.0-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:3891cd086c5071bda6b4ee9d266bb2ac39c998c045c2ebcd1e818b8316fb5d41"}, +] + +[[package]] +name = "tqdm" +version = "4.66.5" +requires_python = ">=3.7" +summary = "Fast, Extensible Progress Meter" +files = [ + {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, + {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, ] + +[[package]] +name = "triton" +version = "3.2.0" +summary = "A language and compiler for custom Deep Learning operations" files = [ - {file = "tifffile-2024.8.24-py3-none-any.whl", hash = "sha256:40faba20cb0af05c0eb500eda63244dd81500360e1518ff4548eb61ce3943099"}, - {file = "tifffile-2024.8.24.tar.gz", hash = "sha256:65a77e50cfcf05e3553b52dd13367caad3f88eae7f533188653ce9b2d88b7464"}, + {file = "triton-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8009a1fb093ee8546495e96731336a33fb8856a38e45bb4ab6affd6dbc3ba220"}, ] [[package]] @@ -1085,18 +2092,26 @@ name = "typing-extensions" version = "4.12.2" requires_python = ">=3.8" summary = "Backported and Experimental Type Hints for Python 3.8+" -groups = ["dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] +[[package]] +name = "tzdata" +version = "2024.1" +requires_python = ">=2" +summary = "Provider of IANA time zone data" +files = [ + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, +] + [[package]] name = "urllib3" version = "2.2.2" requires_python = ">=3.8" summary = "HTTP library with thread-safe connection pooling, file post, and more." -groups = ["docs"] files = [ {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, @@ -1107,11 +2122,9 @@ name = "virtualenv" version = "20.26.3" requires_python = ">=3.7" summary = "Virtual Python Environment builder" -groups = ["dev"] dependencies = [ "distlib<1,>=0.3.7", "filelock<4,>=3.12.2", - "importlib-metadata>=6.6; python_version < \"3.8\"", "platformdirs<5,>=3.9.1", ] files = [ @@ -1120,30 +2133,108 @@ files = [ ] [[package]] -name = "watchdog" -version = "5.0.0" -requires_python = ">=3.9" -summary = "Filesystem events monitoring" -groups = ["docs"] -files = [ - {file = "watchdog-5.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3c177085c3d210d1c73cb4569442bdaef706ebebc423bd7aed9e90fc12b2e553"}, - {file = "watchdog-5.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01ab36cddc836a0f202c66267daaef92ba5c17c7d6436deff0587bb61234c5c9"}, - {file = "watchdog-5.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0834c21efa3e767849b09e667274604c7cdfe30b49eb95d794565c53f4db3c1e"}, - {file = "watchdog-5.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1e26f570dd7f5178656affb24d6f0e22ce66c8daf88d4061a27bfb9ac866b40d"}, - {file = "watchdog-5.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d146331e6b206baa9f6dd40f72b5783ad2302c240df68e7fce196d30588ccf7b"}, - {file = "watchdog-5.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6c96b1706430839872a3e33b9370ee3f7a0079f6b828129d88498ad1f96a0f45"}, - {file = "watchdog-5.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:663b096368ed7831ac42259919fdb9e0a1f0a8994d972675dfbcca0225e74de1"}, - {file = "watchdog-5.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:685931412978d00a91a193d9018fc9e394e565e8e7a0c275512a80e59c6e85f8"}, - {file = "watchdog-5.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:109daafc5b0f2a98d1fa9475ff9737eb3559d57b18129a36495e20c71de0b44f"}, - {file = "watchdog-5.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d76efab5248aafbf8a2c2a63cd7b9545e6b346ad1397af8b862a3bb3140787d8"}, - {file = "watchdog-5.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:ff4e957c45c446de34c513eadce01d0b65da7eee47c01dce472dd136124552c9"}, - {file = "watchdog-5.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:16c1aa3377bb1f82c5e24277fcbf4e2cac3c4ce46aaaf7212d53caa9076eb7b7"}, - {file = "watchdog-5.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:22fcad6168fc43cf0e709bd854be5b8edbb0b260f0a6f28f1ea9baa53c6907f7"}, - {file = "watchdog-5.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:0120b2fa65732797ffa65fa8ee5540c288aa861d91447df298626d6385a24658"}, - {file = "watchdog-5.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2aa59fab7ff75281778c649557275ca3085eccbdf825a0e2a5ca3810e977afe5"}, - {file = "watchdog-5.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:78db0fe0336958fc0e1269545c980b6f33d04d184ba191b2800a8b71d3e971a9"}, - {file = "watchdog-5.0.0-py3-none-win32.whl", hash = "sha256:d1acef802916083f2ad7988efc7decf07e46e266916c0a09d8fb9d387288ea12"}, - {file = "watchdog-5.0.0-py3-none-win_amd64.whl", hash = "sha256:3c2d50fdb86aa6df3973313272f5a17eb26eab29ff5a0bf54b6d34597b4dc4e4"}, - {file = "watchdog-5.0.0-py3-none-win_ia64.whl", hash = "sha256:1d17ec7e022c34fa7ddc72aa41bf28c9d1207ffb193df18ba4f6fde453725b3c"}, - {file = "watchdog-5.0.0.tar.gz", hash = "sha256:990aedb9e2f336b45a70aed9c014450e7c4a70fd99c5f5b1834d57e1453a177e"}, +name = "websocket-client" +version = "1.8.0" +requires_python = ">=3.8" +summary = "WebSocket client for Python with low level API options" +files = [ + {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, + {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"}, +] + +[[package]] +name = "werkzeug" +version = "3.0.4" +requires_python = ">=3.8" +summary = "The comprehensive WSGI web application library." +dependencies = [ + "MarkupSafe>=2.1.1", +] +files = [ + {file = "werkzeug-3.0.4-py3-none-any.whl", hash = "sha256:02c9eb92b7d6c06f31a782811505d2157837cea66aaede3e217c7c27c039476c"}, + {file = "werkzeug-3.0.4.tar.gz", hash = "sha256:34f2371506b250df4d4f84bfe7b0921e4762525762bbd936614909fe25cd7306"}, +] + +[[package]] +name = "wrapt" +version = "1.16.0" +requires_python = ">=3.6" +summary = "Module for decorators, wrappers and monkey patching." +files = [ + {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, + {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, + {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, + {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, + {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, + {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, + {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, +] + +[[package]] +name = "yarl" +version = "1.9.4" +requires_python = ">=3.7" +summary = "Yet another URL library" +dependencies = [ + "idna>=2.0", + "multidict>=4.0", +] +files = [ + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, + {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, + {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, + {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, + {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, + {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, + {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, +] + +[[package]] +name = "zipp" +version = "3.20.1" +requires_python = ">=3.8" +summary = "Backport of pathlib-compatible object wrapper for zip files" +files = [ + {file = "zipp-3.20.1-py3-none-any.whl", hash = "sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064"}, + {file = "zipp-3.20.1.tar.gz", hash = "sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b"}, ] diff --git a/preprocessing/mask_generator.py b/preprocessing/mask_generator.py new file mode 100644 index 0000000..aa40f73 --- /dev/null +++ b/preprocessing/mask_generator.py @@ -0,0 +1,69 @@ +from pathlib import Path +import pyvips +import ray +from openslide import PROPERTY_NAME_MPP_X, PROPERTY_NAME_MPP_Y, OpenSlide +from rationai.masks import tissue_mask, write_big_tiff +from rationai.masks import process_items + + +# folders: +# archive tumor cases +# chive negative cases +# Prospective negative cases +# Prospective test cases +# Prospective tumor cases + +SLIDES_PATH = "/mnt/data/scans/AI scans/Prostata/" +MASK_DEST = "./data/tissue_masks" +LEVEL = 3 + + +@ray.remote +def process_slide(slide_path: Path) -> None: + # pyvips.Image xres and yres variables don't respect level therefore we + # extract proper spatial resulution based on desired level using OpenSlide + with OpenSlide(slide_path) as slide: + downsample = slide.level_downsamples[LEVEL] + xres = 1000 / \ + (float(slide.properties[PROPERTY_NAME_MPP_X]) * downsample) + yres = 1000 / \ + (float(slide.properties[PROPERTY_NAME_MPP_Y]) * downsample) + + slide = pyvips.Image.new_from_file(slide_path, level=LEVEL) + mask = tissue_mask(slide) + mask_path = Path(MASK_DEST, f"{Path(slide_path).stem}.tiff") + mask_path.parent.mkdir(exist_ok=True, parents=True) + write_big_tiff(mask, path=mask_path, xres=xres, yres=yres) + + +BROKEN_SLIDES = { + 'P-2016_3829-04-1.mrxs', 'P-2016_3732-06-1.mrxs', 'P-2016_3732-03-1.mrxs', 'P-2016_3760-06-1.mrxs', 'P-2016_3629-13-1.mrxs', 'P-2016_3926-03-1.mrxs', + 'P-2016_3852-02-1.mrxs', 'P-2016_3988-07-1.mrxs', 'P-2016_3852-01-1.mrxs', + 'P-2016_3851-02-1.mrxs', 'P-2016_3629-12-1.mrxs', 'P-2016_3667-10-0.mrxs', 'P-2016_3606-04-1.mrxs', 'P-2016_3597-10-1.mrxs', 'P-2016_3988-10-1.mrxs', 'P-2016_3829-01-1.mrxs', 'P-2016_3926-02-1.mrxs', 'P-2019_3025-03-1.mrxs', + 'P-2016_3760-04-1.mrxs', 'P-2016_3732-04-1.mrxs', 'P-2016_3667-09-1.mrxs', 'P-2019_3292-06-1.mrxs', 'P-2016_3988-02-1.mrxs', 'P-2016_3667-11-0.mrxs', 'P-2016_3606-05-1.mrxs', 'P-2016_3851-09-1.mrxs', 'P-2016_3627-09-1.mrxs', 'P-2016_3606-03-1.mrxs', 'P-2016_3627-10-1.mrxs', 'P-2016_3852-03-1.mrxs', 'P-2016_3851-07-1.mrxs', 'P-2016_3829-03-1.mrxs', 'P-2016_3627-06-1.mrxs', 'P-2016_3597-09-1.mrxs', 'P-2016_3926-01-1.mrxs', 'P-2016_3629-11-1.mrxs', 'P-2016_3760-03-1.mrxs'} + +def main() -> None: + folders = [ + # "archive tumor cases", #DONE + # "archive negative cases", # DONE + # "Prospective negative cases", #DONE + # "Prospective test cases", # DONE + # "Prospective tumor cases" # DONE + ] + + for folder in folders: + slides = [] + for slide in Path(SLIDES_PATH, folder).rglob("*.mrxs"): + if slide.name in BROKEN_SLIDES: + continue + slides.append(slide) + # slides.extend(list(Path(SLIDES_PATH, folder).rglob("*.mrxs"))) + + # slides = list(Path(SLIDES_PATH).rglob("*.mrxs")) + # process_slide(slides[0]) + process_items(slides, process_item=process_slide) + print(f"Processed {len(slides)} slides from {folder} folder") + + +if __name__ == "__main__": + main() diff --git a/preprocessing/tiler.py b/preprocessing/tiler.py new file mode 100644 index 0000000..fb54ac8 --- /dev/null +++ b/preprocessing/tiler.py @@ -0,0 +1,120 @@ +from pathlib import Path +import mlflow +import ray +from rationai.tiling import tiling +from rationai.tiling.modules.masks import PyvipsMask +from rationai.tiling.modules.tile_sources import OpenSlideTileSource +from rationai.tiling.typing import TiledSlideMetadata, TileMetadata +from rationai.tiling.writers import save_mlflow_dataset +from sklearn.model_selection import train_test_split + + +SLIDES_PATH = "/mnt/data/scans/AI scans/Prostata/" +TISSUE_MASKS_PATH = "./data/tissue_masks" + + +# level avg_mpp_x avg_mpp_y +# 0 0.233876 0.234331 +# 1 0.467751 0.468661 +# 2 0.935503 0.937323 +# 3 1.871006 1.874646 +# 4 3.742012 3.749291 +# 5 7.484024 7.498583 +# 6 14.968047 14.997165 +# 7 29.936095 29.994331 +# 8 59.872189 59.988661 +# 9 119.744379 119.97732 +SlideMPP = 0.46 + +source = OpenSlideTileSource(mpp=SlideMPP, tile_extent=512, stride=256) + + +TISSUE_PERCENTAGE = 0.5 + + +class TissueMask(PyvipsMask[TileMetadata]): + def forward_tile( + self, tile_labels: TileMetadata, class_overlaps: dict[int, float] + ) -> TileMetadata | None: + if class_overlaps.get(0, 0) > TISSUE_PERCENTAGE: + return None + return tile_labels + + +tissue_mask = TissueMask( + tile_extent=source.tile_extent, absolute_roi_extent=256, relative_roi_offset=0 +) + + +@ray.remote +def handler(slide_path: Path) -> TiledSlideMetadata: + slide, tiles = source(slide_path) + + tissue_mask_path = Path(TISSUE_MASKS_PATH, slide_path.name[:-5] + ".tiff") + tiles = tissue_mask(tissue_mask_path, slide.extent, tiles) + + return slide, tiles + +BROKEN_SLIDES = { + 'P-2016_3829-04-1.mrxs', 'P-2016_3732-06-1.mrxs', 'P-2016_3732-03-1.mrxs', 'P-2016_3760-06-1.mrxs', 'P-2016_3629-13-1.mrxs', 'P-2016_3926-03-1.mrxs', + 'P-2016_3852-02-1.mrxs', 'P-2016_3988-07-1.mrxs', 'P-2016_3852-01-1.mrxs', + 'P-2016_3851-02-1.mrxs', 'P-2016_3629-12-1.mrxs', 'P-2016_3667-10-0.mrxs', 'P-2016_3606-04-1.mrxs', 'P-2016_3597-10-1.mrxs', 'P-2016_3988-10-1.mrxs', 'P-2016_3829-01-1.mrxs', 'P-2016_3926-02-1.mrxs', 'P-2019_3025-03-1.mrxs', + 'P-2016_3760-04-1.mrxs', 'P-2016_3732-04-1.mrxs', 'P-2016_3667-09-1.mrxs', 'P-2019_3292-06-1.mrxs', 'P-2016_3988-02-1.mrxs', 'P-2016_3667-11-0.mrxs', 'P-2016_3606-05-1.mrxs', 'P-2016_3851-09-1.mrxs', 'P-2016_3627-09-1.mrxs', 'P-2016_3606-03-1.mrxs', 'P-2016_3627-10-1.mrxs', 'P-2016_3852-03-1.mrxs', 'P-2016_3851-07-1.mrxs', 'P-2016_3829-03-1.mrxs', 'P-2016_3627-06-1.mrxs', 'P-2016_3597-09-1.mrxs', 'P-2016_3926-01-1.mrxs', 'P-2016_3629-11-1.mrxs', 'P-2016_3760-03-1.mrxs'} + +def main() -> None: + folders = [ + "archive tumor cases", + "archive negative cases", + "Prospective negative cases", + "Prospective test cases", + "Prospective tumor cases" + ] + + slides = [] + for folder in folders: + for slide in Path(SLIDES_PATH, folder).rglob("*.mrxs"): + if slide.name in BROKEN_SLIDES: + continue + slides.append(slide) + + slides, test_slides = train_test_split( + slides, test_size=0.2 + ) + train_slides, val_slides = train_test_split(slides, test_size=0.1) + + train_slides_df, train_tiles_df = tiling( + slides=train_slides, handler=handler) + val_slides_df, val_tiles_df = tiling( + slides=list(val_slides), handler=handler) + test_slides_df, test_tiles_df = tiling( + slides=list(test_slides), handler=handler) + + train_slides_df.to_csv("./data/datasets/train_slides.csv", index=False) + train_tiles_df.to_csv("./data/datasets/train_tiles.csv", index=False) + + val_slides_df.to_csv("./data/datasets/val_slides.csv", index=False) + val_tiles_df.to_csv("./data/datasets/val_tiles.csv", index=False) + + test_slides_df.to_csv("./data/datasets/test_slides.csv", index=False) + test_tiles_df.to_csv("./data/datasets/test_tiles.csv", index=False) + + mlflow.set_experiment(experiment_name="Stain Normalization") + with mlflow.start_run(run_name="Stain Normalization Dataset") as _: + save_mlflow_dataset( + slides=train_slides_df, + tiles=train_tiles_df, + dataset_name="Stain Normalization - train", + ) + save_mlflow_dataset( + slides=val_slides_df, tiles=val_tiles_df, dataset_name="Stain Normalization - val" + ) + save_mlflow_dataset( + slides=test_slides_df, + tiles=test_tiles_df, + dataset_name="Stain Normalization - test", + ) + + +if __name__ == "__main__": + # pass + main() diff --git a/pyproject.toml b/pyproject.toml index aa64ea2..64d0f5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,27 +1,43 @@ [project] -name = "rationai-staining" +name = "project-name" version = "0.1.0" -description = "" -authors = [{ name = "Tomáš Balák" }] +authors = [] requires-python = ">=3.11" readme = "README.md" license = { file = "LICENSE" } -dependencies = ["numpy>=1.26.4", "pillow>=10.3.0", "scikit-image>=0.23.2"] +dependencies = [ + "torch>=2.4.0", + "lightning>=2.4.0", + "torchmetrics>=1.4.1", + "rationai-mlkit @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/mlkit.git", + "rationai-masks @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git", + "rationai-tiling @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git", + "albumentations>=2.0.4", + "scikit-learn>=1.6.1", + "torchvision>=0.21.0", + "rationai-kube-jobs @ git+ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/kube-jobs.git", +] [tool.pdm.dev-dependencies] dev = ["mypy", "pre-commit", "ruff"] -test = ["pytest", "pytest-cov", "perlin-noise>=1.12"] -docs = ["mkdocs-material>=9.5.32", "mkdocstrings[python]>=0.25.2"] [tool.pdm.scripts] +mask_generate = "python preprocessing/mask_generator.py" +tiler = "python preprocessing/tiler.py" + +train = "python -m project_name mode=fit" +validate = "python -m project_name mode=validate" +test = "python -m project_name mode=test" +predict = "python -m project_name mode=predict" l = { composite = ["lint", "format", "mypy"] } -test = "pytest tests" lint = "ruff check --fix" format = "ruff format" mypy = "mypy ." -docs = "mkdocs serve" post_install = { composite = [ "pre-commit autoupdate", "pre-commit install", "pre-commit install --hook-type commit-msg", ] } + +[tool.pdm] +distribution = false diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 276b351..0000000 --- a/pytest.ini +++ /dev/null @@ -1,6 +0,0 @@ -[pytest] -markers = - color_conversion: tests expected values and method behavior of the ColorConversion Enum - stain_separation: tests expected behavior of the stain separation functionality - stain_reconstruction: tests that applying color combination after color separation return yields roughly the same image - stain_estimator: tests stain estimation functionality \ No newline at end of file diff --git a/rationai/staining/__init__.py b/rationai/staining/__init__.py deleted file mode 100644 index 12ba09e..0000000 --- a/rationai/staining/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -from rationai.staining.convert_color import ( - ColorConversion, - ConversionType, - convert_color, -) -from rationai.staining.estimate_stain_vectors import estimate_stain_vectors -from rationai.staining.modify_and_reconstruct import modify_and_reconstruct - - -__all__ = [ - "ColorConversion", - "ConversionType", - "convert_color", - "estimate_stain_vectors", - "modify_and_reconstruct", -] diff --git a/rationai/staining/constants.py b/rationai/staining/constants.py deleted file mode 100644 index 46b81eb..0000000 --- a/rationai/staining/constants.py +++ /dev/null @@ -1,27 +0,0 @@ -QUPATH_H = (0.651, 0.701, 0.290) -""" -QuPath value for default Hematoxylin staining vector. - -Note: - QuPath uses this value by default, even in the H&DAB protocol. - However, this value does not resemble the actual color - of Hematoxylin present in H&DAB-stained slides. - It is therefore advised to use the `LIGHT_H` value when working with - the H&DAB protocol. -""" - -LIGHT_H = (0.849, 0.514, 0.124) -""" -Value for a light blue Hematoxylin staining vector -present in the H&DAB protocol. -""" - -QUPATH_E = (0.216, 0.801, 0.558) -""" -QuPath value for default Eosin staining vector. -""" - -QUPATH_DAB = (0.269, 0.568, 0.778) -""" -QuPath value for default DAB staining vector. -""" diff --git a/rationai/staining/convert_color.py b/rationai/staining/convert_color.py deleted file mode 100644 index 21ac72b..0000000 --- a/rationai/staining/convert_color.py +++ /dev/null @@ -1,197 +0,0 @@ -from collections.abc import Sequence -from enum import Enum, auto, unique -from typing import overload - -import numpy as np -from PIL.Image import Image -from skimage.color import combine_stains - -from rationai.staining.constants import LIGHT_H, QUPATH_DAB, QUPATH_E, QUPATH_H -from rationai.staining.typing import ( - RGBArray, - StainArray, -) -from rationai.staining.utils import inv_mat, residual - - -@unique -class ConversionType(Enum): - """Enum representing different types of conversions.""" - - STAIN2RGB = auto() - RGB2STAIN = auto() - - -@unique -class ColorConversion(Enum): - """Enum representing different color conversions. - - Value of the enum is a tuple with deconvolution matrix and - the type of the conversion. - - Note: - Stain channels are denoted by their first letters - (H for hematoxylin, D for DAB, ...) and residual channel - is denoted by the letter R. - """ - - HER2RGB = ( - (QUPATH_H, QUPATH_E, residual(QUPATH_H, QUPATH_E)), - ConversionType.STAIN2RGB, - ) - """**Combines** `Hematoxylin`, `Eosin` and `Residual` channels **to RGB**.""" - - HDR2RGB = ( - (LIGHT_H, QUPATH_DAB, residual(LIGHT_H, QUPATH_DAB)), - ConversionType.STAIN2RGB, - ) - """**Combines** `Hematoxylin`, `DAB` and `Residual` channels **to RGB**.""" - - HDR2RGB_LEGACY = ( - (QUPATH_H, QUPATH_DAB, residual(QUPATH_H, QUPATH_DAB)), - ConversionType.STAIN2RGB, - ) - """**Combines** `Hematoxylin`, `DAB` and `Residual` channels **to RGB**. - - This conversion uses the Hematoxylin vector from QuPath, and it is no longer - recommended and is marked as legacy as it did not - provide expected results for staining detection and separation. - """ - - RGB2HDR = (inv_mat(HDR2RGB[0]), ConversionType.RGB2STAIN) - """**Separates RGB image** into `Hematoxylin`, `DAB` and `Residual` channels.""" - - RGB2HER = (inv_mat(HER2RGB[0]), ConversionType.RGB2STAIN) - """**Separates RGB image** into `Hematoxylin`, `Eosin` and `Residual` channels.""" - - RGB2HDR_LEGACY = (inv_mat(HDR2RGB_LEGACY[0]), ConversionType.RGB2STAIN) - """**Separates RGB image** into `Hematoxylin`, `DAB` and `Residual` channels. - - This conversion uses the Hematoxylin vector from QuPath, and it is no longer - recommended and is marked as legacy as it did not - provide expected results for staining detection and separation. - """ - - @property - def conv_type(self) -> ConversionType: - """Returns conversion type associated with the color conversion. - - Returns: - Conversion type associated with the color conversion. - """ - return self.value[1] - - @property - def matrix(self) -> StainArray: - """Returns the real form of the color deconvolution matrix. - - Returns: - Deconvolution matrix (3x3) represented by the color conversion - as a numpy array. - """ - return np.stack(self.value[0]) - - @property - def inverse(self) -> "ColorConversion": - """Returns the inverse color conversion. - - Returns: - Inverse color conversion (i. e., RGB2HER is inverse to HER2RGB). - """ - match self: - # H&E Protocol - case ColorConversion.RGB2HER: - return ColorConversion.HER2RGB - case ColorConversion.HER2RGB: - return ColorConversion.RGB2HER - - # H&DAB Protocol - case ColorConversion.RGB2HDR: - return ColorConversion.HDR2RGB - case ColorConversion.HDR2RGB: - return ColorConversion.RGB2HDR - - # Legacy H&DAB Protocol - case ColorConversion.RGB2HDR_LEGACY: - return ColorConversion.HDR2RGB_LEGACY - case ColorConversion.HDR2RGB_LEGACY: - return ColorConversion.RGB2HDR_LEGACY - - -def _separate_stains( - img: RGBArray | Image, rgb2stain: StainArray, keep_negative_values: bool = False -) -> StainArray: - values = np.maximum(np.asarray(img).astype(np.float64) / 255, 1e-6) - stains = (np.log(values) / np.log(1e-6)) @ rgb2stain - - if not keep_negative_values: - stains = np.maximum(stains, 0) - - return stains - - -@overload -def convert_color( - tile: RGBArray | Image, - conversion: ColorConversion, - keep_negative_values: bool = False, -) -> tuple[StainArray, ...]: ... - - -@overload -def convert_color( - tile: StainArray | Sequence[StainArray], - conversion: ColorConversion, - keep_negative_values: bool = False, -) -> RGBArray: ... - - -def convert_color( - tile: RGBArray | Image | StainArray | Sequence[StainArray], - conversion: ColorConversion, - keep_negative_values: bool = False, -) -> RGBArray | tuple[StainArray, ...]: - """Converts a tile into the specified color space. - - Based on the provided conversion enum, either color deconvolution - or stain channel combination is performed. - - Note: - Due to implementation details, it is **NOT GUARANTEED** that - `convert_tile(convert_tile(I, conv), conv.inverse) == I`. Difference between the - images can also vary depending on the used conversion. - - Args: - tile: Tile that should be converted. Can either be a RGB image or three stain - channels stacked along the last axis. Stain channels can be stacked using - the following code: - ```python - stacked_stains = np.stack([c0, c1, c2], axis=-1) - ``` - Here, `c[0-2]` are the individual stain channels. - - conversion: Desired color conversion. - - keep_negative_values: Decides if the negative values produced by color separation - should be kept. - - Returns: - Depending on the conversion, the result can either be a RGB image, - or a tuple of the separated stain channels. - """ - match conversion.conv_type: - case ConversionType.RGB2STAIN: - return tuple( - np.moveaxis( - _separate_stains(tile, conversion.matrix, keep_negative_values), # type: ignore[arg-type] - -1, - 0, - ) - ) - - case ConversionType.STAIN2RGB: - if isinstance(tile, Sequence): - tile = np.stack(tile, axis=-1) - - tile = np.asarray(tile, dtype=np.float64) - return (255 * combine_stains(tile, conversion.matrix)).astype(np.uint8) diff --git a/rationai/staining/estimate_stain_vectors.py b/rationai/staining/estimate_stain_vectors.py deleted file mode 100644 index 83f0540..0000000 --- a/rationai/staining/estimate_stain_vectors.py +++ /dev/null @@ -1,63 +0,0 @@ -import numpy as np -from PIL.Image import Image - -from rationai.staining.typing import StainArray, Tile -from rationai.staining.utils import normalize - - -def estimate_stain_vectors( - img: Tile, i0: int = 240, alpha: int = 1, beta: float = 0.15 -) -> StainArray: - """Detects two most dominant staining vectors in the image. - - Args: - img: Stained image with three RGB channels. - i0: Transmitted light intensity. Defaults to 240. - alpha: Percentile offset for robust estimation. Defaults to 1. - beta: Threshold for transparent pixels in the OD-space. Defaults to 0.15. - - Returns: - Array with two most dominant color vectors. - - References: - Adapted from: `https://github.com/schaugf/HEnorm_python` - - Paper: A method for normalizing histology slides for quantitative analysis, - M Macenko, M Niethammer, JS Marron, D Borland, JT Woosley, G Xiaojun, - C Schmitt, NE Thomas, IEEE ISBI, 2009. dx.doi.org/10.1109/ISBI.2009.5193250 - """ - if isinstance(img, Image): - img = np.asarray(img) - - img = img.reshape((-1, 3)) - - # Calculate the optical density - od = -np.log((img.astype(np.float64) + 1) / i0) # (H*W, 3) - - # remove transparent pixels - od_hat = od[~np.any(od < beta, axis=-1)] - - if od_hat.size <= 6: - # Not enough stained pixels remained, return empty vectors - return np.full(shape=(2, 3), fill_value=np.nan) - - # Compute eigenvectors - _, eigvecs = np.linalg.eigh(np.cov(od_hat.T)) - - # Project on the plane spanned by the eigenvectors - # corresponding to the two largest eigenvalues. - t_hat = od_hat.dot(eigvecs[:, 1:3]) - - phi = np.arctan2(t_hat[:, 1], t_hat[:, 0]) - - min_phi = np.percentile(phi, alpha) - max_phi = np.percentile(phi, 100 - alpha) - - v_min = eigvecs[:, 1:3].dot(np.array([(np.cos(min_phi), np.sin(min_phi))]).T) - v_max = eigvecs[:, 1:3].dot(np.array([(np.cos(max_phi), np.sin(max_phi))]).T) - - # Make hematoxylin first and eosin second (heuristic) - if v_min[0] > v_max[0]: - return np.array((normalize(v_min[:, 0]), normalize(v_max[:, 0]))) - - return np.array((normalize(v_max[:, 0]), normalize(v_min[:, 0]))) diff --git a/rationai/staining/modify_and_reconstruct.py b/rationai/staining/modify_and_reconstruct.py deleted file mode 100644 index 10d3bab..0000000 --- a/rationai/staining/modify_and_reconstruct.py +++ /dev/null @@ -1,73 +0,0 @@ -import numpy as np -from skimage.color import combine_stains - -from rationai.staining.typing import ModifyFunction, RGBArray, StainTuple -from rationai.staining.utils.inv_mat import inv_mat -from rationai.staining.utils.residual import residual - - -def modify_and_reconstruct( - tile: RGBArray, - modify: ModifyFunction, - stain0: StainTuple, - stain1: StainTuple, - stain2: StainTuple | None = None, -) -> RGBArray: - """Modifies RGB tile in stain space and converts back to RGB. - - Args: - tile: Input RGB representation of the region. - modify: Function that takes three channels in stain space - and returns their modified versions. - stain0: First default color vector. - stain1: Second default color vector. - stain2: Third default color vector. If not provided, - a residual vector is computed from the first two. - - Returns: - Modified region in RGB space. - - Note: - - To ensure as precise reconstruction as possible, - **clipping of negative values is ommited** from stain separation. - - References: - Stain separation is adapted from skimage.color.separate_stains. - - Example: - ```python - import numpy as np - from skimage.data import immunohistochemistry - - from rationai.staining import modify_and_reconstruct - from rationai.staining.constants import QUPATH_DAB, QUPATH_H - - - def modify(c0, c1, c2): - # Remove second channel from the image - c1 = np.zeros_like(c1) - - return c0, c1, c2 - - - original = immunohistochemistry() - modified = modify_and_reconstruct(original, modify, QUPATH_H, QUPATH_DAB) - ``` - """ - if stain2 is None: - stain2 = residual(stain0, stain1) - - mat = (stain0, stain1, stain2) - - stain2rgb = np.stack(mat) - rgb2stain = np.stack(inv_mat(mat)) - - values = np.maximum(tile.astype(np.float64) / 255, 1e-6) - log_adjust = np.log(1e-6) - - stains = (np.log(values) / log_adjust) @ rgb2stain - c0, c1, c2 = tuple(np.moveaxis(stains, -1, 0)) - - modified_stains = np.stack(modify(c0, c1, c2), axis=-1) - - return (255 * combine_stains(modified_stains, stain2rgb)).astype(np.uint8) diff --git a/rationai/staining/scripts/remove_red_stain.py b/rationai/staining/scripts/remove_red_stain.py deleted file mode 100644 index f413265..0000000 --- a/rationai/staining/scripts/remove_red_stain.py +++ /dev/null @@ -1,38 +0,0 @@ -from pathlib import Path -from typing import TypeAlias - -import numpy as np -from numpy.typing import NDArray -from PIL import Image - -from rationai.staining.modify_and_reconstruct import modify_and_reconstruct - - -Channel: TypeAlias = NDArray[np.float64] - - -DIRECTORY_PATH = "/mnt/data/Projects/MOU/Bad_scans/stain separation/" -FILES = Path(DIRECTORY_PATH).glob("*.JPG") - -# STAIN DEFINITIONS -HEMATOXYLIN = (0.849, 0.514, 0.124) -DAB = (0.262, 0.631, 0.730) -RED = (0.128, 0.875, 0.466) - - -def modify(c0: Channel, c1: Channel, c2: Channel) -> tuple[Channel, Channel, Channel]: - c2 = np.zeros_like(c2) - - return c0, c1, c2 - - -def main() -> None: - for file in FILES: - img = np.asarray(Image.open(file).convert("RGB")) - modified = modify_and_reconstruct(img, modify, HEMATOXYLIN, DAB, RED) - - Image.fromarray(modified).save(f"{file.stem}.png") - - -if __name__ == "__main__": - main() diff --git a/rationai/staining/typing.py b/rationai/staining/typing.py deleted file mode 100644 index 66f443f..0000000 --- a/rationai/staining/typing.py +++ /dev/null @@ -1,47 +0,0 @@ -from collections.abc import Callable -from typing import TypeAlias - -from numpy import float64, uint8 -from numpy.typing import NDArray -from PIL.Image import Image - - -ModifyFunction: TypeAlias = Callable[ - [NDArray[float64], NDArray[float64], NDArray[float64]], - tuple[NDArray[float64], NDArray[float64], NDArray[float64]], -] -""" -Type definition for function that modifies stain channels. - -The function takes three arrays in stain space and returns their modified versions. -""" - -Tile: TypeAlias = NDArray[float64 | uint8] | Image -""" -Type definition for a single tile. -""" - -StainArray: TypeAlias = NDArray[float64] -""" -Type definition for an array of stain values. -""" - -StainChannels: TypeAlias = tuple[StainArray, StainArray, StainArray] -""" -Type definition for all three deconvoluted stain channels. -""" - -StainTuple: TypeAlias = tuple[float, float, float] -""" -Type definition for a tuple of stain values. -""" - -StainTupleMatrix: TypeAlias = tuple[StainTuple, StainTuple, StainTuple] -""" -Type definition for a stain matrix represented by a tuple of tuples. -""" - -RGBArray: TypeAlias = NDArray[uint8] -""" -Type definition for an array of RGB values. -""" diff --git a/rationai/staining/utils/__init__.py b/rationai/staining/utils/__init__.py deleted file mode 100644 index ca302ed..0000000 --- a/rationai/staining/utils/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from rationai.staining.utils.inv_mat import inv_mat -from rationai.staining.utils.normalize import normalize -from rationai.staining.utils.residual import residual - - -__all__ = ["inv_mat", "normalize", "residual"] diff --git a/rationai/staining/utils/inv_mat.py b/rationai/staining/utils/inv_mat.py deleted file mode 100644 index 4ec3419..0000000 --- a/rationai/staining/utils/inv_mat.py +++ /dev/null @@ -1,8 +0,0 @@ -import numpy as np - -from rationai.staining.typing import StainTupleMatrix - - -def inv_mat(matrix: StainTupleMatrix) -> StainTupleMatrix: - mat = np.linalg.inv(np.stack(matrix)) - return tuple(tuple(row) for row in mat) # type: ignore [return-value] diff --git a/rationai/staining/utils/normalize.py b/rationai/staining/utils/normalize.py deleted file mode 100644 index d0acb37..0000000 --- a/rationai/staining/utils/normalize.py +++ /dev/null @@ -1,9 +0,0 @@ -from numpy import float64 -from numpy.linalg import norm -from numpy.typing import NDArray - - -def normalize(x: NDArray[float64]) -> NDArray[float64]: - """Normalizes a given vector to unit length.""" - n = norm(x) - return x if n == 0 else x / n diff --git a/rationai/staining/utils/residual.py b/rationai/staining/utils/residual.py deleted file mode 100644 index a031289..0000000 --- a/rationai/staining/utils/residual.py +++ /dev/null @@ -1,12 +0,0 @@ -import numpy as np -from numpy import float64 - -from rationai.staining.typing import StainTuple -from rationai.staining.utils.normalize import normalize - - -def residual(c0: StainTuple, c1: StainTuple) -> StainTuple: - c0_arr = np.array(c0, dtype=float64) - c1_arr = np.array(c1, dtype=float64) - - return tuple(np.round(normalize(np.cross(c0_arr, c1_arr)), decimals=3)) diff --git a/stain_normalization/__init__.py b/stain_normalization/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/stain_normalization/data/__init__.py b/stain_normalization/data/__init__.py new file mode 100644 index 0000000..48b179a --- /dev/null +++ b/stain_normalization/data/__init__.py @@ -0,0 +1,4 @@ +from stain_normalization.data.data_module import DataModule + + +__all__ = ["DataModule"] diff --git a/stain_normalization/data/data_module.py b/stain_normalization/data/data_module.py new file mode 100644 index 0000000..40147f6 --- /dev/null +++ b/stain_normalization/data/data_module.py @@ -0,0 +1,51 @@ +from collections.abc import Iterable + +from hydra.utils import instantiate +from lightning import LightningDataModule +from omegaconf import DictConfig +from torch.utils.data import DataLoader + +from stain_normalization.typing import Input + + +class DataModule(LightningDataModule): + def __init__( + self, batch_size: int, num_workers: int = 0, **datasets: DictConfig + ) -> None: + super().__init__() + self.batch_size = batch_size + self.num_workers = num_workers + self.datasets = datasets + + def setup(self, stage: str) -> None: + match stage: + case "fit": + self.train = instantiate(self.datasets["train"]) + self.val = instantiate(self.datasets["val"]) + case "validate": + self.val = instantiate(self.datasets["val"]) + case "test": + self.test = instantiate(self.datasets["test"]) + + def train_dataloader(self) -> Iterable[Input]: + return DataLoader( + self.train, + batch_size=self.batch_size, + shuffle=True, + drop_last=True, + num_workers=self.num_workers, + persistent_workers=self.num_workers > 0, + ) + + def val_dataloader(self) -> Iterable[Input]: + return DataLoader( + self.val, + batch_size=self.batch_size, + num_workers=self.num_workers, + persistent_workers=self.num_workers > 0, + ) + + def test_dataloader(self) -> Iterable[Input]: + return DataLoader( + self.test, batch_size=self.batch_size, num_workers=self.num_workers + ) diff --git a/stain_normalization/data/datasets/__init__.py b/stain_normalization/data/datasets/__init__.py new file mode 100644 index 0000000..32125cf --- /dev/null +++ b/stain_normalization/data/datasets/__init__.py @@ -0,0 +1,4 @@ +from stain_normalization.data.datasets.stain_normalization import StainNormalization, StainNormalizationPredict + + +__all__ = ["StainNormalization", "StainNormalizationPredict"] diff --git a/stain_normalization/data/datasets/stain_normalization.py b/stain_normalization/data/datasets/stain_normalization.py new file mode 100644 index 0000000..e2468c3 --- /dev/null +++ b/stain_normalization/data/datasets/stain_normalization.py @@ -0,0 +1,114 @@ +from collections.abc import Iterable + +import pandas as pd +from albumentations import TransformType +from albumentations.pytorch import ToTensorV2 +from rationai.mlkit.data.datasets import MetaTiledSlides, OpenSlideTilesDataset +from torch.utils.data import Dataset + +from stain_normalization.typing import Metadata, PredictSample, Sample + + +class StainNormalization(MetaTiledSlides[Sample]): + def __init__( + self, + uris: Iterable[str], + modify:TransformType, + transforms: TransformType | None = None, + + ) -> None: + self.modify = modify + self.transforms = transforms + super().__init__(uris=uris) + + def generate_datasets(self) -> Iterable[Dataset[Sample]]: + return ( + _StainNormalizationSlideTiles( + slide, + tiles=self.filter_tiles_by_slide(slide["id"]), + include_target=True, + modify=self.modify, + transforms=self.transforms, + ) + for _, slide in self.slides.iterrows() + ) + + +class StainNormalizationPredict(MetaTiledSlides[PredictSample]): + def __init__( + self, + uris: Iterable[str], + modify:TransformType, + transforms: TransformType | None = None, + + ) -> None: + self.modify = modify + self.transforms = transforms + super().__init__(uris=uris) + + def generate_datasets(self) -> Iterable[Dataset[PredictSample]]: + return ( + _StainNormalizationSlideTiles( + slide, + tiles=self.filter_tiles_by_slide(slide["id"]), + include_target=False, + modify=self.modify, + transforms=self.transforms, + ) + for _, slide in self.slides.iterrows() + ) + + +class _StainNormalizationSlideTiles(Dataset[Sample | PredictSample]): + def __init__( + self, + slide_metadata: pd.Series, + tiles: pd.DataFrame, + include_target: bool, + transforms: TransformType | None = None, + modify:TransformType | None = None, + ) -> None: + super().__init__() + self.slide_tiles = OpenSlideTilesDataset( + slide_path=slide_metadata.path, + level=slide_metadata.level, + tile_extent_x=slide_metadata.tile_extent_x, + tile_extent_y=slide_metadata.tile_extent_y, + tiles=tiles, + ) + self.transforms = transforms + self.include_target = include_target + self.to_tensor = ToTensorV2() + self.modify = modify + + def __len__(self) -> int: + return len(self.slide_tiles) + + def __getitem__(self, idx: int) -> Sample | PredictSample: + original_image = self.slide_tiles[idx] + + + if self.transforms is not None: + original_image = self.transforms(image=original_image)["image"] + + modified_image = self.modify(original_image)["image"] + + # modification_name = "Original" + # if self.modify: + # out = self.modify(original_image) + # modified_image = out["image"] + # modification_name = out["modification_name"] + # metadata = Metadata( + # slide=self.slide_tiles.slide_path.stem, + # x=self.slide_tiles.tiles.iloc[idx]["x"], + # y=self.slide_tiles.tiles.iloc[idx]["y"], + # modification=modification_name + # ) + + original_image = self.to_tensor(image=original_image)["image"] + modified_image = self.to_tensor(image=modified_image)["image"] + + if self.include_target: + return modified_image, original_image + + return modified_image diff --git a/stain_normalization/data/transformation/__init__.py b/stain_normalization/data/transformation/__init__.py new file mode 100644 index 0000000..5e28fc7 --- /dev/null +++ b/stain_normalization/data/transformation/__init__.py @@ -0,0 +1,7 @@ +from stain_normalization.data.transformation.hed_factor import HEDFactor +from stain_normalization.data.transformation.exposure_adjustment import ExposureAdjustment +from stain_normalization.data.transformation.hue_shift import HueShift +from stain_normalization.data.transformation.combiend_modification import CombinedModifications + +__all__ = ["HEDFactor", "ExposureAdjustment", "HueShift", "CombinedModifications"] + diff --git a/stain_normalization/data/transformation/combiend_modification.py b/stain_normalization/data/transformation/combiend_modification.py new file mode 100644 index 0000000..d893f3f --- /dev/null +++ b/stain_normalization/data/transformation/combiend_modification.py @@ -0,0 +1,23 @@ +import numpy as np +from albumentations import ImageOnlyTransform +from skimage import exposure + + +class CombinedModifications(ImageOnlyTransform): + def __init__(self, intensity_range=(0.4, 1.5), brightness_range=(-0.4, 0.4), always_apply=True, p=1.0): + super().__init__(always_apply, p) + self.intensity_range = intensity_range + self.brightness_range = brightness_range + + def apply(self, img, **params): + def modify_channel(channel, intensity_range, brightness_range): + intensity_scale = np.random.uniform(*intensity_range) + channel = channel * intensity_scale + brightness_shift = np.random.uniform(*brightness_range) + channel = exposure.adjust_gamma(channel, gamma=1 + brightness_shift) + return np.clip(channel, 0, 1) + + h = modify_channel(img[:, :, 0], self.intensity_range, self.brightness_range) + e = modify_channel(img[:, :, 1], self.intensity_range, self.brightness_range) + d = img[:, :, 2] # Skip modification for D channel + return np.stack((h, e, d), axis=-1) \ No newline at end of file diff --git a/stain_normalization/data/transformation/exposure_adjustment.py b/stain_normalization/data/transformation/exposure_adjustment.py new file mode 100644 index 0000000..71af622 --- /dev/null +++ b/stain_normalization/data/transformation/exposure_adjustment.py @@ -0,0 +1,17 @@ +import numpy as np +from albumentations import ImageOnlyTransform + + +class ExposureAdjustment(ImageOnlyTransform): + def __init__(self, brightness_range: tuple[float, float] = (0.8, 1.2), always_apply: bool = True, p: float = 1): + super().__init__(always_apply, p) + self.brightness_range = brightness_range + + def apply(self, img, **params): + brightness_factor = np.random.uniform(*self.brightness_range) + h, e, d = img[:, :, 0], img[:, :, 1], img[:, :, 2] + h = np.clip(h * brightness_factor, 0, 1) + e = np.clip(e * brightness_factor, 0, 1) + d = np.clip(d * brightness_factor, 0, 1) + return np.stack((h, e, d), axis=-1) + diff --git a/stain_normalization/data/transformation/hed_factor.py b/stain_normalization/data/transformation/hed_factor.py new file mode 100644 index 0000000..13d668b --- /dev/null +++ b/stain_normalization/data/transformation/hed_factor.py @@ -0,0 +1,27 @@ +import numpy as np +from albumentations import ImageOnlyTransform +from skimage.color import separate_stains, combine_stains, hed_from_rgb, rgb_from_hed + +class HEDFactor(ImageOnlyTransform): + def __init__(self, + h_range: tuple[float, float] = (0.8, 1.2), + e_range: tuple[float, float] = (0.8, 1.2), + always_apply: bool = True, + p: float = 1.0): + super().__init__(always_apply, p) + self.h_range = h_range + self.e_range = e_range + + def apply(self, img, **params): + h_factor = np.random.uniform(*self.h_range) + e_factor = np.random.uniform(*self.e_range) + + return self.multiply_channels(img, e_factor, h_factor) + + def multiply_channels(self, image, e_factor=1.1, h_factor=1.1,): + hed_image = separate_stains(image, hed_from_rgb) + h = hed_image[:, :, 0] * h_factor + e = hed_image[:, :, 1] * e_factor + d = hed_image[:, :, 2] + modified_rgb = combine_stains(np.stack((h, e, d), axis=-1), rgb_from_hed) + return modified_rgb diff --git a/stain_normalization/data/transformation/hue_shift.py b/stain_normalization/data/transformation/hue_shift.py new file mode 100644 index 0000000..b6708eb --- /dev/null +++ b/stain_normalization/data/transformation/hue_shift.py @@ -0,0 +1,34 @@ + + +import numpy as np +from albumentations import ImageOnlyTransform +from skimage.color import ( + rgb2hsv, hsv2rgb, + rgb2hed, hed2rgb +) + + +class HueShift(ImageOnlyTransform): + def __init__(self, hue_shift_range: tuple[float, float] = (0.0, 1.0), always_apply: bool = True, p: float = 1): + super().__init__(always_apply, p) + self.hue_shift_range = hue_shift_range + + def apply(self, img, **params): + hue_shift = np.random.uniform(*self.hue_shift_range) + + h, e, d = img[:, :, 0], img[:, :, 1], img[:, :, 2] + h_mod = np.clip(h * e, 0, 1) + e_mod = np.clip(e * 1, 0, 1) + d_mod = np.clip(d * 1, 0, 1) + + hed_mod = np.stack((h_mod, e_mod, d_mod), axis=-1) + + rgb_image = hed2rgb(hed_mod) + hsv_image = rgb2hsv(rgb_image) + + # Apply the random hue shift + hsv_image[:, :, 0] = (hsv_image[:, :, 0] + hue_shift) % 1.0 + + modified_rgb = hsv2rgb(hsv_image) + return rgb2hed(modified_rgb) + diff --git a/stain_normalization/main.py b/stain_normalization/main.py new file mode 100644 index 0000000..24659aa --- /dev/null +++ b/stain_normalization/main.py @@ -0,0 +1,35 @@ +from random import randint + +import hydra +from lightning import seed_everything +from lightning.pytorch.loggers import Logger +from omegaconf import DictConfig, OmegaConf +from rationai.mlkit import Trainer, autolog + +from stain_normalization.data import DataModule +from stain_normalization.stain_normalization_model import StainNormalizationModel + + +OmegaConf.register_new_resolver( + "random_seed", lambda: randint(0, 2**31), use_cache=True +) + + +@hydra.main(config_path="../configs", config_name="default", version_base=None) +@autolog +def main(config: DictConfig, logger: Logger | None) -> None: + seed_everything(config.seed, workers=True) + + data = hydra.utils.instantiate( + config.data, + _recursive_=False, # to avoid instantiating all the datasets + _target_=DataModule, + ) + model = hydra.utils.instantiate(config.model, _target_=StainNormalizationModel) + + trainer = hydra.utils.instantiate(config.trainer, _target_=Trainer, logger=logger) + getattr(trainer, config.mode)(model, datamodule=data, ckpt_path=config.checkpoint) + + +if __name__ == "__main__": + main() # pylint: disable=no-value-for-parameter diff --git a/stain_normalization/modeling/__init__.py b/stain_normalization/modeling/__init__.py new file mode 100644 index 0000000..6e52155 --- /dev/null +++ b/stain_normalization/modeling/__init__.py @@ -0,0 +1,3 @@ +from . import metrics +from .l1ssim_loss import L1SSIMLoss +__all__ = ["L1SSIMLoss", "metrics"] \ No newline at end of file diff --git a/stain_normalization/modeling/backbone/__init__.py b/stain_normalization/modeling/backbone/__init__.py new file mode 100644 index 0000000..68da66e --- /dev/null +++ b/stain_normalization/modeling/backbone/__init__.py @@ -0,0 +1,5 @@ +from stain_normalization.modeling.backbone.resnet50 import resnet50 +from stain_normalization.modeling.backbone.vgg16 import vgg16 + + +__all__ = ["resnet50", "vgg16"] \ No newline at end of file diff --git a/stain_normalization/modeling/backbone/resnet50.py b/stain_normalization/modeling/backbone/resnet50.py new file mode 100644 index 0000000..7705385 --- /dev/null +++ b/stain_normalization/modeling/backbone/resnet50.py @@ -0,0 +1,7 @@ +import torchvision +from torch import nn + + +def resnet50(weights: str | None = None) -> nn.Module: + resnet = torchvision.models.resnet50(weights=weights) + return nn.Sequential(*(list(resnet.children())[:-2])) diff --git a/stain_normalization/modeling/backbone/vgg16.py b/stain_normalization/modeling/backbone/vgg16.py new file mode 100644 index 0000000..b621ed7 --- /dev/null +++ b/stain_normalization/modeling/backbone/vgg16.py @@ -0,0 +1,6 @@ +from torch import nn +from torchvision + +def vgg16(weights: str | None = None) -> nn.Module: + return torchvision.models.vgg16(weights=weights).features + diff --git a/stain_normalization/modeling/decode_head/__init__.py b/stain_normalization/modeling/decode_head/__init__.py new file mode 100644 index 0000000..3f19fab --- /dev/null +++ b/stain_normalization/modeling/decode_head/__init__.py @@ -0,0 +1,4 @@ +from stain_normalization.modeling.decode_head.unet import UNetDecoder + + +__all__ = ["UNetDecoder"] \ No newline at end of file diff --git a/stain_normalization/modeling/decode_head/unet.py b/stain_normalization/modeling/decode_head/unet.py new file mode 100644 index 0000000..57052aa --- /dev/null +++ b/stain_normalization/modeling/decode_head/unet.py @@ -0,0 +1,35 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + +class UNetDecoder(nn.Module): + def __init__(self, in_channels=2048, out_channels=3): + super(UNetDecoder, self).__init__() + + # First upsampling block + self.upconv1 = nn.ConvTranspose2d(in_channels, 1024, kernel_size=2, stride=2) + self.conv1 = nn.Conv2d(1024, 512, kernel_size=3, padding=1) + + # Second upsampling block + self.upconv2 = nn.ConvTranspose2d(512, 256, kernel_size=2, stride=2) + self.conv2 = nn.Conv2d(256, 128, kernel_size=3, padding=1) + + # Third upsampling block + self.upconv3 = nn.ConvTranspose2d(128, 64, kernel_size=2, stride=2) + self.conv3 = nn.Conv2d(64, 64, kernel_size=3, padding=1) + + # Fourth upsampling block + self.upconv4 = nn.ConvTranspose2d(64, 32, kernel_size=2, stride=2) + self.conv4 = nn.Conv2d(32, 16, kernel_size=3, padding=1) + + # Final upsampling to match 512x512 resolution + self.upconv5 = nn.ConvTranspose2d(16, 8, kernel_size=2, stride=2) + self.conv5 = nn.Conv2d(8, out_channels, kernel_size=1) # 3-channel RGB output + + def forward(self, x): + x = F.relu(self.conv1(self.upconv1(x))) + x = F.relu(self.conv2(self.upconv2(x))) + x = F.relu(self.conv3(self.upconv3(x))) + x = F.relu(self.conv4(self.upconv4(x))) + x = self.conv5(self.upconv5(x)) # No activation; apply outside + return x diff --git a/stain_normalization/modeling/l1ssim_loss.py b/stain_normalization/modeling/l1ssim_loss.py new file mode 100644 index 0000000..16cf95d --- /dev/null +++ b/stain_normalization/modeling/l1ssim_loss.py @@ -0,0 +1,75 @@ +import torch.nn.functional as F +import torch +import torch.nn as nn +from torch.autograd import Variable +from math import exp + + +class L1SSIMLoss(nn.Module): + def __init__(self, lambda_dssim: float = 0.5): + super().__init__() + self.lambda_dssim = lambda_dssim + + def forward(self, image: torch.Tensor, target_image: torch.Tensor) -> torch.Tensor: + + Ll1 = F.l1_loss(image, target_image, reduction='mean') + + ssim_loss = 1.0 - ssim(image, target_image) + + total_loss = (1.0 - self.lambda_dssim) * Ll1 + \ + self.lambda_dssim * ssim_loss + return total_loss + + + +def gaussian(window_size, sigma): + gauss = torch.Tensor([exp(-(x - window_size // 2) ** 2 / + float(2 * sigma ** 2)) for x in range(window_size)]) + return gauss / gauss.sum() + + +def create_window(window_size, channel): + _1D_window = gaussian(window_size, 1.5).unsqueeze(1) + _2D_window = _1D_window.mm( + _1D_window.t()).float().unsqueeze(0).unsqueeze(0) + window = Variable(_2D_window.expand( + channel, 1, window_size, window_size).contiguous()) + return window + + +def ssim(img1, img2, window_size=11, size_average=True): + channel = img1.size(-3) + window = create_window(window_size, channel) + + if img1.is_cuda: + window = window.cuda(img1.get_device()) + window = window.type_as(img1) + + return _ssim(img1, img2, window, window_size, channel, size_average) + + +def _ssim(img1, img2, window, window_size, channel, size_average=True): + mu1 = F.conv2d(img1, window, padding=window_size // 2, groups=channel) + mu2 = F.conv2d(img2, window, padding=window_size // 2, groups=channel) + + mu1_sq = mu1.pow(2) + mu2_sq = mu2.pow(2) + mu1_mu2 = mu1 * mu2 + + sigma1_sq = F.conv2d(img1 * img1, window, + padding=window_size // 2, groups=channel) - mu1_sq + sigma2_sq = F.conv2d(img2 * img2, window, + padding=window_size // 2, groups=channel) - mu2_sq + sigma12 = F.conv2d(img1 * img2, window, + padding=window_size // 2, groups=channel) - mu1_mu2 + + C1 = 0.01 ** 2 + C2 = 0.03 ** 2 + + ssim_map = ((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) / \ + ((mu1_sq + mu2_sq + C1) * (sigma1_sq + sigma2_sq + C2)) + + if size_average: + return ssim_map.mean() + else: + return ssim_map.mean(1).mean(1).mean(1) diff --git a/stain_normalization/stain_normalization_model.py b/stain_normalization/stain_normalization_model.py new file mode 100644 index 0000000..19e626f --- /dev/null +++ b/stain_normalization/stain_normalization_model.py @@ -0,0 +1,68 @@ +from collections import defaultdict + +import torch +from lightning import LightningModule +from torch import Tensor, nn +from torch.optim.optimizer import Optimizer +from stain_normalization.modeling import L1SSIMLoss +from stain_normalization.typing import Input, Outputs +from torch.optim import Adam +from torch.optim.optimizer import Optimizer +from torchmetrics import PeakSignalNoiseRatio,StructuralSimilarityIndexMeasure, MetricCollection + +class StainNormalizationModel(LightningModule): + def __init__(self, backbone: nn.Module, decode_head: nn.Module) -> None: + super().__init__() + self.backbone = backbone + self.decode_head = decode_head + self.criterion = L1SSIMLoss() + + self.val_metrics = MetricCollection( + { + "ssim": StructuralSimilarityIndexMeasure(), + "psnr": PeakSignalNoiseRatio() + } + ) + self.test_metrics = self.val_metrics.clone(prefix="test/") + self.val_metrics.prefix = "validation/" + + def forward(self, x: Input) -> Outputs: + features = self.backbone(x) + return self.decode_head(features) + + def training_step(self, batch: Input) -> Tensor: + inputs, targets = batch + outputs = self(inputs) + + loss = self.criterion(outputs, targets) + self.log("train/loss", loss, on_step=True, prog_bar=True) + + return loss + + def validation_step(self, batch: Input) -> None: + inputs, targets = batch + outputs = self(inputs) + + loss = self.criterion(outputs, targets) + self.log("validation/loss", loss, on_step=False, on_epoch=True) + self.val_metrics.update(outputs, targets) + self.log_dict( + self.val_metrics, + batch_size=len(inputs), + on_epoch=True, + ) + + + def test_step(self, batch: Input) -> None: + inputs, targets = batch + outputs = self(inputs) + self.test_metrics.update(outputs, targets) + self.log_dict( + self.test_metrics, + batch_size=len(inputs), + on_epoch=True, + ) + + + def configure_optimizers(self) -> Optimizer: + return Adam(self.parameters(), lr=1e-4) diff --git a/stain_normalization/typing.py b/stain_normalization/typing.py new file mode 100644 index 0000000..0216ae3 --- /dev/null +++ b/stain_normalization/typing.py @@ -0,0 +1,20 @@ +from typing import TypeAlias, TypedDict + +from torch import Tensor + +# class Metadata(TypedDict): +# slide: str +# x: int +# y: int +# transormation: str +# PredictSample: TypeAlias = tuple[Tensor, Metadata] +# Sample: TypeAlias = tuple[Tensor, Tensor, Metadata] + +PredictSample: TypeAlias = tuple[Tensor] +Sample: TypeAlias = tuple[Tensor, Tensor] + +Input: TypeAlias = Sample + +Outputs: TypeAlias = Tensor + + diff --git a/tests/conftest.py b/tests/conftest.py deleted file mode 100644 index 6479e1f..0000000 --- a/tests/conftest.py +++ /dev/null @@ -1,51 +0,0 @@ -import numpy as np -import pytest -from numpy.typing import NDArray -from PIL import Image -from skimage.color import rgb2lab - -from rationai.staining.typing import RGBArray, StainArray - - -def stain2lab(stain: StainArray) -> NDArray[np.float64]: - """Converts a pixel in stain space to LAB space.""" - stain = np.maximum(stain, 1e-6) - rgb = np.exp(stain) - - return rgb2lab(rgb) - - -@pytest.fixture -def white() -> RGBArray: - """Returns an array with all-white RBG image of size (512, 512).""" - return np.full(shape=(512, 512, 3), dtype=np.uint8, fill_value=255) - - -@pytest.fixture -def black() -> RGBArray: - """Returns an array with all-black RBG image of size (512, 512).""" - return np.full(shape=(512, 512, 3), dtype=np.uint8, fill_value=0) - - -@pytest.fixture -def sample1_original() -> RGBArray: - """Returns original of the sample1 image.""" - return np.asarray(Image.open("tests/data/sample1/original.jpg")) - - -@pytest.fixture -def sample1_hematoxylin() -> NDArray[np.uint8]: - """Returns hematoxylin channel of the sample1 image (created by QuPath).""" - return np.asarray(Image.open("tests/data/sample1/hematoxylin.png")) - - -@pytest.fixture -def sample1_eosin() -> NDArray[np.uint8]: - """Returns eosin channel of the sample1 image (created by QuPath).""" - return np.asarray(Image.open("tests/data/sample1/eosin.png")) - - -@pytest.fixture -def sample1_residual() -> NDArray[np.uint8]: - """Returns residual channel of the sample1 image (created by QuPath).""" - return np.asarray(Image.open("tests/data/sample1/residual.png")) diff --git a/tests/data/generated/h_dab.png b/tests/data/generated/h_dab.png deleted file mode 100644 index 973134f479e972613496c065dbafc4bf5f59431f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14855 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 diff --git a/tests/data/generated/h_dab_legacy.png b/tests/data/generated/h_dab_legacy.png deleted file mode 100644 index 270e45f78e889780ffde33bf52159f12b7a5f2bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14366 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 diff --git a/tests/data/generated/h_e.png b/tests/data/generated/h_e.png deleted file mode 100644 index 951b6697b379c00ca08849f1a59bcad3eb42136b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14366 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 diff --git a/tests/data/sample1/eosin.png b/tests/data/sample1/eosin.png deleted file mode 100644 index dbbc43e1f89585ce9131b62cd408bfda68fb7c76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 183161 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

    u=Q|CJeHjO9#3ae8&I170Leij+2Yt_Z60Pl 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ðd36n2EjZ-UN$)p6e&HA65pPz`p!x#VGw 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%0D!$GQYU4*;Z`bAoerYjA`A>BV4s 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 IM6N<$g7Ww23IG5A diff --git a/tests/data/sample1/original.jpg b/tests/data/sample1/original.jpg deleted file mode 100644 index 41a0c97bcee644e2ad1ca46101bbf8d14e30cb1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83398 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 NDArray[np.float64]: - low, high = np.min(x), np.max(x) - - return (x - low) * (up - down) / (high - low) + down - - -def _noise_array( - size: int, params: list[tuple[int | None, float]] -) -> NDArray[np.float64]: - result = np.zeros(shape=(size, size), dtype=np.float64) - - for seed, octaves in params: - noise = PerlinNoise(octaves=octaves, seed=seed) - - arr = np.array( - [[noise([i / size, j / size]) for j in range(size)] for i in range(size)], - dtype=np.float64, - ) - arr = _stretch(arr, -1, 1) - result += arr - - result[result < 0] = 1 - result[(result >= 0) & (result < 0.5)] = 2 - result[(result >= 0.5) & (result < 0.85)] = 3 - result[(result >= 0.85) & (result < 1)] = 4 - - return result - - -def artificial_image(c0: RGBArray, c1: RGBArray, conv: ColorConversion) -> RGBArray: - noise = _noise_array(size=512, params=[(None, 10), (None, 4)]) - - c0_pure = convert_color(c0, conv)[0] - c1_pure = convert_color(c1, conv)[1] - - c0_noise = np.where((noise == 3) | (noise == 4), c0_pure, 0) - c1_noise = np.where((noise >= 2) & (noise < 4), c1_pure, 0) - - img = np.stack([c0_noise, c1_noise, ZEROS], axis=-1) - - return np.asarray(convert_color(img, conv.inverse), dtype=np.uint8) - - -def main() -> None: - h_e = artificial_image(H_RGB, E_RGB, ColorConversion.RGB2HER) - Image.fromarray(h_e).save("tests/data/generated/h_e.png") - - h_dab_legacy = artificial_image(H_RGB, DAB_RGB, ColorConversion.RGB2HDR_LEGACY) - Image.fromarray(h_dab_legacy).save("tests/data/generated/h_dab_legacy.png") - - h_dab = artificial_image(LIGHT_H_RGB, DAB_RGB, ColorConversion.RGB2HDR) - Image.fromarray(h_dab).save("tests/data/generated/h_dab.png") - - -if __name__ == "__main__": - main() diff --git a/tests/test_color_conversion.py b/tests/test_color_conversion.py deleted file mode 100644 index 302d421..0000000 --- a/tests/test_color_conversion.py +++ /dev/null @@ -1,32 +0,0 @@ -import numpy as np -import pytest - -from rationai.staining import ColorConversion -from rationai.staining.constants import LIGHT_H, QUPATH_DAB, QUPATH_E, QUPATH_H -from rationai.staining.typing import StainTuple -from rationai.staining.utils import residual - - -@pytest.mark.parametrize( - "conversion", - ColorConversion, - ids=(conversion.name for conversion in ColorConversion), -) -def test_inverse_conversion(conversion: ColorConversion) -> None: - original = conversion.matrix - inverse = conversion.inverse.matrix - - assert np.all(np.isclose(original, np.linalg.inv(inverse))) - - -STAINS = [ - # Expected values are taken from QuPath - (QUPATH_H, QUPATH_E, (0.316, -0.598, 0.737)), - (LIGHT_H, QUPATH_DAB, (0.418, -0.796, 0.437)), - (QUPATH_H, QUPATH_DAB, (0.633, -0.713, 0.302)), -] - - -@pytest.mark.parametrize("s0,s1,expected", STAINS, ids=["H&E", "H&DAB", "H&DAB_LEGACY"]) -def test_residual(s0: StainTuple, s1: StainTuple, expected: StainTuple) -> None: - assert np.all(np.isclose(residual(s0, s1), expected, atol=0.001)) diff --git a/tests/test_modify_and_reconstruct.py b/tests/test_modify_and_reconstruct.py deleted file mode 100644 index f9c2a86..0000000 --- a/tests/test_modify_and_reconstruct.py +++ /dev/null @@ -1,61 +0,0 @@ -import numpy as np -import pytest -from numpy.typing import NDArray -from skimage.data import immunohistochemistry -from skimage.metrics import structural_similarity - -from rationai.staining import modify_and_reconstruct -from rationai.staining.constants import LIGHT_H, QUPATH_DAB, QUPATH_E, QUPATH_H -from rationai.staining.typing import ModifyFunction, RGBArray, StainTuple - - -C = NDArray[np.float64] - - -def _no_change(c0: C, c1: C, c2: C) -> tuple[C, C, C]: - return c0, c1, c2 - - -IMAGES_AND_STAINS = [ - ( - immunohistochemistry(), - _no_change, - LIGHT_H, - QUPATH_DAB, - None, - ), - ( - "sample1_original", - _no_change, - QUPATH_H, - QUPATH_E, - None, - ), -] - -TEST_IDS = ["skimage-default; no change", "Sample1; no change"] - - -@pytest.mark.parametrize( - "original,modify,stain0,stain1,stain2", IMAGES_AND_STAINS, ids=TEST_IDS -) -def test_modify_and_reconstruct( - original: RGBArray | str, - modify: ModifyFunction, - stain0: StainTuple, - stain1: StainTuple, - stain2: StainTuple, - request: pytest.FixtureRequest, -) -> None: - if isinstance(original, str): - original = request.getfixturevalue(original) - - reconstructed = modify_and_reconstruct( - tile=original, # type: ignore[arg-type] - modify=modify, - stain0=stain0, - stain1=stain1, - stain2=stain2, - ) - - assert structural_similarity(original, reconstructed, channel_axis=-1) > 0.99 diff --git a/tests/test_stain_estimator.py b/tests/test_stain_estimator.py deleted file mode 100644 index 83e390e..0000000 --- a/tests/test_stain_estimator.py +++ /dev/null @@ -1,121 +0,0 @@ -from pathlib import Path -from typing import Any - -import numpy as np -import pytest -from conftest import stain2lab -from numpy.typing import NDArray -from PIL import Image -from skimage.color import deltaE_ciede2000 -from skimage.data import immunohistochemistry -from skimage.filters import gaussian - -from rationai.staining import ColorConversion, convert_color, estimate_stain_vectors -from rationai.staining.constants import LIGHT_H, QUPATH_DAB, QUPATH_E, QUPATH_H -from rationai.staining.typing import StainArray, Tile - - -ZEROS = np.zeros(shape=(512, 512), dtype=np.float64) - - -H_RGB = np.array([88, 76, 180], dtype=np.uint8) # from QuPath -E_RGB = np.array([199, 50, 112], dtype=np.uint8) # from QuPath -DAB_RGB = np.array([186, 110, 56], dtype=np.uint8) # from QuPath - - -NANS = np.full(shape=(3), fill_value=np.nan) - -GENERATED_DIR = Path("tests/data/generated") - - -def _blur(img: NDArray[np.uint8], sigma: float) -> NDArray[np.uint8]: - result = np.empty_like(img) - channels = [img[..., 0], img[..., 1], img[..., 2]] - - for i, c in enumerate(channels): - result[..., i] = (255 * gaussian(c, sigma=sigma)).astype(np.uint8) - - return result - - -# Input, first expected stain, second expected stain -DATA = [ - ( - immunohistochemistry(), - QUPATH_H, - QUPATH_DAB, - ), - ( - np.asarray(Image.open(GENERATED_DIR / "h_e.png")), - QUPATH_H, - QUPATH_E, - ), - ( - np.asarray(Image.open(GENERATED_DIR / "h_dab.png")), - LIGHT_H, - QUPATH_DAB, - ), - ( - np.asarray(Image.open(GENERATED_DIR / "h_dab_legacy.png")), - QUPATH_H, - QUPATH_DAB, - ), - ( - _blur(np.asarray(Image.open(GENERATED_DIR / "h_e.png")), sigma=5), - QUPATH_H, - QUPATH_E, - ), - ( - _blur(np.asarray(Image.open(GENERATED_DIR / "h_dab.png")), sigma=5), - LIGHT_H, - QUPATH_DAB, - ), - ( - _blur(np.asarray(Image.open(GENERATED_DIR / "h_dab_legacy.png")), sigma=5), - QUPATH_H, - QUPATH_DAB, - ), - ( - convert_color(np.stack([ZEROS] * 3, axis=-1), ColorConversion.HER2RGB), - NANS, - NANS, - ), -] - -IDS = [ - "Default skimage image H&DAB_LEGACY", - "Artificial H&E", - "Artificial H&DAB", - "Artificial H&DAB_LEGACY", - "Blurred Artificial H&E", - "Blurred Artificial H&DAB", - "Blurred Artificial H&DAB_LEGACY", - "Empty Image", -] - - -@pytest.mark.parametrize("img,expected1,expected2", DATA, ids=IDS) -class TestStainEstimator: - @staticmethod - def _both_nan(x: NDArray[Any], y: NDArray[Any]) -> bool: - return bool(np.isnan(x).all()) and bool(np.isnan(y).all()) - - def test_estimation_vector_similarity( - self, img: Tile, expected1: StainArray, expected2: StainArray - ) -> None: - stain1, stain2 = estimate_stain_vectors(img) - - assert np.dot(stain1, expected1) > 0.97 or self._both_nan(stain1, expected1) - assert np.dot(stain2, expected2) > 0.97 or self._both_nan(stain2, expected2) - - def test_estimation_color_difference( - self, img: Tile, expected1: StainArray, expected2: StainArray - ) -> None: - stain1, stain2 = estimate_stain_vectors(img) - delta = deltaE_ciede2000 - - stain1, stain2 = stain2lab(stain1), stain2lab(stain2) - expected1, expected2 = stain2lab(expected1), stain2lab(expected2) - - assert delta(stain1, expected1) < 15 or self._both_nan(stain1, expected1) - assert delta(stain2, expected2) < 15 or self._both_nan(stain2, expected2) diff --git a/tests/test_stain_reconstruction.py b/tests/test_stain_reconstruction.py deleted file mode 100644 index 323a764..0000000 --- a/tests/test_stain_reconstruction.py +++ /dev/null @@ -1,44 +0,0 @@ -import numpy as np -import pytest -from skimage.data import immunohistochemistry -from skimage.metrics import structural_similarity - -from rationai.staining import ColorConversion, convert_color -from rationai.staining.typing import RGBArray - - -IMAGES_AND_CONVS = [ - ( - immunohistochemistry(), - ColorConversion.RGB2HDR, - ), - ( - immunohistochemistry(), - ColorConversion.RGB2HDR_LEGACY, - ), - ( - "sample1_original", - ColorConversion.RGB2HER, - ), -] - -TEST_IDS = ["skimage-default; H&DAB", "skimage-default; H&DAB_LEGACY", "Sample1; H&E"] - - -@pytest.mark.parametrize("original,conv", IMAGES_AND_CONVS, ids=TEST_IDS) -def test_structural_similarity( - original: RGBArray | str, conv: ColorConversion, request: pytest.FixtureRequest -) -> None: - """For reconstruction, only structural similarity is tested. - - Due to implementation details, the reconsturction is not idempotent, - and some bigger color shifts can occur in some conversions. - Therefore, the color similarity cannot be reliably tested. - """ - if isinstance(original, str): - original = request.getfixturevalue(original) - - c0, c1, c2 = convert_color(original, conv) # type: ignore[arg-type] - reconstructed = convert_color(np.stack([c0, c1, c2], axis=-1), conv.inverse) - - assert structural_similarity(original, reconstructed, channel_axis=-1) > 0.95 diff --git a/tests/test_stain_separation.py b/tests/test_stain_separation.py deleted file mode 100644 index ecca7d8..0000000 --- a/tests/test_stain_separation.py +++ /dev/null @@ -1,53 +0,0 @@ -import numpy as np -import pytest -from numpy.typing import NDArray -from skimage.metrics import structural_similarity - -from rationai.staining import ColorConversion, convert_color -from rationai.staining.typing import RGBArray, StainArray - - -RGB_CONVERSIONS = [ - ColorConversion.RGB2HER, - ColorConversion.RGB2HDR, - ColorConversion.RGB2HDR_LEGACY, -] -RGB_NAMES = [c.name for c in RGB_CONVERSIONS] - - -@pytest.mark.parametrize("conversion", RGB_CONVERSIONS, ids=RGB_NAMES) -def test_black_image(black: RGBArray, conversion: ColorConversion) -> None: - c0, c1, c2 = convert_color(black, conversion) - - rgb2stain = np.stack(conversion.value[0]) - expected_values = np.sum(rgb2stain, axis=0) - - assert np.all(c0 == expected_values[0]) - assert np.all(c1 == expected_values[1]) - assert np.all(c2 == expected_values[2]) - - -@pytest.mark.parametrize("conversion", RGB_CONVERSIONS, ids=RGB_NAMES) -def test_white_image(white: RGBArray, conversion: ColorConversion) -> None: - c0, c1, c2 = convert_color(white, conversion) - - assert np.all(c0 == 0) - assert np.all(c1 == 0) - assert np.all(c2 == 0) - - -def test_real_sample1_image( - sample1_original: RGBArray, - sample1_hematoxylin: StainArray, - sample1_eosin: StainArray, - sample1_residual: StainArray, -) -> None: - h, e, r = convert_color(sample1_original, ColorConversion.RGB2HER) - - assert structural_similarity(_prepare(h), sample1_hematoxylin) > 0.95 - assert structural_similarity(_prepare(e), sample1_eosin) > 0.95 - assert structural_similarity(_prepare(r), sample1_residual) > 0.95 - - -def _prepare(c: NDArray[np.float64]) -> NDArray[np.uint8]: - return (255 * (c / np.max(c))).astype(np.uint8) From 844d765ff95ae82a4297f833ffe1d2cde28e8554 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 7 Mar 2025 00:56:57 +0000 Subject: [PATCH 27/85] fix: transformations inputs --- configs/data/modify/test.yaml | 14 ++++---- configs/data/modify/train.yaml | 14 ++++---- configs/data/transforms/train.yaml | 11 +----- configs/default.yaml | 4 +-- .../data/transformation/__init__.py | 4 +-- .../transformation/combiend_modification.py | 16 +++++---- .../transformation/exposure_adjustment.py | 10 ++++-- .../data/transformation/hed_factor.py | 2 +- .../data/transformation/hue_shift.py | 34 ------------------ .../data/transformation/hvs_modification.py | 35 +++++++++++++++++++ 10 files changed, 74 insertions(+), 70 deletions(-) delete mode 100644 stain_normalization/data/transformation/hue_shift.py create mode 100644 stain_normalization/data/transformation/hvs_modification.py diff --git a/configs/data/modify/test.yaml b/configs/data/modify/test.yaml index c8d3ae7..d69792c 100644 --- a/configs/data/modify/test.yaml +++ b/configs/data/modify/test.yaml @@ -1,12 +1,14 @@ _target_: albumentations.OneOf transforms: - - _target_: stain_normalization.data.transformation.HED + - _target_: stain_normalization.data.transformation.HEDFactor h_range: [0.8, 1.2] e_range: [0.8, 1.2] - _target_: stain_normalization.data.transformation.ExposureAdjustment brightness_range: [0.8, 1.2] - - _target_: UnnaturalColorModification - hue_shift_range: [0.2, 1.0] - - _target_: CombinedModifications - brightness_range: [-0.4, 0.4] - intensity_range: [0.65, 1.35] \ No newline at end of file + - _target_: stain_normalization.data.transformation.HVSModification + hue_shift_range: [-0.4, 0.4] + saturation_range: [0.8, 1.5] + value_range: [0.8, 1.3] + - _target_: stain_normalization.data.transformation.CombinedModifications + intensity_range: [0.65, 1.35] + brightness_range: [-0.4, 0.4] \ No newline at end of file diff --git a/configs/data/modify/train.yaml b/configs/data/modify/train.yaml index c8d3ae7..d69792c 100644 --- a/configs/data/modify/train.yaml +++ b/configs/data/modify/train.yaml @@ -1,12 +1,14 @@ _target_: albumentations.OneOf transforms: - - _target_: stain_normalization.data.transformation.HED + - _target_: stain_normalization.data.transformation.HEDFactor h_range: [0.8, 1.2] e_range: [0.8, 1.2] - _target_: stain_normalization.data.transformation.ExposureAdjustment brightness_range: [0.8, 1.2] - - _target_: UnnaturalColorModification - hue_shift_range: [0.2, 1.0] - - _target_: CombinedModifications - brightness_range: [-0.4, 0.4] - intensity_range: [0.65, 1.35] \ No newline at end of file + - _target_: stain_normalization.data.transformation.HVSModification + hue_shift_range: [-0.4, 0.4] + saturation_range: [0.8, 1.5] + value_range: [0.8, 1.3] + - _target_: stain_normalization.data.transformation.CombinedModifications + intensity_range: [0.65, 1.35] + brightness_range: [-0.4, 0.4] \ No newline at end of file diff --git a/configs/data/transforms/train.yaml b/configs/data/transforms/train.yaml index 81162f1..4cec39f 100644 --- a/configs/data/transforms/train.yaml +++ b/configs/data/transforms/train.yaml @@ -4,13 +4,4 @@ transforms: p: 1.0 - _target_: albumentations.Flip p: 0.75 - - _target_: albumentations.RandomGamma - p: 0.5 - - _target_: albumentations.RandomBrightnessContrast - p: 0.5 - - _target_: albumentations.HueSaturationValue - p: 0.5 - - _target_: albumentations.Normalize - mean: [198.9923, 156.7051, 185.0198] - std: [36.8293, 46.7353, 35.8960] - max_pixel_value: 1 + diff --git a/configs/default.yaml b/configs/default.yaml index 79092e5..42230e2 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -29,8 +29,8 @@ data: metadata: user: xlopatka - experiment_name: Stain Normalization - run_name: Test Stain Normalization + experiment_name: Stain-Normalization + run_name: Testing #First Train Stain Normalization description: Testing Stain Normalization hyperparams: {} diff --git a/stain_normalization/data/transformation/__init__.py b/stain_normalization/data/transformation/__init__.py index 5e28fc7..b817f7b 100644 --- a/stain_normalization/data/transformation/__init__.py +++ b/stain_normalization/data/transformation/__init__.py @@ -1,7 +1,7 @@ from stain_normalization.data.transformation.hed_factor import HEDFactor from stain_normalization.data.transformation.exposure_adjustment import ExposureAdjustment -from stain_normalization.data.transformation.hue_shift import HueShift +from stain_normalization.data.transformation.hvs_modification import HVSModification from stain_normalization.data.transformation.combiend_modification import CombinedModifications -__all__ = ["HEDFactor", "ExposureAdjustment", "HueShift", "CombinedModifications"] +__all__ = ["HEDFactor", "ExposureAdjustment", "HVSModification", "CombinedModifications"] diff --git a/stain_normalization/data/transformation/combiend_modification.py b/stain_normalization/data/transformation/combiend_modification.py index d893f3f..610ebcb 100644 --- a/stain_normalization/data/transformation/combiend_modification.py +++ b/stain_normalization/data/transformation/combiend_modification.py @@ -1,6 +1,7 @@ import numpy as np from albumentations import ImageOnlyTransform from skimage import exposure +from skimage.color import separate_stains, combine_stains, hed_from_rgb, rgb_from_hed class CombinedModifications(ImageOnlyTransform): @@ -9,15 +10,18 @@ def __init__(self, intensity_range=(0.4, 1.5), brightness_range=(-0.4, 0.4), alw self.intensity_range = intensity_range self.brightness_range = brightness_range - def apply(self, img, **params): + def apply(self, hed_img, **params): def modify_channel(channel, intensity_range, brightness_range): intensity_scale = np.random.uniform(*intensity_range) channel = channel * intensity_scale brightness_shift = np.random.uniform(*brightness_range) channel = exposure.adjust_gamma(channel, gamma=1 + brightness_shift) return np.clip(channel, 0, 1) - - h = modify_channel(img[:, :, 0], self.intensity_range, self.brightness_range) - e = modify_channel(img[:, :, 1], self.intensity_range, self.brightness_range) - d = img[:, :, 2] # Skip modification for D channel - return np.stack((h, e, d), axis=-1) \ No newline at end of file + + hed_image = separate_stains(hed_img, hed_from_rgb) + h = modify_channel(hed_image[:, :, 0], self.intensity_range, self.brightness_range) + e = modify_channel(hed_image[:, :, 1], self.intensity_range, self.brightness_range) + d = hed_image[:, :, 2] # Skip modification for D channel + + modified_rgb = combine_stains(np.stack((h, e, d), axis=-1), rgb_from_hed) + return modified_rgb \ No newline at end of file diff --git a/stain_normalization/data/transformation/exposure_adjustment.py b/stain_normalization/data/transformation/exposure_adjustment.py index 71af622..4cc9a41 100644 --- a/stain_normalization/data/transformation/exposure_adjustment.py +++ b/stain_normalization/data/transformation/exposure_adjustment.py @@ -1,5 +1,6 @@ import numpy as np from albumentations import ImageOnlyTransform +from skimage.color import separate_stains, combine_stains, hed_from_rgb, rgb_from_hed class ExposureAdjustment(ImageOnlyTransform): @@ -8,10 +9,13 @@ def __init__(self, brightness_range: tuple[float, float] = (0.8, 1.2), always_ap self.brightness_range = brightness_range def apply(self, img, **params): + hed_img = separate_stains(img, hed_from_rgb) + brightness_factor = np.random.uniform(*self.brightness_range) - h, e, d = img[:, :, 0], img[:, :, 1], img[:, :, 2] + h, e, d = hed_img[:, :, 0], hed_img[:, :, 1], hed_img[:, :, 2] h = np.clip(h * brightness_factor, 0, 1) e = np.clip(e * brightness_factor, 0, 1) d = np.clip(d * brightness_factor, 0, 1) - return np.stack((h, e, d), axis=-1) - + + modified_rgb = combine_stains(np.stack((h, e, d), axis=-1), rgb_from_hed) + return modified_rgb diff --git a/stain_normalization/data/transformation/hed_factor.py b/stain_normalization/data/transformation/hed_factor.py index 13d668b..80af22d 100644 --- a/stain_normalization/data/transformation/hed_factor.py +++ b/stain_normalization/data/transformation/hed_factor.py @@ -12,7 +12,7 @@ def __init__(self, self.h_range = h_range self.e_range = e_range - def apply(self, img, **params): + def apply(self, img, **params): h_factor = np.random.uniform(*self.h_range) e_factor = np.random.uniform(*self.e_range) diff --git a/stain_normalization/data/transformation/hue_shift.py b/stain_normalization/data/transformation/hue_shift.py deleted file mode 100644 index b6708eb..0000000 --- a/stain_normalization/data/transformation/hue_shift.py +++ /dev/null @@ -1,34 +0,0 @@ - - -import numpy as np -from albumentations import ImageOnlyTransform -from skimage.color import ( - rgb2hsv, hsv2rgb, - rgb2hed, hed2rgb -) - - -class HueShift(ImageOnlyTransform): - def __init__(self, hue_shift_range: tuple[float, float] = (0.0, 1.0), always_apply: bool = True, p: float = 1): - super().__init__(always_apply, p) - self.hue_shift_range = hue_shift_range - - def apply(self, img, **params): - hue_shift = np.random.uniform(*self.hue_shift_range) - - h, e, d = img[:, :, 0], img[:, :, 1], img[:, :, 2] - h_mod = np.clip(h * e, 0, 1) - e_mod = np.clip(e * 1, 0, 1) - d_mod = np.clip(d * 1, 0, 1) - - hed_mod = np.stack((h_mod, e_mod, d_mod), axis=-1) - - rgb_image = hed2rgb(hed_mod) - hsv_image = rgb2hsv(rgb_image) - - # Apply the random hue shift - hsv_image[:, :, 0] = (hsv_image[:, :, 0] + hue_shift) % 1.0 - - modified_rgb = hsv2rgb(hsv_image) - return rgb2hed(modified_rgb) - diff --git a/stain_normalization/data/transformation/hvs_modification.py b/stain_normalization/data/transformation/hvs_modification.py new file mode 100644 index 0000000..21e36d5 --- /dev/null +++ b/stain_normalization/data/transformation/hvs_modification.py @@ -0,0 +1,35 @@ + + +import numpy as np +from albumentations import ImageOnlyTransform +from skimage.color import rgb2hsv, hsv2rgb + + +class HVSModification(ImageOnlyTransform): + def __init__( + self, + hue_shift_range: tuple[float, float] = (-0.2, 0.2), + saturation_range: tuple[float, float] = (0.8, 1.5), + value_range: tuple[float, float] = (0.8, 1.3), + always_apply: bool = True, + p: float = 1.0 + ): + + super().__init__(always_apply, p) + self.hue_shift_range = hue_shift_range + self.saturation_range = saturation_range + self.value_range = value_range + + def apply(self, img, **params): + hue_shift = np.random.uniform(*self.hue_shift_range) + saturation_scale = np.random.uniform(*self.saturation_range) + value_scale = np.random.uniform(*self.value_range) + + hsv_image = rgb2hsv(img) + hsv_image[:, :, 0] = (hsv_image[:, :, 0] + hue_shift) % 1.0 + hsv_image[:, :, 1] = np.clip(hsv_image[:, :, 1] * saturation_scale, 0, 1) + hsv_image[:, :, 2] = np.clip(hsv_image[:, :, 2] * value_scale, 0, 1) + + modified_rgb = hsv2rgb(hsv_image) + + return modified_rgb \ No newline at end of file From fc427f83425a9ed346d4fcecc759ba2dc55970e8 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 7 Mar 2025 00:57:48 +0000 Subject: [PATCH 28/85] chore: added data link --- configs/data/datasets/stain_normalization/test.yaml | 2 +- configs/data/datasets/stain_normalization/train.yaml | 4 ++-- configs/data/datasets/stain_normalization/val.yaml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configs/data/datasets/stain_normalization/test.yaml b/configs/data/datasets/stain_normalization/test.yaml index 311cda0..78dabba 100644 --- a/configs/data/datasets/stain_normalization/test.yaml +++ b/configs/data/datasets/stain_normalization/test.yaml @@ -3,4 +3,4 @@ defaults: - /data/modify@modify: test _target_: stain_normalization.data.datasets.StainNormalization -uris: ["mlflow-artifacts:/79/bc7dfc2258164cd292091c3056609277/artifacts/Stain Normalization - test/tiles.parquet"] +uris: ["mlflow-artifacts:/79/bc7dfc2258164cd292091c3056609277/artifacts/Stain Normalization - test"] diff --git a/configs/data/datasets/stain_normalization/train.yaml b/configs/data/datasets/stain_normalization/train.yaml index 3d99dad..5a7feec 100644 --- a/configs/data/datasets/stain_normalization/train.yaml +++ b/configs/data/datasets/stain_normalization/train.yaml @@ -1,6 +1,6 @@ defaults: - - /data/transforms@transforms: train + # - /data/transforms@transforms: train - /data/modify@modify: train _target_: stain_normalization.data.datasets.StainNormalization -uris: ["mlflow-artifacts:"] +uris: ["mlflow-artifacts:/79/bc7dfc2258164cd292091c3056609277/artifacts/Stain Normalization - train"] diff --git a/configs/data/datasets/stain_normalization/val.yaml b/configs/data/datasets/stain_normalization/val.yaml index 53e608c..142cfb8 100644 --- a/configs/data/datasets/stain_normalization/val.yaml +++ b/configs/data/datasets/stain_normalization/val.yaml @@ -3,5 +3,5 @@ defaults: - /data/modify@modify: test _target_: stain_normalization.data.datasets.StainNormalization -uris: ["mlflow-artifacts:"] +uris: ["mlflow-artifacts:/79/bc7dfc2258164cd292091c3056609277/artifacts/Stain Normalization - val"] From f039733acbc0058923a4cadb66113780362847c3 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 7 Mar 2025 01:05:35 +0000 Subject: [PATCH 29/85] fix: resolve issues before job run --- configs/data/transforms/train copy.yaml | 16 + configs/model/decode_head/unet2048.yaml | 1 - configs/model/decode_head/unet512.yaml | 1 - job.py | 18 + pdm.lock | 2599 ++++++++++++----- pyproject.toml | 35 +- stain_normalization/{main.py => __main__.py} | 0 .../data/datasets/stain_normalization.py | 22 +- stain_normalization/modeling/__init__.py | 1 - .../modeling/backbone/vgg16.py | 2 +- .../stain_normalization_model.py | 8 +- 11 files changed, 1863 insertions(+), 840 deletions(-) create mode 100644 configs/data/transforms/train copy.yaml create mode 100644 job.py rename stain_normalization/{main.py => __main__.py} (100%) diff --git a/configs/data/transforms/train copy.yaml b/configs/data/transforms/train copy.yaml new file mode 100644 index 0000000..81162f1 --- /dev/null +++ b/configs/data/transforms/train copy.yaml @@ -0,0 +1,16 @@ +_target_: albumentations.Compose +transforms: + - _target_: albumentations.RandomRotate90 + p: 1.0 + - _target_: albumentations.Flip + p: 0.75 + - _target_: albumentations.RandomGamma + p: 0.5 + - _target_: albumentations.RandomBrightnessContrast + p: 0.5 + - _target_: albumentations.HueSaturationValue + p: 0.5 + - _target_: albumentations.Normalize + mean: [198.9923, 156.7051, 185.0198] + std: [36.8293, 46.7353, 35.8960] + max_pixel_value: 1 diff --git a/configs/model/decode_head/unet2048.yaml b/configs/model/decode_head/unet2048.yaml index 1936549..00a4921 100644 --- a/configs/model/decode_head/unet2048.yaml +++ b/configs/model/decode_head/unet2048.yaml @@ -1,5 +1,4 @@ decode_head: _target_: stain_normalization.modeling.decode_head.UNetDecoder in_channels: 2048 - weights: null diff --git a/configs/model/decode_head/unet512.yaml b/configs/model/decode_head/unet512.yaml index d3461f1..531bf9f 100644 --- a/configs/model/decode_head/unet512.yaml +++ b/configs/model/decode_head/unet512.yaml @@ -1,5 +1,4 @@ decode_head: _target_: stain_normalization.modeling.decode_head.UNetDecoder in_channels: 512 - weights: null diff --git a/job.py b/job.py new file mode 100644 index 0000000..20b4bf4 --- /dev/null +++ b/job.py @@ -0,0 +1,18 @@ +from kube_jobs import Storage, submit_job + + +submit_job( + job_name="some name", + username="your name", + cpu=10, + memory="128Gi", + gpu="A40", + script=[ + "git clone https://gitlab.ics.muni.cz/rationai/digital-pathology/... workdir", + "cd workdir", + "git checkout develop", + "pdm sync --skip=post_install", + "pdm fit model/backbone=resnet18", + ], + storage=Storage(mou=True), +) diff --git a/pdm.lock b/pdm.lock index 395416d..57e651c 100644 --- a/pdm.lock +++ b/pdm.lock @@ -3,78 +3,104 @@ [metadata] groups = ["default", "dev"] -strategy = [] -lock_version = "4.4" -content_hash = "sha256:24e67ec88809826b854439f3edd657a4285070b448d3ae0add27a80782d3b633" +strategy = ["inherit_metadata"] +lock_version = "4.5.0" +content_hash = "sha256:b6548b53106f41b522b88929821234d14bbde40c39c47dafe1e38851ca0ec4e9" + +[[metadata.targets]] +requires_python = ">=3.11" [[package]] name = "aiohappyeyeballs" -version = "2.3.6" -requires_python = ">=3.8" +version = "2.5.0" +requires_python = ">=3.9" summary = "Happy Eyeballs for asyncio" +groups = ["default"] files = [ - {file = "aiohappyeyeballs-2.3.6-py3-none-any.whl", hash = "sha256:15dca2611fa78442f1cb54cf07ffb998573f2b4fbeab45ca8554c045665c896b"}, - {file = "aiohappyeyeballs-2.3.6.tar.gz", hash = "sha256:88211068d2a40e0436033956d7de3926ff36d54776f8b1022d6b21320cadae79"}, + {file = "aiohappyeyeballs-2.5.0-py3-none-any.whl", hash = "sha256:0850b580748c7071db98bffff6d4c94028d0d3035acc20fd721a0ce7e8cac35d"}, + {file = "aiohappyeyeballs-2.5.0.tar.gz", hash = "sha256:18fde6204a76deeabc97c48bdd01d5801cfda5d6b9c8bbeb1aaaee9d648ca191"}, ] [[package]] name = "aiohttp" -version = "3.10.3" -requires_python = ">=3.8" +version = "3.11.13" +requires_python = ">=3.9" summary = "Async http client/server framework (asyncio)" +groups = ["default"] dependencies = [ "aiohappyeyeballs>=2.3.0", "aiosignal>=1.1.2", + "async-timeout<6.0,>=4.0; python_version < \"3.11\"", "attrs>=17.3.0", "frozenlist>=1.1.1", "multidict<7.0,>=4.5", - "yarl<2.0,>=1.0", -] -files = [ - {file = "aiohttp-3.10.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e021c4c778644e8cdc09487d65564265e6b149896a17d7c0f52e9a088cc44e1b"}, - {file = "aiohttp-3.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:24fade6dae446b183e2410a8628b80df9b7a42205c6bfc2eff783cbeedc224a2"}, - {file = "aiohttp-3.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bc8e9f15939dacb0e1f2d15f9c41b786051c10472c7a926f5771e99b49a5957f"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5a9ec959b5381271c8ec9310aae1713b2aec29efa32e232e5ef7dcca0df0279"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a5d0ea8a6467b15d53b00c4e8ea8811e47c3cc1bdbc62b1aceb3076403d551f"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9ed607dbbdd0d4d39b597e5bf6b0d40d844dfb0ac6a123ed79042ef08c1f87e"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e66d5b506832e56add66af88c288c1d5ba0c38b535a1a59e436b300b57b23e"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fda91ad797e4914cca0afa8b6cccd5d2b3569ccc88731be202f6adce39503189"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:61ccb867b2f2f53df6598eb2a93329b5eee0b00646ee79ea67d68844747a418e"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6d881353264e6156f215b3cb778c9ac3184f5465c2ece5e6fce82e68946868ef"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b031ce229114825f49cec4434fa844ccb5225e266c3e146cb4bdd025a6da52f1"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5337cc742a03f9e3213b097abff8781f79de7190bbfaa987bd2b7ceb5bb0bdec"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab3361159fd3dcd0e48bbe804006d5cfb074b382666e6c064112056eb234f1a9"}, - {file = "aiohttp-3.10.3-cp311-cp311-win32.whl", hash = "sha256:05d66203a530209cbe40f102ebaac0b2214aba2a33c075d0bf825987c36f1f0b"}, - {file = "aiohttp-3.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:70b4a4984a70a2322b70e088d654528129783ac1ebbf7dd76627b3bd22db2f17"}, - {file = "aiohttp-3.10.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:166de65e2e4e63357cfa8417cf952a519ac42f1654cb2d43ed76899e2319b1ee"}, - {file = "aiohttp-3.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7084876352ba3833d5d214e02b32d794e3fd9cf21fdba99cff5acabeb90d9806"}, - {file = "aiohttp-3.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d98c604c93403288591d7d6d7d6cc8a63459168f8846aeffd5b3a7f3b3e5e09"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d73b073a25a0bb8bf014345374fe2d0f63681ab5da4c22f9d2025ca3e3ea54fc"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8da6b48c20ce78f5721068f383e0e113dde034e868f1b2f5ee7cb1e95f91db57"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a9dcdccf50284b1b0dc72bc57e5bbd3cc9bf019060dfa0668f63241ccc16aa7"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56fb94bae2be58f68d000d046172d8b8e6b1b571eb02ceee5535e9633dcd559c"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf75716377aad2c718cdf66451c5cf02042085d84522aec1f9246d3e4b8641a6"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6c51ed03e19c885c8e91f574e4bbe7381793f56f93229731597e4a499ffef2a5"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b84857b66fa6510a163bb083c1199d1ee091a40163cfcbbd0642495fed096204"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c124b9206b1befe0491f48185fd30a0dd51b0f4e0e7e43ac1236066215aff272"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3461d9294941937f07bbbaa6227ba799bc71cc3b22c40222568dc1cca5118f68"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:08bd0754d257b2db27d6bab208c74601df6f21bfe4cb2ec7b258ba691aac64b3"}, - {file = "aiohttp-3.10.3-cp312-cp312-win32.whl", hash = "sha256:7f9159ae530297f61a00116771e57516f89a3de6ba33f314402e41560872b50a"}, - {file = "aiohttp-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:e1128c5d3a466279cb23c4aa32a0f6cb0e7d2961e74e9e421f90e74f75ec1edf"}, - {file = "aiohttp-3.10.3.tar.gz", hash = "sha256:21650e7032cc2d31fc23d353d7123e771354f2a3d5b05a5647fc30fea214e696"}, + "propcache>=0.2.0", + "yarl<2.0,>=1.17.0", +] +files = [ + {file = "aiohttp-3.11.13-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b35aab22419ba45f8fc290d0010898de7a6ad131e468ffa3922b1b0b24e9d2e"}, + {file = "aiohttp-3.11.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81cba651db8795f688c589dd11a4fbb834f2e59bbf9bb50908be36e416dc760"}, + {file = "aiohttp-3.11.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f55d0f242c2d1fcdf802c8fabcff25a9d85550a4cf3a9cf5f2a6b5742c992839"}, + {file = "aiohttp-3.11.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4bea08a6aad9195ac9b1be6b0c7e8a702a9cec57ce6b713698b4a5afa9c2e33"}, + {file = "aiohttp-3.11.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6070bcf2173a7146bb9e4735b3c62b2accba459a6eae44deea0eb23e0035a23"}, + {file = "aiohttp-3.11.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:718d5deb678bc4b9d575bfe83a59270861417da071ab44542d0fcb6faa686636"}, + {file = "aiohttp-3.11.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f6b2c5b4a4d22b8fb2c92ac98e0747f5f195e8e9448bfb7404cd77e7bfa243f"}, + {file = "aiohttp-3.11.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:747ec46290107a490d21fe1ff4183bef8022b848cf9516970cb31de6d9460088"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:01816f07c9cc9d80f858615b1365f8319d6a5fd079cd668cc58e15aafbc76a54"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:a08ad95fcbd595803e0c4280671d808eb170a64ca3f2980dd38e7a72ed8d1fea"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c97be90d70f7db3aa041d720bfb95f4869d6063fcdf2bb8333764d97e319b7d0"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ab915a57c65f7a29353c8014ac4be685c8e4a19e792a79fe133a8e101111438e"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:35cda4e07f5e058a723436c4d2b7ba2124ab4e0aa49e6325aed5896507a8a42e"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:af55314407714fe77a68a9ccaab90fdb5deb57342585fd4a3a8102b6d4370080"}, + {file = "aiohttp-3.11.13-cp311-cp311-win32.whl", hash = "sha256:42d689a5c0a0c357018993e471893e939f555e302313d5c61dfc566c2cad6185"}, + {file = "aiohttp-3.11.13-cp311-cp311-win_amd64.whl", hash = "sha256:b73a2b139782a07658fbf170fe4bcdf70fc597fae5ffe75e5b67674c27434a9f"}, + {file = "aiohttp-3.11.13-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2eabb269dc3852537d57589b36d7f7362e57d1ece308842ef44d9830d2dc3c90"}, + {file = "aiohttp-3.11.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b77ee42addbb1c36d35aca55e8cc6d0958f8419e458bb70888d8c69a4ca833d"}, + {file = "aiohttp-3.11.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55789e93c5ed71832e7fac868167276beadf9877b85697020c46e9a75471f55f"}, + {file = "aiohttp-3.11.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c929f9a7249a11e4aa5c157091cfad7f49cc6b13f4eecf9b747104befd9f56f2"}, + {file = "aiohttp-3.11.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d33851d85537bbf0f6291ddc97926a754c8f041af759e0aa0230fe939168852b"}, + {file = "aiohttp-3.11.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9229d8613bd8401182868fe95688f7581673e1c18ff78855671a4b8284f47bcb"}, + {file = "aiohttp-3.11.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669dd33f028e54fe4c96576f406ebb242ba534dd3a981ce009961bf49960f117"}, + {file = "aiohttp-3.11.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c1b20a1ace54af7db1f95af85da530fe97407d9063b7aaf9ce6a32f44730778"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5724cc77f4e648362ebbb49bdecb9e2b86d9b172c68a295263fa072e679ee69d"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:aa36c35e94ecdb478246dd60db12aba57cfcd0abcad43c927a8876f25734d496"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9b5b37c863ad5b0892cc7a4ceb1e435e5e6acd3f2f8d3e11fa56f08d3c67b820"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e06cf4852ce8c4442a59bae5a3ea01162b8fcb49ab438d8548b8dc79375dad8a"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5194143927e494616e335d074e77a5dac7cd353a04755330c9adc984ac5a628e"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:afcb6b275c2d2ba5d8418bf30a9654fa978b4f819c2e8db6311b3525c86fe637"}, + {file = "aiohttp-3.11.13-cp312-cp312-win32.whl", hash = "sha256:7104d5b3943c6351d1ad7027d90bdd0ea002903e9f610735ac99df3b81f102ee"}, + {file = "aiohttp-3.11.13-cp312-cp312-win_amd64.whl", hash = "sha256:47dc018b1b220c48089b5b9382fbab94db35bef2fa192995be22cbad3c5730c8"}, + {file = "aiohttp-3.11.13-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9862d077b9ffa015dbe3ce6c081bdf35135948cb89116e26667dd183550833d1"}, + {file = "aiohttp-3.11.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fbfef0666ae9e07abfa2c54c212ac18a1f63e13e0760a769f70b5717742f3ece"}, + {file = "aiohttp-3.11.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:93a1f7d857c4fcf7cabb1178058182c789b30d85de379e04f64c15b7e88d66fb"}, + {file = "aiohttp-3.11.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba40b7ae0f81c7029583a338853f6607b6d83a341a3dcde8bed1ea58a3af1df9"}, + {file = "aiohttp-3.11.13-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5b95787335c483cd5f29577f42bbe027a412c5431f2f80a749c80d040f7ca9f"}, + {file = "aiohttp-3.11.13-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7d474c5c1f0b9405c1565fafdc4429fa7d986ccbec7ce55bc6a330f36409cad"}, + {file = "aiohttp-3.11.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e83fb1991e9d8982b3b36aea1e7ad27ea0ce18c14d054c7a404d68b0319eebb"}, + {file = "aiohttp-3.11.13-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4586a68730bd2f2b04a83e83f79d271d8ed13763f64b75920f18a3a677b9a7f0"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fe4eb0e7f50cdb99b26250d9328faef30b1175a5dbcfd6d0578d18456bac567"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2a8a6bc19818ac3e5596310ace5aa50d918e1ebdcc204dc96e2f4d505d51740c"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7f27eec42f6c3c1df09cfc1f6786308f8b525b8efaaf6d6bd76c1f52c6511f6a"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:2a4a13dfbb23977a51853b419141cd0a9b9573ab8d3a1455c6e63561387b52ff"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:02876bf2f69b062584965507b07bc06903c2dc93c57a554b64e012d636952654"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b992778d95b60a21c4d8d4a5f15aaab2bd3c3e16466a72d7f9bfd86e8cea0d4b"}, + {file = "aiohttp-3.11.13-cp313-cp313-win32.whl", hash = "sha256:507ab05d90586dacb4f26a001c3abf912eb719d05635cbfad930bdbeb469b36c"}, + {file = "aiohttp-3.11.13-cp313-cp313-win_amd64.whl", hash = "sha256:5ceb81a4db2decdfa087381b5fc5847aa448244f973e5da232610304e199e7b2"}, + {file = "aiohttp-3.11.13.tar.gz", hash = "sha256:8ce789231404ca8fff7f693cdce398abf6d90fd5dae2b1847477196c243b1fbb"}, ] [[package]] name = "aiosignal" -version = "1.3.1" -requires_python = ">=3.7" +version = "1.3.2" +requires_python = ">=3.9" summary = "aiosignal: a list of registered asynchronous callbacks" +groups = ["default"] dependencies = [ "frozenlist>=1.1.0", ] files = [ - {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, - {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, + {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, + {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, ] [[package]] @@ -82,11 +108,13 @@ name = "albucore" version = "0.0.23" requires_python = ">=3.9" summary = "High-performance image processing functions for deep learning and computer vision." +groups = ["default"] dependencies = [ "numpy>=1.24.4", "opencv-python-headless>=4.9.0.80", "simsimd>=5.9.2", "stringzilla>=3.10.4", + "typing-extensions>=4.9.0; python_version < \"3.10\"", ] files = [ {file = "albucore-0.0.23-py3-none-any.whl", hash = "sha256:99274ac0c15a1a7d9a726df9d54d5ab70d9d0c189e2a935399dba3d4bafad415"}, @@ -95,44 +123,39 @@ files = [ [[package]] name = "albumentations" -version = "2.0.4" +version = "2.0.5" requires_python = ">=3.9" -summary = "Fast, flexible, and advanced augmentation library for deep learning, computer vision, and medical imaging. Albumentations offers a wide range of transformations for both 2D (images, masks, bboxes, keypoints) and 3D (volumes, volumetric masks) data, with optimized performance and seamless integration into ML workflows." +summary = "Fast, flexible, and advanced augmentation library for deep learning, computer vision, and medical imaging. Albumentations offers a wide range of transformations for both 2D (images, masks, bboxes, keypoints) and 3D (volumes, volumetric masks, keypoints) data, with optimized performance and seamless integration into ML workflows." +groups = ["default"] dependencies = [ "PyYAML", "albucore==0.0.23", + "eval-type-backport; python_version < \"3.10\"", "numpy>=1.24.4", "opencv-python-headless>=4.9.0.80", "pydantic>=2.9.2", "scipy>=1.10.0", + "typing-extensions>=4.9.0; python_version < \"3.10\"", ] files = [ - {file = "albumentations-2.0.4-py3-none-any.whl", hash = "sha256:1d60b62527d34bb8791fad90f6faeeb065ecc5cb4577ff85614f022d56183010"}, - {file = "albumentations-2.0.4.tar.gz", hash = "sha256:4f0cd22576fba8030fc99f1ccacee1452a1880ac2a9ce0882f0d93e5477d5c70"}, + {file = "albumentations-2.0.5-py3-none-any.whl", hash = "sha256:1fc253942d34dd7c07652bf6511049c8bb7d522baec7f1fe355df16293c3c7b6"}, + {file = "albumentations-2.0.5.tar.gz", hash = "sha256:e19e1c0f14c903c3c230f3d83f14814b84f1180393189bf96779f653031f3278"}, ] [[package]] name = "alembic" -version = "1.13.2" -requires_python = ">=3.8" +version = "1.15.1" +requires_python = ">=3.9" summary = "A database migration tool for SQLAlchemy." +groups = ["default"] dependencies = [ "Mako", - "SQLAlchemy>=1.3.0", - "typing-extensions>=4", -] -files = [ - {file = "alembic-1.13.2-py3-none-any.whl", hash = "sha256:6b8733129a6224a9a711e17c99b08462dbf7cc9670ba8f2e2ae9af860ceb1953"}, - {file = "alembic-1.13.2.tar.gz", hash = "sha256:1ff0ae32975f4fd96028c39ed9bb3c867fe3af956bd7bb37343b54c9fe7445ef"}, + "SQLAlchemy>=1.4.0", + "typing-extensions>=4.12", ] - -[[package]] -name = "aniso8601" -version = "9.0.1" -summary = "A library for parsing ISO 8601 strings." files = [ - {file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"}, - {file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"}, + {file = "alembic-1.15.1-py3-none-any.whl", hash = "sha256:197de710da4b3e91cf66a826a5b31b5d59a127ab41bd0fc42863e2902ce2bbbe"}, + {file = "alembic-1.15.1.tar.gz", hash = "sha256:e1a1c738577bca1f27e68728c910cd389b9a92152ff91d902da649c192e30c49"}, ] [[package]] @@ -140,6 +163,10 @@ name = "annotated-types" version = "0.7.0" requires_python = ">=3.8" summary = "Reusable constraint types to use with typing.Annotated" +groups = ["default"] +dependencies = [ + "typing-extensions>=4.0.0; python_version < \"3.9\"", +] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -149,48 +176,56 @@ files = [ name = "antlr4-python3-runtime" version = "4.9.3" summary = "ANTLR 4.9.3 runtime for Python 3.7" +groups = ["default"] +dependencies = [ + "typing; python_version < \"3.5\"", +] files = [ {file = "antlr4-python3-runtime-4.9.3.tar.gz", hash = "sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b"}, ] [[package]] name = "attrs" -version = "24.2.0" -requires_python = ">=3.7" +version = "25.1.0" +requires_python = ">=3.8" summary = "Classes Without Boilerplate" +groups = ["default"] files = [ - {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, - {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, + {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"}, + {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"}, ] [[package]] name = "blinker" -version = "1.8.2" -requires_python = ">=3.8" +version = "1.9.0" +requires_python = ">=3.9" summary = "Fast, simple object-to-object and broadcast signaling" +groups = ["default"] files = [ - {file = "blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01"}, - {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"}, + {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"}, + {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, ] [[package]] name = "cachetools" -version = "5.5.0" +version = "5.5.2" requires_python = ">=3.7" summary = "Extensible memoizing collections and decorators" +groups = ["default"] files = [ - {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, - {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, + {file = "cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a"}, + {file = "cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4"}, ] [[package]] name = "certifi" -version = "2024.8.30" +version = "2025.1.31" requires_python = ">=3.6" summary = "Python package for providing Mozilla's CA Bundle." +groups = ["default"] files = [ - {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, - {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, + {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, + {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, ] [[package]] @@ -198,11 +233,45 @@ name = "cffi" version = "1.17.1" requires_python = ">=3.8" summary = "Foreign Function Interface for Python calling C code." +groups = ["default"] dependencies = [ "pycparser", ] files = [ + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, ] @@ -211,6 +280,7 @@ name = "cfgv" version = "3.4.0" requires_python = ">=3.8" summary = "Validate configuration and produce human readable error messages." +groups = ["dev"] files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -218,112 +288,143 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.3.2" -requires_python = ">=3.7.0" +version = "3.4.1" +requires_python = ">=3.7" summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +groups = ["default"] +files = [ + {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, + {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, + {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, ] [[package]] name = "click" -version = "8.1.7" +version = "8.1.8" requires_python = ">=3.7" summary = "Composable command line interface toolkit" +groups = ["default"] +dependencies = [ + "colorama; platform_system == \"Windows\"", + "importlib-metadata; python_version < \"3.8\"", +] files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, + {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, + {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, ] [[package]] name = "cloudpickle" -version = "3.0.0" +version = "3.1.1" requires_python = ">=3.8" summary = "Pickler class to extend the standard pickle.Pickler functionality" +groups = ["default"] +files = [ + {file = "cloudpickle-3.1.1-py3-none-any.whl", hash = "sha256:c8c5a44295039331ee9dad40ba100a9c7297b6f988e50e87ccdf3765a668350e"}, + {file = "cloudpickle-3.1.1.tar.gz", hash = "sha256:b216fa8ae4019d5482a8ac3c95d8f6346115d8835911fd4aefd1a445e4242c64"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +summary = "Cross-platform colored terminal text." +groups = ["default"] +marker = "platform_system == \"Windows\"" files = [ - {file = "cloudpickle-3.0.0-py3-none-any.whl", hash = "sha256:246ee7d0c295602a036e86369c77fecda4ab17b506496730f2f576d9016fd9c7"}, - {file = "cloudpickle-3.0.0.tar.gz", hash = "sha256:996d9a482c6fb4f33c1a35335cf8afd065d2a56e973270364840712d9131a882"}, + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "contourpy" -version = "1.3.0" -requires_python = ">=3.9" +version = "1.3.1" +requires_python = ">=3.10" summary = "Python library for calculating contours of 2D quadrilateral grids" +groups = ["default"] dependencies = [ "numpy>=1.23", ] files = [ - {file = "contourpy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fa4c02abe6c446ba70d96ece336e621efa4aecae43eaa9b030ae5fb92b309ad"}, - {file = "contourpy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:834e0cfe17ba12f79963861e0f908556b2cedd52e1f75e6578801febcc6a9f49"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbc4c3217eee163fa3984fd1567632b48d6dfd29216da3ded3d7b844a8014a66"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4865cd1d419e0c7a7bf6de1777b185eebdc51470800a9f42b9e9decf17762081"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:303c252947ab4b14c08afeb52375b26781ccd6a5ccd81abcdfc1fafd14cf93c1"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d"}, - {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76a896b2f195b57db25d6b44e7e03f221d32fe318d03ede41f8b4d9ba1bff53c"}, - {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e1fd23e9d01591bab45546c089ae89d926917a66dceb3abcf01f6105d927e2cb"}, - {file = "contourpy-1.3.0-cp311-cp311-win32.whl", hash = "sha256:d402880b84df3bec6eab53cd0cf802cae6a2ef9537e70cf75e91618a3801c20c"}, - {file = "contourpy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:6cb6cc968059db9c62cb35fbf70248f40994dfcd7aa10444bbf8b3faeb7c2d67"}, - {file = "contourpy-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:570ef7cf892f0afbe5b2ee410c507ce12e15a5fa91017a0009f79f7d93a1268f"}, - {file = "contourpy-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:da84c537cb8b97d153e9fb208c221c45605f73147bd4cadd23bdae915042aad6"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0be4d8425bfa755e0fd76ee1e019636ccc7c29f77a7c86b4328a9eb6a26d0639"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c0da700bf58f6e0b65312d0a5e695179a71d0163957fa381bb3c1f72972537c"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb8b141bb00fa977d9122636b16aa67d37fd40a3d8b52dd837e536d64b9a4d06"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3634b5385c6716c258d0419c46d05c8aa7dc8cb70326c9a4fb66b69ad2b52e09"}, - {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0dce35502151b6bd35027ac39ba6e5a44be13a68f55735c3612c568cac3805fd"}, - {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea348f053c645100612b333adc5983d87be69acdc6d77d3169c090d3b01dc35"}, - {file = "contourpy-1.3.0-cp312-cp312-win32.whl", hash = "sha256:90f73a5116ad1ba7174341ef3ea5c3150ddf20b024b98fb0c3b29034752c8aeb"}, - {file = "contourpy-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:b11b39aea6be6764f84360fce6c82211a9db32a7c7de8fa6dd5397cf1d079c3b"}, - {file = "contourpy-1.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3e1c7fa44aaae40a2247e2e8e0627f4bea3dd257014764aa644f319a5f8600e3"}, - {file = "contourpy-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:364174c2a76057feef647c802652f00953b575723062560498dc7930fc9b1cb7"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32b238b3b3b649e09ce9aaf51f0c261d38644bdfa35cbaf7b263457850957a84"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d51fca85f9f7ad0b65b4b9fe800406d0d77017d7270d31ec3fb1cc07358fdea0"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:732896af21716b29ab3e988d4ce14bc5133733b85956316fb0c56355f398099b"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d73f659398a0904e125280836ae6f88ba9b178b2fed6884f3b1f95b989d2c8da"}, - {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c6c7c2408b7048082932cf4e641fa3b8ca848259212f51c8c59c45aa7ac18f14"}, - {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f317576606de89da6b7e0861cf6061f6146ead3528acabff9236458a6ba467f8"}, - {file = "contourpy-1.3.0-cp313-cp313-win32.whl", hash = "sha256:31cd3a85dbdf1fc002280c65caa7e2b5f65e4a973fcdf70dd2fdcb9868069294"}, - {file = "contourpy-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:4553c421929ec95fb07b3aaca0fae668b2eb5a5203d1217ca7c34c063c53d087"}, - {file = "contourpy-1.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:345af746d7766821d05d72cb8f3845dfd08dd137101a2cb9b24de277d716def8"}, - {file = "contourpy-1.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3bb3808858a9dc68f6f03d319acd5f1b8a337e6cdda197f02f4b8ff67ad2057b"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:420d39daa61aab1221567b42eecb01112908b2cab7f1b4106a52caaec8d36973"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d63ee447261e963af02642ffcb864e5a2ee4cbfd78080657a9880b8b1868e18"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:167d6c890815e1dac9536dca00828b445d5d0df4d6a8c6adb4a7ec3166812fa8"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:710a26b3dc80c0e4febf04555de66f5fd17e9cf7170a7b08000601a10570bda6"}, - {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:75ee7cb1a14c617f34a51d11fa7524173e56551646828353c4af859c56b766e2"}, - {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927"}, - {file = "contourpy-1.3.0.tar.gz", hash = "sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4"}, + {file = "contourpy-1.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e8b974d8db2c5610fb4e76307e265de0edb655ae8169e8b21f41807ccbeec4b"}, + {file = "contourpy-1.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20914c8c973f41456337652a6eeca26d2148aa96dd7ac323b74516988bea89fc"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d40d37c1c3a4961b4619dd9d77b12124a453cc3d02bb31a07d58ef684d3d86"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:113231fe3825ebf6f15eaa8bc1f5b0ddc19d42b733345eae0934cb291beb88b6"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dbbc03a40f916a8420e420d63e96a1258d3d1b58cbdfd8d1f07b49fcbd38e85"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a04ecd68acbd77fa2d39723ceca4c3197cb2969633836ced1bea14e219d077c"}, + {file = "contourpy-1.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c414fc1ed8ee1dbd5da626cf3710c6013d3d27456651d156711fa24f24bd1291"}, + {file = "contourpy-1.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:31c1b55c1f34f80557d3830d3dd93ba722ce7e33a0b472cba0ec3b6535684d8f"}, + {file = "contourpy-1.3.1-cp311-cp311-win32.whl", hash = "sha256:f611e628ef06670df83fce17805c344710ca5cde01edfdc72751311da8585375"}, + {file = "contourpy-1.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b2bdca22a27e35f16794cf585832e542123296b4687f9fd96822db6bae17bfc9"}, + {file = "contourpy-1.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ffa84be8e0bd33410b17189f7164c3589c229ce5db85798076a3fa136d0e509"}, + {file = "contourpy-1.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805617228ba7e2cbbfb6c503858e626ab528ac2a32a04a2fe88ffaf6b02c32bc"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade08d343436a94e633db932e7e8407fe7de8083967962b46bdfc1b0ced39454"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47734d7073fb4590b4a40122b35917cd77be5722d80683b249dac1de266aac80"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ba94a401342fc0f8b948e57d977557fbf4d515f03c67682dd5c6191cb2d16ec"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efa874e87e4a647fd2e4f514d5e91c7d493697127beb95e77d2f7561f6905bd9"}, + {file = "contourpy-1.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf98051f1045b15c87868dbaea84f92408337d4f81d0e449ee41920ea121d3b"}, + {file = "contourpy-1.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:61332c87493b00091423e747ea78200659dc09bdf7fd69edd5e98cef5d3e9a8d"}, + {file = "contourpy-1.3.1-cp312-cp312-win32.whl", hash = "sha256:e914a8cb05ce5c809dd0fe350cfbb4e881bde5e2a38dc04e3afe1b3e58bd158e"}, + {file = "contourpy-1.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:08d9d449a61cf53033612cb368f3a1b26cd7835d9b8cd326647efe43bca7568d"}, + {file = "contourpy-1.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2"}, + {file = "contourpy-1.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c"}, + {file = "contourpy-1.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3"}, + {file = "contourpy-1.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1"}, + {file = "contourpy-1.3.1-cp313-cp313-win32.whl", hash = "sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82"}, + {file = "contourpy-1.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd"}, + {file = "contourpy-1.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30"}, + {file = "contourpy-1.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda"}, + {file = "contourpy-1.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242"}, + {file = "contourpy-1.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1"}, + {file = "contourpy-1.3.1-cp313-cp313t-win32.whl", hash = "sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1"}, + {file = "contourpy-1.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546"}, + {file = "contourpy-1.3.1.tar.gz", hash = "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699"}, ] [[package]] @@ -331,6 +432,7 @@ name = "cycler" version = "0.12.1" requires_python = ">=3.8" summary = "Composable style cycles" +groups = ["default"] files = [ {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, @@ -338,38 +440,41 @@ files = [ [[package]] name = "databricks-sdk" -version = "0.31.1" +version = "0.44.1" requires_python = ">=3.7" summary = "Databricks SDK for Python (Beta)" +groups = ["default"] dependencies = [ "google-auth~=2.0", "requests<3,>=2.28.1", ] files = [ - {file = "databricks_sdk-0.31.1-py3-none-any.whl", hash = "sha256:9ab286f87ae1cc98a00ef7d207e40661f4d14a464071425ad169d235919b35f6"}, - {file = "databricks_sdk-0.31.1.tar.gz", hash = "sha256:8609e655d0e5ecb15c2a8a6468e737f8dcb4f28c33239388de3ab386b921d790"}, + {file = "databricks-sdk-0.44.1.tar.gz", hash = "sha256:78b6734f79ddd33d05f00bbf3a0a9114e881c0a29d99dab369f19e3db0ac532c"}, + {file = "databricks_sdk-0.44.1-py3-none-any.whl", hash = "sha256:2808fc73ec9934551eb8587bf82da3a42aa0f11ebc2e97468e8ced8e5b27e34d"}, ] [[package]] name = "deprecated" -version = "1.2.14" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.2.18" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" summary = "Python @deprecated decorator to deprecate old python classes, functions or methods." +groups = ["default"] dependencies = [ "wrapt<2,>=1.10", ] files = [ - {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, - {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, + {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"}, + {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"}, ] [[package]] name = "distlib" -version = "0.3.8" +version = "0.3.9" summary = "Distribution utilities" +groups = ["dev"] files = [ - {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, - {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, + {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, + {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, ] [[package]] @@ -377,7 +482,9 @@ name = "docker" version = "7.1.0" requires_python = ">=3.8" summary = "A Python library for the Docker Engine API." +groups = ["default"] dependencies = [ + "pywin32>=304; sys_platform == \"win32\"", "requests>=2.26.0", "urllib3>=1.26.0", ] @@ -390,6 +497,7 @@ files = [ name = "durationpy" version = "0.9" summary = "Module for converting between datetime.timedelta and Go's Duration strings." +groups = ["default"] files = [ {file = "durationpy-0.9-py3-none-any.whl", hash = "sha256:e65359a7af5cedad07fb77a2dd3f390f8eb0b74cb845589fa6c057086834dd38"}, {file = "durationpy-0.9.tar.gz", hash = "sha256:fd3feb0a69a0057d582ef643c355c40d2fa1c942191f914d12203b1a01ac722a"}, @@ -397,185 +505,225 @@ files = [ [[package]] name = "filelock" -version = "3.15.4" -requires_python = ">=3.8" +version = "3.17.0" +requires_python = ">=3.9" summary = "A platform independent file lock." +groups = ["default", "dev"] files = [ - {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, - {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, + {file = "filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338"}, + {file = "filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e"}, ] [[package]] name = "flask" -version = "3.0.3" -requires_python = ">=3.8" +version = "3.1.0" +requires_python = ">=3.9" summary = "A simple framework for building complex web applications." +groups = ["default"] dependencies = [ "Jinja2>=3.1.2", - "Werkzeug>=3.0.0", - "blinker>=1.6.2", + "Werkzeug>=3.1", + "blinker>=1.9", "click>=8.1.3", - "itsdangerous>=2.1.2", + "importlib-metadata>=3.6; python_version < \"3.10\"", + "itsdangerous>=2.2", ] files = [ - {file = "flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3"}, - {file = "flask-3.0.3.tar.gz", hash = "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842"}, + {file = "flask-3.1.0-py3-none-any.whl", hash = "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136"}, + {file = "flask-3.1.0.tar.gz", hash = "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac"}, ] [[package]] name = "fonttools" -version = "4.53.1" +version = "4.56.0" requires_python = ">=3.8" summary = "Tools to manipulate font files" -files = [ - {file = "fonttools-4.53.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da33440b1413bad53a8674393c5d29ce64d8c1a15ef8a77c642ffd900d07bfe1"}, - {file = "fonttools-4.53.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ff7e5e9bad94e3a70c5cd2fa27f20b9bb9385e10cddab567b85ce5d306ea923"}, - {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6e7170d675d12eac12ad1a981d90f118c06cf680b42a2d74c6c931e54b50719"}, - {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee32ea8765e859670c4447b0817514ca79054463b6b79784b08a8df3a4d78e3"}, - {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6e08f572625a1ee682115223eabebc4c6a2035a6917eac6f60350aba297ccadb"}, - {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b21952c092ffd827504de7e66b62aba26fdb5f9d1e435c52477e6486e9d128b2"}, - {file = "fonttools-4.53.1-cp311-cp311-win32.whl", hash = "sha256:9dfdae43b7996af46ff9da520998a32b105c7f098aeea06b2226b30e74fbba88"}, - {file = "fonttools-4.53.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4d0096cb1ac7a77b3b41cd78c9b6bc4a400550e21dc7a92f2b5ab53ed74eb02"}, - {file = "fonttools-4.53.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d92d3c2a1b39631a6131c2fa25b5406855f97969b068e7e08413325bc0afba58"}, - {file = "fonttools-4.53.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b3c8ebafbee8d9002bd8f1195d09ed2bd9ff134ddec37ee8f6a6375e6a4f0e8"}, - {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32f029c095ad66c425b0ee85553d0dc326d45d7059dbc227330fc29b43e8ba60"}, - {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f5e6c3510b79ea27bb1ebfcc67048cde9ec67afa87c7dd7efa5c700491ac7f"}, - {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f677ce218976496a587ab17140da141557beb91d2a5c1a14212c994093f2eae2"}, - {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9e6ceba2a01b448e36754983d376064730690401da1dd104ddb543519470a15f"}, - {file = "fonttools-4.53.1-cp312-cp312-win32.whl", hash = "sha256:791b31ebbc05197d7aa096bbc7bd76d591f05905d2fd908bf103af4488e60670"}, - {file = "fonttools-4.53.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ed170b5e17da0264b9f6fae86073be3db15fa1bd74061c8331022bca6d09bab"}, - {file = "fonttools-4.53.1-py3-none-any.whl", hash = "sha256:f1f8758a2ad110bd6432203a344269f445a2907dc24ef6bccfd0ac4e14e0d71d"}, - {file = "fonttools-4.53.1.tar.gz", hash = "sha256:e128778a8e9bc11159ce5447f76766cefbd876f44bd79aff030287254e4752c4"}, +groups = ["default"] +files = [ + {file = "fonttools-4.56.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ef04bc7827adb7532be3d14462390dd71287644516af3f1e67f1e6ff9c6d6df"}, + {file = "fonttools-4.56.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ffda9b8cd9cb8b301cae2602ec62375b59e2e2108a117746f12215145e3f786c"}, + {file = "fonttools-4.56.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e993e8db36306cc3f1734edc8ea67906c55f98683d6fd34c3fc5593fdbba4c"}, + {file = "fonttools-4.56.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:003548eadd674175510773f73fb2060bb46adb77c94854af3e0cc5bc70260049"}, + {file = "fonttools-4.56.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd9825822e7bb243f285013e653f6741954d8147427aaa0324a862cdbf4cbf62"}, + {file = "fonttools-4.56.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b23d30a2c0b992fb1c4f8ac9bfde44b5586d23457759b6cf9a787f1a35179ee0"}, + {file = "fonttools-4.56.0-cp311-cp311-win32.whl", hash = "sha256:47b5e4680002ae1756d3ae3b6114e20aaee6cc5c69d1e5911f5ffffd3ee46c6b"}, + {file = "fonttools-4.56.0-cp311-cp311-win_amd64.whl", hash = "sha256:14a3e3e6b211660db54ca1ef7006401e4a694e53ffd4553ab9bc87ead01d0f05"}, + {file = "fonttools-4.56.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6f195c14c01bd057bc9b4f70756b510e009c83c5ea67b25ced3e2c38e6ee6e9"}, + {file = "fonttools-4.56.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fa760e5fe8b50cbc2d71884a1eff2ed2b95a005f02dda2fa431560db0ddd927f"}, + {file = "fonttools-4.56.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d54a45d30251f1d729e69e5b675f9a08b7da413391a1227781e2a297fa37f6d2"}, + {file = "fonttools-4.56.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:661a8995d11e6e4914a44ca7d52d1286e2d9b154f685a4d1f69add8418961563"}, + {file = "fonttools-4.56.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d94449ad0a5f2a8bf5d2f8d71d65088aee48adbe45f3c5f8e00e3ad861ed81a"}, + {file = "fonttools-4.56.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f59746f7953f69cc3290ce2f971ab01056e55ddd0fb8b792c31a8acd7fee2d28"}, + {file = "fonttools-4.56.0-cp312-cp312-win32.whl", hash = "sha256:bce60f9a977c9d3d51de475af3f3581d9b36952e1f8fc19a1f2254f1dda7ce9c"}, + {file = "fonttools-4.56.0-cp312-cp312-win_amd64.whl", hash = "sha256:300c310bb725b2bdb4f5fc7e148e190bd69f01925c7ab437b9c0ca3e1c7cd9ba"}, + {file = "fonttools-4.56.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f20e2c0dfab82983a90f3d00703ac0960412036153e5023eed2b4641d7d5e692"}, + {file = "fonttools-4.56.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f36a0868f47b7566237640c026c65a86d09a3d9ca5df1cd039e30a1da73098a0"}, + {file = "fonttools-4.56.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62b4c6802fa28e14dba010e75190e0e6228513573f1eeae57b11aa1a39b7e5b1"}, + {file = "fonttools-4.56.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a05d1f07eb0a7d755fbe01fee1fd255c3a4d3730130cf1bfefb682d18fd2fcea"}, + {file = "fonttools-4.56.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0073b62c3438cf0058488c002ea90489e8801d3a7af5ce5f7c05c105bee815c3"}, + {file = "fonttools-4.56.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cad98c94833465bcf28f51c248aaf07ca022efc6a3eba750ad9c1e0256d278"}, + {file = "fonttools-4.56.0-cp313-cp313-win32.whl", hash = "sha256:d0cb73ccf7f6d7ca8d0bc7ea8ac0a5b84969a41c56ac3ac3422a24df2680546f"}, + {file = "fonttools-4.56.0-cp313-cp313-win_amd64.whl", hash = "sha256:62cc1253827d1e500fde9dbe981219fea4eb000fd63402283472d38e7d8aa1c6"}, + {file = "fonttools-4.56.0-py3-none-any.whl", hash = "sha256:1088182f68c303b50ca4dc0c82d42083d176cba37af1937e1a976a31149d4d14"}, + {file = "fonttools-4.56.0.tar.gz", hash = "sha256:a114d1567e1a1586b7e9e7fc2ff686ca542a82769a296cef131e4c4af51e58f4"}, ] [[package]] name = "frozenlist" -version = "1.4.1" +version = "1.5.0" requires_python = ">=3.8" summary = "A list-like structure which implements collections.abc.MutableSequence" -files = [ - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, - {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, - {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, - {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, - {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, - {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, - {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, +groups = ["default"] +files = [ + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf"}, + {file = "frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942"}, + {file = "frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f"}, + {file = "frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8"}, + {file = "frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03"}, + {file = "frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c"}, + {file = "frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28"}, + {file = "frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3"}, + {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, ] [[package]] name = "fsspec" -version = "2024.6.1" +version = "2025.2.0" requires_python = ">=3.8" summary = "File-system specification" +groups = ["default"] files = [ - {file = "fsspec-2024.6.1-py3-none-any.whl", hash = "sha256:3cb443f8bcd2efb31295a5b9fdb02aee81d8452c80d28f97a6d0959e6cee101e"}, - {file = "fsspec-2024.6.1.tar.gz", hash = "sha256:fad7d7e209dd4c1208e3bbfda706620e0da5142bebbd9c384afb95b07e798e49"}, + {file = "fsspec-2025.2.0-py3-none-any.whl", hash = "sha256:9de2ad9ce1f85e1931858535bc882543171d197001a0a5eb2ddc04f1781ab95b"}, + {file = "fsspec-2025.2.0.tar.gz", hash = "sha256:1c24b16eaa0a1798afa0337aa0db9b256718ab2a89c425371f5628d22c3b6afd"}, ] [[package]] name = "fsspec" -version = "2024.6.1" +version = "2025.2.0" extras = ["http"] requires_python = ">=3.8" summary = "File-system specification" +groups = ["default"] dependencies = [ "aiohttp!=4.0.0a0,!=4.0.0a1", - "fsspec==2024.6.1", + "fsspec==2025.2.0", ] files = [ - {file = "fsspec-2024.6.1-py3-none-any.whl", hash = "sha256:3cb443f8bcd2efb31295a5b9fdb02aee81d8452c80d28f97a6d0959e6cee101e"}, - {file = "fsspec-2024.6.1.tar.gz", hash = "sha256:fad7d7e209dd4c1208e3bbfda706620e0da5142bebbd9c384afb95b07e798e49"}, + {file = "fsspec-2025.2.0-py3-none-any.whl", hash = "sha256:9de2ad9ce1f85e1931858535bc882543171d197001a0a5eb2ddc04f1781ab95b"}, + {file = "fsspec-2025.2.0.tar.gz", hash = "sha256:1c24b16eaa0a1798afa0337aa0db9b256718ab2a89c425371f5628d22c3b6afd"}, ] [[package]] name = "gitdb" -version = "4.0.11" +version = "4.0.12" requires_python = ">=3.7" summary = "Git Object Database" +groups = ["default"] dependencies = [ "smmap<6,>=3.0.1", ] files = [ - {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, - {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, + {file = "gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf"}, + {file = "gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571"}, ] [[package]] name = "gitpython" -version = "3.1.43" +version = "3.1.44" requires_python = ">=3.7" summary = "GitPython is a Python library used to interact with Git repositories" +groups = ["default"] dependencies = [ "gitdb<5,>=4.0.1", + "typing-extensions>=3.7.4.3; python_version < \"3.8\"", ] files = [ - {file = "GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff"}, - {file = "GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c"}, + {file = "GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110"}, + {file = "gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269"}, ] [[package]] name = "google-auth" -version = "2.34.0" +version = "2.38.0" requires_python = ">=3.7" summary = "Google Authentication Library" +groups = ["default"] dependencies = [ "cachetools<6.0,>=2.0.0", "pyasn1-modules>=0.2.1", "rsa<5,>=3.1.4", ] files = [ - {file = "google_auth-2.34.0-py2.py3-none-any.whl", hash = "sha256:72fd4733b80b6d777dcde515628a9eb4a577339437012874ea286bca7261ee65"}, - {file = "google_auth-2.34.0.tar.gz", hash = "sha256:8eb87396435c19b20d32abd2f984e31c191a15284af72eb922f10e5bde9c04cc"}, + {file = "google_auth-2.38.0-py2.py3-none-any.whl", hash = "sha256:e7dae6694313f434a2727bf2906f27ad259bae090d7aa896590d86feec3d9d4a"}, + {file = "google_auth-2.38.0.tar.gz", hash = "sha256:8285113607d3b80a3f1543b75962447ba8a09fe85783432a784fdeef6ac094c4"}, ] [[package]] name = "graphene" -version = "3.3" +version = "3.4.3" summary = "GraphQL Framework for Python" +groups = ["default"] dependencies = [ - "aniso8601<10,>=8", "graphql-core<3.3,>=3.1", "graphql-relay<3.3,>=3.1", + "python-dateutil<3,>=2.7.0", + "typing-extensions<5,>=4.7.1", ] files = [ - {file = "graphene-3.3-py2.py3-none-any.whl", hash = "sha256:bb3810be33b54cb3e6969506671eb72319e8d7ba0d5ca9c8066472f75bf35a38"}, - {file = "graphene-3.3.tar.gz", hash = "sha256:529bf40c2a698954217d3713c6041d69d3f719ad0080857d7ee31327112446b0"}, + {file = "graphene-3.4.3-py2.py3-none-any.whl", hash = "sha256:820db6289754c181007a150db1f7fff544b94142b556d12e3ebc777a7bf36c71"}, + {file = "graphene-3.4.3.tar.gz", hash = "sha256:2a3786948ce75fe7e078443d37f609cbe5bb36ad8d6b828740ad3b95ed1a0aaa"}, ] [[package]] name = "graphql-core" -version = "3.2.3" -requires_python = ">=3.6,<4" +version = "3.2.6" +requires_python = "<4,>=3.6" summary = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL." +groups = ["default"] +dependencies = [ + "typing-extensions<5,>=4; python_version < \"3.10\"", +] files = [ - {file = "graphql-core-3.2.3.tar.gz", hash = "sha256:06d2aad0ac723e35b1cb47885d3e5c45e956a53bc1b209a9fc5369007fe46676"}, - {file = "graphql_core-3.2.3-py3-none-any.whl", hash = "sha256:5766780452bd5ec8ba133f8bf287dc92713e3868ddd83aee4faab9fc3e303dc3"}, + {file = "graphql_core-3.2.6-py3-none-any.whl", hash = "sha256:78b016718c161a6fb20a7d97bbf107f331cd1afe53e45566c59f776ed7f0b45f"}, + {file = "graphql_core-3.2.6.tar.gz", hash = "sha256:c08eec22f9e40f0bd61d805907e3b3b1b9a320bc606e23dc145eebca07c8fbab"}, ] [[package]] @@ -583,8 +731,10 @@ name = "graphql-relay" version = "3.2.0" requires_python = ">=3.6,<4" summary = "Relay library for graphql-core" +groups = ["default"] dependencies = [ "graphql-core<3.3,>=3.2", + "typing-extensions<5,>=4.1; python_version < \"3.8\"", ] files = [ {file = "graphql-relay-3.2.0.tar.gz", hash = "sha256:1ff1c51298356e481a0be009ccdff249832ce53f30559c1338f22a0e0d17250c"}, @@ -593,29 +743,47 @@ files = [ [[package]] name = "greenlet" -version = "3.0.3" +version = "3.1.1" requires_python = ">=3.7" summary = "Lightweight in-process concurrent programming" -files = [ - {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, - {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, - {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, - {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, - {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +groups = ["default"] +marker = "(platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\") and python_version < \"3.14\"" +files = [ + {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, + {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, + {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, + {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, + {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, + {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, + {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, ] [[package]] @@ -623,7 +791,10 @@ name = "gunicorn" version = "23.0.0" requires_python = ">=3.7" summary = "WSGI HTTP Server for UNIX" +groups = ["default"] +marker = "platform_system != \"Windows\"" dependencies = [ + "importlib-metadata; python_version < \"3.8\"", "packaging", ] files = [ @@ -635,8 +806,10 @@ files = [ name = "hydra-core" version = "1.3.2" summary = "A framework for elegantly configuring complex applications" +groups = ["default"] dependencies = [ "antlr4-python3-runtime==4.9.*", + "importlib-resources; python_version < \"3.9\"", "omegaconf<2.4,>=2.2", "packaging", ] @@ -647,35 +820,54 @@ files = [ [[package]] name = "identify" -version = "2.6.0" -requires_python = ">=3.8" +version = "2.6.8" +requires_python = ">=3.9" summary = "File identification library for Python" +groups = ["dev"] files = [ - {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, - {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, + {file = "identify-2.6.8-py2.py3-none-any.whl", hash = "sha256:83657f0f766a3c8d0eaea16d4ef42494b39b34629a4b3192a9d020d349b3e255"}, + {file = "identify-2.6.8.tar.gz", hash = "sha256:61491417ea2c0c5c670484fd8abbb34de34cdae1e5f39a73ee65e48e4bb663fc"}, ] [[package]] name = "idna" -version = "3.7" -requires_python = ">=3.5" +version = "3.10" +requires_python = ">=3.6" summary = "Internationalized Domain Names in Applications (IDNA)" +groups = ["default"] files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[[package]] +name = "imageio" +version = "2.37.0" +requires_python = ">=3.9" +summary = "Library for reading and writing a wide range of image, video, scientific, and volumetric data formats." +groups = ["default"] +dependencies = [ + "numpy", + "pillow>=8.3.2", +] +files = [ + {file = "imageio-2.37.0-py3-none-any.whl", hash = "sha256:11efa15b87bc7871b61590326b2d635439acc321cf7f8ce996f812543ce10eed"}, + {file = "imageio-2.37.0.tar.gz", hash = "sha256:71b57b3669666272c818497aebba2b4c5f20d5b37c81720e5e1a56d59c492996"}, ] [[package]] name = "importlib-metadata" -version = "8.4.0" +version = "8.5.0" requires_python = ">=3.8" summary = "Read metadata from Python packages" +groups = ["default"] dependencies = [ - "zipp>=0.5", + "typing-extensions>=3.6.4; python_version < \"3.8\"", + "zipp>=3.20", ] files = [ - {file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"}, - {file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"}, + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, ] [[package]] @@ -683,6 +875,7 @@ name = "itsdangerous" version = "2.2.0" requires_python = ">=3.8" summary = "Safely pass data to untrusted environments and back." +groups = ["default"] files = [ {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, @@ -690,15 +883,16 @@ files = [ [[package]] name = "jinja2" -version = "3.1.4" +version = "3.1.6" requires_python = ">=3.7" summary = "A very fast and expressive template engine." +groups = ["default"] dependencies = [ "MarkupSafe>=2.0", ] files = [ - {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, - {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, ] [[package]] @@ -706,6 +900,7 @@ name = "joblib" version = "1.4.2" requires_python = ">=3.8" summary = "Lightweight pipelining with Python functions" +groups = ["default"] files = [ {file = "joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6"}, {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"}, @@ -716,9 +911,12 @@ name = "jsonschema" version = "4.23.0" requires_python = ">=3.8" summary = "An implementation of JSON Schema validation for Python" +groups = ["default"] dependencies = [ "attrs>=22.2.0", + "importlib-resources>=1.4.0; python_version < \"3.9\"", "jsonschema-specifications>=2023.03.6", + "pkgutil-resolve-name>=1.3.10; python_version < \"3.9\"", "referencing>=0.28.4", "rpds-py>=0.7.1", ] @@ -732,6 +930,7 @@ name = "jsonschema-specifications" version = "2024.10.1" requires_python = ">=3.9" summary = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +groups = ["default"] dependencies = [ "referencing>=0.31.0", ] @@ -742,44 +941,70 @@ files = [ [[package]] name = "kiwisolver" -version = "1.4.6" -requires_python = ">=3.8" +version = "1.4.8" +requires_python = ">=3.10" summary = "A fast implementation of the Cassowary constraint solver" -files = [ - {file = "kiwisolver-1.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:45a5cb5abad1ad9c265eed7e058fefafeb7964565b93b397ba2f480faec8d674"}, - {file = "kiwisolver-1.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e52b2568c47fb4f54d17576954e02b1de156c85152f87283a99db9670fd18c0"}, - {file = "kiwisolver-1.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:00af95204100bc1d0f26e1ed52ec77d6e3da5c9b845c88d31875c164e4ba6c0c"}, - {file = "kiwisolver-1.4.6-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50ab1fedf86f3951a9e90a64edd15f598860ed60cd3664259756f097d527b5ae"}, - {file = "kiwisolver-1.4.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbc985766bf20141ce64baecc39fb9fedbce094b2b8de1bb62676b79328988e4"}, - {file = "kiwisolver-1.4.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1942a155c737a7c3835a957897f0cc9ebc0085b7a75d934d86aecb1b27b8873"}, - {file = "kiwisolver-1.4.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f464403e391724f8e7dff188d3fb77a85bd1273b3fdba182e6671abcc44434f8"}, - {file = "kiwisolver-1.4.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce5efe545eea86f52ec5a1185e5052815ea86778e8268bad71fa46433f7c0bef"}, - {file = "kiwisolver-1.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cb30165f71b7b3378668346e220c81d590593a3a1ff76428a53780310df03f35"}, - {file = "kiwisolver-1.4.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5a987f740e1c9964e614acb87ba1f014b4be760a341effc8dc789913d1840e6"}, - {file = "kiwisolver-1.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f2ceaa6d0450623d108956647ef19a1a28c7e07880f1171c932477308d44d80b"}, - {file = "kiwisolver-1.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:788cbf99738f18ae8a27b9d4d7314502b4b917005cfdacd1d6a59038332ae24d"}, - {file = "kiwisolver-1.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2f6668678a6b9488a7f8a6320e1b1c6396d179a976472dbc08d1600d04119511"}, - {file = "kiwisolver-1.4.6-cp311-cp311-win32.whl", hash = "sha256:10a09a3e4213c2806bcfd2eb4edb756c557973d2cacf06873b18a247fce897da"}, - {file = "kiwisolver-1.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:683ffef2c51fdc54112dc610d06b59b88c21e23fb669b905da6d5bec80da1bde"}, - {file = "kiwisolver-1.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:3b852c7f0ed9a2fd339c228829bca0964233ed45de50aae3e87b72ca37d177f8"}, - {file = "kiwisolver-1.4.6-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:979df7e9334f6a3694ee9be8d42817e519ef6d155a16499714d082cf41296852"}, - {file = "kiwisolver-1.4.6-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:50c9c6c42bb6ca231626d1182b9128e89c5ce3c64456f811ff0280deb42d7bfe"}, - {file = "kiwisolver-1.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ace86489e7951bd26329a589198d3875c3d48380f889c69d3eb254b506a80101"}, - {file = "kiwisolver-1.4.6-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f94771988da902b475f78e85cf63c5c94392773b4a6494234d87c1b363b2fbc5"}, - {file = "kiwisolver-1.4.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a62379eee430b1c477bb0a0bf6858a57c7c0dad9cee8b3144a5cb5d366c66a54"}, - {file = "kiwisolver-1.4.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e99b97d69499a7414572c906fbc7ca312519f2e17999730129f6c4492786e953"}, - {file = "kiwisolver-1.4.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab93f58afe3a02922a343189404f24ed885564e6316649790240124b95ef1d6e"}, - {file = "kiwisolver-1.4.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34105f4460ba50fc18a16a8e77a5122f7affe075628763fda748ad0ec534c3ee"}, - {file = "kiwisolver-1.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0081f85f0222620563409d4804c6567a930a45dafbe9674c7913fde131653992"}, - {file = "kiwisolver-1.4.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:df2a4a7cc2e01991e039a792457751b601bdf30143ab5f23f9a1e58f20c875f4"}, - {file = "kiwisolver-1.4.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1f401784df70ea2870e4e10adade66b5b06cb2c151bc2a8a414a1d10554e9a81"}, - {file = "kiwisolver-1.4.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:b19761c8c613b6d04c44f1a4797a144b44136f17ec009ccfb025e17b5698140c"}, - {file = "kiwisolver-1.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ee7289430ded484cc2eff9d8ffcce58ed7fe2c26919321dbc0580322a49e0120"}, - {file = "kiwisolver-1.4.6-cp312-cp312-win32.whl", hash = "sha256:331b9d9f408e874ecf34bd79b79df8e099f0b1b351b8844609c1bfdc8d2d45b2"}, - {file = "kiwisolver-1.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:a9be95d086578b3ada61a4621c0e7ee5f456820bfdccc3329061fdeae1e31179"}, - {file = "kiwisolver-1.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:773f2d87825779ab69196dfcf63e9d91043273421c6128c8d4ed82bc6316068f"}, - {file = "kiwisolver-1.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:140f376c22b5148453acff768cff19c34ebbd593126617018732ea1d9ce65547"}, - {file = "kiwisolver-1.4.6.tar.gz", hash = "sha256:3cda29d601445e6aa11f80d90a9b8c2ae501650c55d7ad29829bd44499c9e7e0"}, +groups = ["default"] +files = [ + {file = "kiwisolver-1.4.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84"}, + {file = "kiwisolver-1.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561"}, + {file = "kiwisolver-1.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c2b9a96e0f326205af81a15718a9073328df1173a2619a68553decb7097fd5d7"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5020c83e8553f770cb3b5fc13faac40f17e0b205bd237aebd21d53d733adb03"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dace81d28c787956bfbfbbfd72fdcef014f37d9b48830829e488fdb32b49d954"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11e1022b524bd48ae56c9b4f9296bce77e15a2e42a502cceba602f804b32bb79"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b9b4d2892fefc886f30301cdd80debd8bb01ecdf165a449eb6e78f79f0fabd6"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a96c0e790ee875d65e340ab383700e2b4891677b7fcd30a699146f9384a2bb0"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23454ff084b07ac54ca8be535f4174170c1094a4cff78fbae4f73a4bcc0d4dab"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:87b287251ad6488e95b4f0b4a79a6d04d3ea35fde6340eb38fbd1ca9cd35bbbc"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b21dbe165081142b1232a240fc6383fd32cdd877ca6cc89eab93e5f5883e1c25"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:768cade2c2df13db52475bd28d3a3fac8c9eff04b0e9e2fda0f3760f20b3f7fc"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d47cfb2650f0e103d4bf68b0b5804c68da97272c84bb12850d877a95c056bd67"}, + {file = "kiwisolver-1.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:ed33ca2002a779a2e20eeb06aea7721b6e47f2d4b8a8ece979d8ba9e2a167e34"}, + {file = "kiwisolver-1.4.8-cp311-cp311-win_arm64.whl", hash = "sha256:16523b40aab60426ffdebe33ac374457cf62863e330a90a0383639ce14bf44b2"}, + {file = "kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502"}, + {file = "kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31"}, + {file = "kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8"}, + {file = "kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50"}, + {file = "kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476"}, + {file = "kiwisolver-1.4.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1c8ceb754339793c24aee1c9fb2485b5b1f5bb1c2c214ff13368431e51fc9a09"}, + {file = "kiwisolver-1.4.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a62808ac74b5e55a04a408cda6156f986cefbcf0ada13572696b507cc92fa1"}, + {file = "kiwisolver-1.4.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68269e60ee4929893aad82666821aaacbd455284124817af45c11e50a4b42e3c"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34d142fba9c464bc3bbfeff15c96eab0e7310343d6aefb62a79d51421fcc5f1b"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc373e0eef45b59197de815b1b28ef89ae3955e7722cc9710fb91cd77b7f47"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77e6f57a20b9bd4e1e2cedda4d0b986ebd0216236f0106e55c28aea3d3d69b16"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08e77738ed7538f036cd1170cbed942ef749137b1311fa2bbe2a7fda2f6bf3cc"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5ce1e481a74b44dd5e92ff03ea0cb371ae7a0268318e202be06c8f04f4f1246"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fc2ace710ba7c1dfd1a3b42530b62b9ceed115f19a1656adefce7b1782a37794"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3452046c37c7692bd52b0e752b87954ef86ee2224e624ef7ce6cb21e8c41cc1b"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e9a60b50fe8b2ec6f448fe8d81b07e40141bfced7f896309df271a0b92f80f3"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:918139571133f366e8362fa4a297aeba86c7816b7ecf0bc79168080e2bd79957"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e063ef9f89885a1d68dd8b2e18f5ead48653176d10a0e324e3b0030e3a69adeb"}, + {file = "kiwisolver-1.4.8-cp313-cp313-win_amd64.whl", hash = "sha256:a17b7c4f5b2c51bb68ed379defd608a03954a1845dfed7cc0117f1cc8a9b7fd2"}, + {file = "kiwisolver-1.4.8-cp313-cp313-win_arm64.whl", hash = "sha256:3cd3bc628b25f74aedc6d374d5babf0166a92ff1317f46267f12d2ed54bc1d30"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:370fd2df41660ed4e26b8c9d6bbcad668fbe2560462cba151a721d49e5b6628c"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:84a2f830d42707de1d191b9490ac186bf7997a9495d4e9072210a1296345f7dc"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7a3ad337add5148cf51ce0b55642dc551c0b9d6248458a757f98796ca7348712"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7506488470f41169b86d8c9aeff587293f530a23a23a49d6bc64dab66bedc71e"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f0121b07b356a22fb0414cec4666bbe36fd6d0d759db3d37228f496ed67c880"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6d6bd87df62c27d4185de7c511c6248040afae67028a8a22012b010bc7ad062"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:291331973c64bb9cce50bbe871fb2e675c4331dab4f31abe89f175ad7679a4d7"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:893f5525bb92d3d735878ec00f781b2de998333659507d29ea4466208df37bed"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b47a465040146981dc9db8647981b8cb96366fbc8d452b031e4f8fdffec3f26d"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:99cea8b9dd34ff80c521aef46a1dddb0dcc0283cf18bde6d756f1e6f31772165"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:151dffc4865e5fe6dafce5480fab84f950d14566c480c08a53c663a0020504b6"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:577facaa411c10421314598b50413aa1ebcf5126f704f1e5d72d7e4e9f020d90"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:be4816dc51c8a471749d664161b434912eee82f2ea66bd7628bd14583a833e85"}, + {file = "kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e"}, ] [[package]] @@ -787,6 +1012,7 @@ name = "kubernetes" version = "32.0.1" requires_python = ">=3.6" summary = "Kubernetes python client" +groups = ["default"] dependencies = [ "certifi>=14.05.14", "durationpy>=0.7", @@ -805,11 +1031,27 @@ files = [ {file = "kubernetes-32.0.1.tar.gz", hash = "sha256:42f43d49abd437ada79a79a16bd48a604d3471a117a8347e87db693f2ba0ba28"}, ] +[[package]] +name = "lazy-loader" +version = "0.4" +requires_python = ">=3.7" +summary = "Makes it easy to load subpackages and functions on demand." +groups = ["default"] +dependencies = [ + "importlib-metadata; python_version < \"3.8\"", + "packaging", +] +files = [ + {file = "lazy_loader-0.4-py3-none-any.whl", hash = "sha256:342aa8e14d543a154047afb4ba8ef17f5563baad3fc610d7b15b213b0f119efc"}, + {file = "lazy_loader-0.4.tar.gz", hash = "sha256:47c75182589b91a4e1a85a136c074285a5ad4d9f39c63e0d7fb76391c4574cd1"}, +] + [[package]] name = "lightning" -version = "2.4.0" +version = "2.5.0.post0" requires_python = ">=3.9" summary = "The Deep Learning framework to train, deploy, and ship AI products Lightning fast." +groups = ["default"] dependencies = [ "PyYAML<8.0,>=5.4", "fsspec[http]<2026.0,>=2022.5.0", @@ -822,36 +1064,39 @@ dependencies = [ "typing-extensions<6.0,>=4.4.0", ] files = [ - {file = "lightning-2.4.0-py3-none-any.whl", hash = "sha256:560163af9711cf59055c448232c473150a299089efce0d2be3cc3288082d8768"}, - {file = "lightning-2.4.0.tar.gz", hash = "sha256:9156604cc56e4b2b603f34fa7f0fe5107375c8e6d85e74544b319a15faa9ed0e"}, + {file = "lightning-2.5.0.post0-py3-none-any.whl", hash = "sha256:b08463326e6fb39cb3e4db8ff2660a80ce3372a0688c80e3370c091346ea220c"}, + {file = "lightning-2.5.0.post0.tar.gz", hash = "sha256:f720fe4f6d03a7f15f9aef3112c5a0d1eafd8d27b903f4a1354b609685b2ec70"}, ] [[package]] name = "lightning-utilities" -version = "0.11.6" -requires_python = ">=3.8" +version = "0.13.1" +requires_python = ">=3.9" summary = "Lightning toolbox for across the our ecosystem." +groups = ["default"] dependencies = [ + "importlib-metadata>=4.0.0; python_version < \"3.8\"", "packaging>=17.1", "setuptools", "typing-extensions", ] files = [ - {file = "lightning_utilities-0.11.6-py3-none-any.whl", hash = "sha256:ecd9953c316cbaf56ad820fbe7bd062187b9973c4a23d47b076cd59dc080a310"}, - {file = "lightning_utilities-0.11.6.tar.gz", hash = "sha256:79fc27ef8ec8b8d55a537920f2c7610270c0c9e037fa6efc78f1aa34ec8cdf04"}, + {file = "lightning_utilities-0.13.1-py3-none-any.whl", hash = "sha256:5e468a3fd5d656ed36898b6efc9420fb476f4e24d14e3a9d59571ddb63620cf2"}, + {file = "lightning_utilities-0.13.1.tar.gz", hash = "sha256:ae9614d6e5165bd1bccf4211dbbe3265ae9a56e989089d768aac21c7e0af3558"}, ] [[package]] name = "mako" -version = "1.3.5" +version = "1.3.9" requires_python = ">=3.8" summary = "A super-fast templating language that borrows the best ideas from the existing templating languages." +groups = ["default"] dependencies = [ "MarkupSafe>=0.9.2", ] files = [ - {file = "Mako-1.3.5-py3-none-any.whl", hash = "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a"}, - {file = "Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc"}, + {file = "Mako-1.3.9-py3-none-any.whl", hash = "sha256:95920acccb578427a9aa38e37a186b1e43156c87260d7ba18ca63aa4c7cbd3a1"}, + {file = "mako-1.3.9.tar.gz", hash = "sha256:b5d65ff3462870feec922dbccf38f6efb44e5714d7b593a656be86663d8600ac"}, ] [[package]] @@ -859,6 +1104,10 @@ name = "markdown" version = "3.7" requires_python = ">=3.8" summary = "Python implementation of John Gruber's Markdown." +groups = ["default"] +dependencies = [ + "importlib-metadata>=4.4; python_version < \"3.10\"", +] files = [ {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, @@ -866,38 +1115,60 @@ files = [ [[package]] name = "markupsafe" -version = "2.1.5" -requires_python = ">=3.7" +version = "3.0.2" +requires_python = ">=3.9" summary = "Safely add untrusted strings to HTML/XML markup." -files = [ - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, - {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +groups = ["default"] +files = [ + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] [[package]] name = "matplotlib" -version = "3.9.2" -requires_python = ">=3.9" +version = "3.10.1" +requires_python = ">=3.10" summary = "Python plotting package" +groups = ["default"] dependencies = [ "contourpy>=1.0.1", "cycler>=0.10", @@ -910,64 +1181,69 @@ dependencies = [ "python-dateutil>=2.7", ] files = [ - {file = "matplotlib-3.9.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d8dd059447824eec055e829258ab092b56bb0579fc3164fa09c64f3acd478772"}, - {file = "matplotlib-3.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c797dac8bb9c7a3fd3382b16fe8f215b4cf0f22adccea36f1545a6d7be310b41"}, - {file = "matplotlib-3.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d719465db13267bcef19ea8954a971db03b9f48b4647e3860e4bc8e6ed86610f"}, - {file = "matplotlib-3.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8912ef7c2362f7193b5819d17dae8629b34a95c58603d781329712ada83f9447"}, - {file = "matplotlib-3.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7741f26a58a240f43bee74965c4882b6c93df3e7eb3de160126d8c8f53a6ae6e"}, - {file = "matplotlib-3.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:ae82a14dab96fbfad7965403c643cafe6515e386de723e498cf3eeb1e0b70cc7"}, - {file = "matplotlib-3.9.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ac43031375a65c3196bee99f6001e7fa5bdfb00ddf43379d3c0609bdca042df9"}, - {file = "matplotlib-3.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:be0fc24a5e4531ae4d8e858a1a548c1fe33b176bb13eff7f9d0d38ce5112a27d"}, - {file = "matplotlib-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf81de2926c2db243c9b2cbc3917619a0fc85796c6ba4e58f541df814bbf83c7"}, - {file = "matplotlib-3.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ee45bc4245533111ced13f1f2cace1e7f89d1c793390392a80c139d6cf0e6c"}, - {file = "matplotlib-3.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:306c8dfc73239f0e72ac50e5a9cf19cc4e8e331dd0c54f5e69ca8758550f1e1e"}, - {file = "matplotlib-3.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:5413401594cfaff0052f9d8b1aafc6d305b4bd7c4331dccd18f561ff7e1d3bd3"}, - {file = "matplotlib-3.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:18128cc08f0d3cfff10b76baa2f296fc28c4607368a8402de61bb3f2eb33c7d9"}, - {file = "matplotlib-3.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4876d7d40219e8ae8bb70f9263bcbe5714415acfdf781086601211335e24f8aa"}, - {file = "matplotlib-3.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d9f07a80deab4bb0b82858a9e9ad53d1382fd122be8cde11080f4e7dfedb38b"}, - {file = "matplotlib-3.9.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7c0410f181a531ec4e93bbc27692f2c71a15c2da16766f5ba9761e7ae518413"}, - {file = "matplotlib-3.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:909645cce2dc28b735674ce0931a4ac94e12f5b13f6bb0b5a5e65e7cea2c192b"}, - {file = "matplotlib-3.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:f32c7410c7f246838a77d6d1eff0c0f87f3cb0e7c4247aebea71a6d5a68cab49"}, - {file = "matplotlib-3.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:37e51dd1c2db16ede9cfd7b5cabdfc818b2c6397c83f8b10e0e797501c963a03"}, - {file = "matplotlib-3.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b82c5045cebcecd8496a4d694d43f9cc84aeeb49fe2133e036b207abe73f4d30"}, - {file = "matplotlib-3.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f053c40f94bc51bc03832a41b4f153d83f2062d88c72b5e79997072594e97e51"}, - {file = "matplotlib-3.9.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbe196377a8248972f5cede786d4c5508ed5f5ca4a1e09b44bda889958b33f8c"}, - {file = "matplotlib-3.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5816b1e1fe8c192cbc013f8f3e3368ac56fbecf02fb41b8f8559303f24c5015e"}, - {file = "matplotlib-3.9.2.tar.gz", hash = "sha256:96ab43906269ca64a6366934106fa01534454a69e471b7bf3d79083981aaab92"}, + {file = "matplotlib-3.10.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:057206ff2d6ab82ff3e94ebd94463d084760ca682ed5f150817b859372ec4401"}, + {file = "matplotlib-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a144867dd6bf8ba8cb5fc81a158b645037e11b3e5cf8a50bd5f9917cb863adfe"}, + {file = "matplotlib-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56c5d9fcd9879aa8040f196a235e2dcbdf7dd03ab5b07c0696f80bc6cf04bedd"}, + {file = "matplotlib-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f69dc9713e4ad2fb21a1c30e37bd445d496524257dfda40ff4a8efb3604ab5c"}, + {file = "matplotlib-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4c59af3e8aca75d7744b68e8e78a669e91ccbcf1ac35d0102a7b1b46883f1dd7"}, + {file = "matplotlib-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:11b65088c6f3dae784bc72e8d039a2580186285f87448babb9ddb2ad0082993a"}, + {file = "matplotlib-3.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:66e907a06e68cb6cfd652c193311d61a12b54f56809cafbed9736ce5ad92f107"}, + {file = "matplotlib-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b4bb156abb8fa5e5b2b460196f7db7264fc6d62678c03457979e7d5254b7be"}, + {file = "matplotlib-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1985ad3d97f51307a2cbfc801a930f120def19ba22864182dacef55277102ba6"}, + {file = "matplotlib-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c96f2c2f825d1257e437a1482c5a2cf4fee15db4261bd6fc0750f81ba2b4ba3d"}, + {file = "matplotlib-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35e87384ee9e488d8dd5a2dd7baf471178d38b90618d8ea147aced4ab59c9bea"}, + {file = "matplotlib-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:cfd414bce89cc78a7e1d25202e979b3f1af799e416010a20ab2b5ebb3a02425c"}, + {file = "matplotlib-3.10.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c42eee41e1b60fd83ee3292ed83a97a5f2a8239b10c26715d8a6172226988d7b"}, + {file = "matplotlib-3.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4f0647b17b667ae745c13721602b540f7aadb2a32c5b96e924cd4fea5dcb90f1"}, + {file = "matplotlib-3.10.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa3854b5f9473564ef40a41bc922be978fab217776e9ae1545c9b3a5cf2092a3"}, + {file = "matplotlib-3.10.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e496c01441be4c7d5f96d4e40f7fca06e20dcb40e44c8daa2e740e1757ad9e6"}, + {file = "matplotlib-3.10.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5d45d3f5245be5b469843450617dcad9af75ca50568acf59997bed9311131a0b"}, + {file = "matplotlib-3.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:8e8e25b1209161d20dfe93037c8a7f7ca796ec9aa326e6e4588d8c4a5dd1e473"}, + {file = "matplotlib-3.10.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:19b06241ad89c3ae9469e07d77efa87041eac65d78df4fcf9cac318028009b01"}, + {file = "matplotlib-3.10.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01e63101ebb3014e6e9f80d9cf9ee361a8599ddca2c3e166c563628b39305dbb"}, + {file = "matplotlib-3.10.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f06bad951eea6422ac4e8bdebcf3a70c59ea0a03338c5d2b109f57b64eb3972"}, + {file = "matplotlib-3.10.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3dfb036f34873b46978f55e240cff7a239f6c4409eac62d8145bad3fc6ba5a3"}, + {file = "matplotlib-3.10.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dc6ab14a7ab3b4d813b88ba957fc05c79493a037f54e246162033591e770de6f"}, + {file = "matplotlib-3.10.1-cp313-cp313t-win_amd64.whl", hash = "sha256:bc411ebd5889a78dabbc457b3fa153203e22248bfa6eedc6797be5df0164dbf9"}, + {file = "matplotlib-3.10.1.tar.gz", hash = "sha256:e8d2d0e3881b129268585bf4765ad3ee73a4591d77b9a18c214ac7e3a79fb2ba"}, ] [[package]] name = "mlflow" -version = "2.16.0" -requires_python = ">=3.8" +version = "2.20.3" +requires_python = ">=3.9" summary = "MLflow is an open source platform for the complete machine learning lifecycle" +groups = ["default"] dependencies = [ "Flask<4", "Jinja2<4,>=2.11; platform_system != \"Windows\"", + "Jinja2<4,>=3.0; platform_system == \"Windows\"", "alembic!=1.10.0,<2", "docker<8,>=4.0.0", "graphene<4", "gunicorn<24; platform_system != \"Windows\"", "markdown<4,>=3.3", "matplotlib<4", - "mlflow-skinny==2.16.0", + "mlflow-skinny==2.20.3", "numpy<3", "pandas<3", - "pyarrow<18,>=4.0.0", + "pyarrow<20,>=4.0.0", "scikit-learn<2", "scipy<2", "sqlalchemy<3,>=1.4.0", + "waitress<4; platform_system == \"Windows\"", ] files = [ - {file = "mlflow-2.16.0-py3-none-any.whl", hash = "sha256:9f27ef6ae7a82d7ecd67b6b4a4d50637a5e8160639115570fbc689758f9c0b54"}, - {file = "mlflow-2.16.0.tar.gz", hash = "sha256:82ea1a2e800f404f1586783b7636091c0a5754cf9ff45afeadf3a5e467f5168f"}, + {file = "mlflow-2.20.3-py3-none-any.whl", hash = "sha256:efafe5d4d17b53be1ae02c7d8708a5e4bbde4bd3aecd2bd68b64a3c4175e9dc6"}, + {file = "mlflow-2.20.3.tar.gz", hash = "sha256:a7b1baf53d4f10160864961320df0c4cb74fb4f21c7522ef80a35290d03573bb"}, ] [[package]] name = "mlflow-skinny" -version = "2.16.0" -requires_python = ">=3.8" +version = "2.20.3" +requires_python = ">=3.9" summary = "MLflow is an open source platform for the complete machine learning lifecycle" +groups = ["default"] dependencies = [ "cachetools<6,>=5.0.0", "click<9,>=7.0", @@ -979,19 +1255,23 @@ dependencies = [ "opentelemetry-sdk<3,>=1.9.0", "packaging<25", "protobuf<6,>=3.12.0", + "pydantic<3,>=1.10.8", "pyyaml<7,>=5.1", "requests<3,>=2.17.3", "sqlparse<1,>=0.4.0", + "typing-extensions<5,>=4.0.0", ] files = [ - {file = "mlflow_skinny-2.16.0-py3-none-any.whl", hash = "sha256:c55541f50efd0f6637377b10e8a654847a3fcd815b8680a95f02e0ca6bd7700c"}, - {file = "mlflow_skinny-2.16.0.tar.gz", hash = "sha256:9b823173063743783b4e7b6c52bdadcc7d9dab48eb883ac454c0d56609df6b2d"}, + {file = "mlflow_skinny-2.20.3-py3-none-any.whl", hash = "sha256:4cf9502bf8b7c4c971c90808560caeb2d57608354927f7b7b3150ca2c580c022"}, + {file = "mlflow_skinny-2.20.3.tar.gz", hash = "sha256:4151f74500611f4c2ee1caf30b0108817b456654b42edbede2503dd6e845ed91"}, ] [[package]] name = "mpmath" version = "1.3.0" summary = "Python library for arbitrary-precision floating-point arithmetic" +groups = ["default"] +marker = "python_version >= \"3.9\"" files = [ {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, @@ -1002,73 +1282,135 @@ name = "msgpack" version = "1.1.0" requires_python = ">=3.8" summary = "MessagePack serializer" +groups = ["default"] files = [ + {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6"}, {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5"}, + {file = "msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88"}, + {file = "msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b"}, + {file = "msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b"}, + {file = "msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c"}, + {file = "msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc"}, + {file = "msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f"}, {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"}, ] [[package]] name = "multidict" -version = "6.0.5" -requires_python = ">=3.7" +version = "6.1.0" +requires_python = ">=3.8" summary = "multidict implementation" -files = [ - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, - {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, - {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, - {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, - {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, - {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, - {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, +groups = ["default"] +dependencies = [ + "typing-extensions>=4.1.0; python_version < \"3.11\"", +] +files = [ + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, + {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, + {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, + {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, + {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, + {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, + {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, + {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, + {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, ] [[package]] name = "mypy" -version = "1.11.1" -requires_python = ">=3.8" +version = "1.15.0" +requires_python = ">=3.9" summary = "Optional static typing for Python" +groups = ["dev"] dependencies = [ "mypy-extensions>=1.0.0", + "tomli>=1.1.0; python_version < \"3.11\"", "typing-extensions>=4.6.0", ] files = [ - {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, - {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, - {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, - {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, - {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, - {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, - {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, - {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, - {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, + {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, + {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, + {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, + {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, + {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, + {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, + {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, + {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, + {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, + {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, + {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, + {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, + {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, + {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, + {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, + {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, + {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, + {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, + {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, + {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, ] [[package]] @@ -1076,6 +1418,7 @@ name = "mypy-extensions" version = "1.0.0" requires_python = ">=3.5" summary = "Type system extensions for programs checked with the mypy type checker." +groups = ["dev"] files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, @@ -1083,12 +1426,13 @@ files = [ [[package]] name = "networkx" -version = "3.3" +version = "3.4.2" requires_python = ">=3.10" summary = "Python package for creating and manipulating graphs and networks" +groups = ["default"] files = [ - {file = "networkx-3.3-py3-none-any.whl", hash = "sha256:28575580c6ebdaf4505b22c6256a2b9de86b316dc63ba9e93abde3d78dfdbcf2"}, - {file = "networkx-3.3.tar.gz", hash = "sha256:0c127d8b2f4865f59ae9cb8aafcd60b5c70f3241ebd66f7defad7c4ab90126c9"}, + {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, + {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"}, ] [[package]] @@ -1096,6 +1440,7 @@ name = "nodeenv" version = "1.9.1" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" summary = "Node.js virtual environment builder" +groups = ["dev"] files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -1103,27 +1448,52 @@ files = [ [[package]] name = "numpy" -version = "1.26.4" -requires_python = ">=3.9" +version = "2.2.3" +requires_python = ">=3.10" summary = "Fundamental package for array computing in Python" -files = [ - {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, - {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, - {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, - {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, - {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, - {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, +groups = ["default"] +files = [ + {file = "numpy-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:16372619ee728ed67a2a606a614f56d3eabc5b86f8b615c79d01957062826ca8"}, + {file = "numpy-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5521a06a3148686d9269c53b09f7d399a5725c47bbb5b35747e1cb76326b714b"}, + {file = "numpy-2.2.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:7c8dde0ca2f77828815fd1aedfdf52e59071a5bae30dac3b4da2a335c672149a"}, + {file = "numpy-2.2.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:77974aba6c1bc26e3c205c2214f0d5b4305bdc719268b93e768ddb17e3fdd636"}, + {file = "numpy-2.2.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d42f9c36d06440e34226e8bd65ff065ca0963aeecada587b937011efa02cdc9d"}, + {file = "numpy-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2712c5179f40af9ddc8f6727f2bd910ea0eb50206daea75f58ddd9fa3f715bb"}, + {file = "numpy-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c8b0451d2ec95010d1db8ca733afc41f659f425b7f608af569711097fd6014e2"}, + {file = "numpy-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9b4a8148c57ecac25a16b0e11798cbe88edf5237b0df99973687dd866f05e1b"}, + {file = "numpy-2.2.3-cp311-cp311-win32.whl", hash = "sha256:1f45315b2dc58d8a3e7754fe4e38b6fce132dab284a92851e41b2b344f6441c5"}, + {file = "numpy-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f48ba6f6c13e5e49f3d3efb1b51c8193215c42ac82610a04624906a9270be6f"}, + {file = "numpy-2.2.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12c045f43b1d2915eca6b880a7f4a256f59d62df4f044788c8ba67709412128d"}, + {file = "numpy-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:87eed225fd415bbae787f93a457af7f5990b92a334e346f72070bf569b9c9c95"}, + {file = "numpy-2.2.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:712a64103d97c404e87d4d7c47fb0c7ff9acccc625ca2002848e0d53288b90ea"}, + {file = "numpy-2.2.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a5ae282abe60a2db0fd407072aff4599c279bcd6e9a2475500fc35b00a57c532"}, + {file = "numpy-2.2.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5266de33d4c3420973cf9ae3b98b54a2a6d53a559310e3236c4b2b06b9c07d4e"}, + {file = "numpy-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b787adbf04b0db1967798dba8da1af07e387908ed1553a0d6e74c084d1ceafe"}, + {file = "numpy-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:34c1b7e83f94f3b564b35f480f5652a47007dd91f7c839f404d03279cc8dd021"}, + {file = "numpy-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4d8335b5f1b6e2bce120d55fb17064b0262ff29b459e8493d1785c18ae2553b8"}, + {file = "numpy-2.2.3-cp312-cp312-win32.whl", hash = "sha256:4d9828d25fb246bedd31e04c9e75714a4087211ac348cb39c8c5f99dbb6683fe"}, + {file = "numpy-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:83807d445817326b4bcdaaaf8e8e9f1753da04341eceec705c001ff342002e5d"}, + {file = "numpy-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bfdb06b395385ea9b91bf55c1adf1b297c9fdb531552845ff1d3ea6e40d5aba"}, + {file = "numpy-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:23c9f4edbf4c065fddb10a4f6e8b6a244342d95966a48820c614891e5059bb50"}, + {file = "numpy-2.2.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:a0c03b6be48aaf92525cccf393265e02773be8fd9551a2f9adbe7db1fa2b60f1"}, + {file = "numpy-2.2.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:2376e317111daa0a6739e50f7ee2a6353f768489102308b0d98fcf4a04f7f3b5"}, + {file = "numpy-2.2.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fb62fe3d206d72fe1cfe31c4a1106ad2b136fcc1606093aeab314f02930fdf2"}, + {file = "numpy-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52659ad2534427dffcc36aac76bebdd02b67e3b7a619ac67543bc9bfe6b7cdb1"}, + {file = "numpy-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1b416af7d0ed3271cad0f0a0d0bee0911ed7eba23e66f8424d9f3dfcdcae1304"}, + {file = "numpy-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1402da8e0f435991983d0a9708b779f95a8c98c6b18a171b9f1be09005e64d9d"}, + {file = "numpy-2.2.3-cp313-cp313-win32.whl", hash = "sha256:136553f123ee2951bfcfbc264acd34a2fc2f29d7cdf610ce7daf672b6fbaa693"}, + {file = "numpy-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:5b732c8beef1d7bc2d9e476dbba20aaff6167bf205ad9aa8d30913859e82884b"}, + {file = "numpy-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:435e7a933b9fda8126130b046975a968cc2d833b505475e588339e09f7672890"}, + {file = "numpy-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7678556eeb0152cbd1522b684dcd215250885993dd00adb93679ec3c0e6e091c"}, + {file = "numpy-2.2.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2e8da03bd561504d9b20e7a12340870dfc206c64ea59b4cfee9fceb95070ee94"}, + {file = "numpy-2.2.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:c9aa4496fd0e17e3843399f533d62857cef5900facf93e735ef65aa4bbc90ef0"}, + {file = "numpy-2.2.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4ca91d61a4bf61b0f2228f24bbfa6a9facd5f8af03759fe2a655c50ae2c6610"}, + {file = "numpy-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:deaa09cd492e24fd9b15296844c0ad1b3c976da7907e1c1ed3a0ad21dded6f76"}, + {file = "numpy-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:246535e2f7496b7ac85deffe932896a3577be7af8fb7eebe7146444680297e9a"}, + {file = "numpy-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:daf43a3d1ea699402c5a850e5313680ac355b4adc9770cd5cfc2940e7861f1bf"}, + {file = "numpy-2.2.3-cp313-cp313t-win32.whl", hash = "sha256:cf802eef1f0134afb81fef94020351be4fe1d6681aadf9c5e862af6602af64ef"}, + {file = "numpy-2.2.3-cp313-cp313t-win_amd64.whl", hash = "sha256:aee2512827ceb6d7f517c8b85aa5d3923afe8fc7a57d028cffcd522f1c6fd082"}, + {file = "numpy-2.2.3.tar.gz", hash = "sha256:dbdc15f0c81611925f382dfa97b3bd0bc2c1ce19d4fe50482cb0ddc12ba30020"}, ] [[package]] @@ -1131,8 +1501,12 @@ name = "nvidia-cublas-cu12" version = "12.4.5.8" requires_python = ">=3" summary = "CUBLAS native runtime libraries" +groups = ["default"] +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ + {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0f8aa1706812e00b9f19dfe0cdb3999b092ccb8ca168c0db5b8ea712456fd9b3"}, {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2fc8da60df463fdefa81e323eef2e36489e1c94335b5358bcb38360adf75ac9b"}, + {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-win_amd64.whl", hash = "sha256:5a796786da89203a0657eda402bcdcec6180254a8ac22d72213abc42069522dc"}, ] [[package]] @@ -1140,8 +1514,12 @@ name = "nvidia-cuda-cupti-cu12" version = "12.4.127" requires_python = ">=3" summary = "CUDA profiling tools runtime libs." +groups = ["default"] +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ + {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:79279b35cf6f91da114182a5ce1864997fd52294a87a16179ce275773799458a"}, {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9dec60f5ac126f7bb551c055072b69d85392b13311fcc1bcda2202d172df30fb"}, + {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:5688d203301ab051449a2b1cb6690fbe90d2b372f411521c86018b950f3d7922"}, ] [[package]] @@ -1149,8 +1527,12 @@ name = "nvidia-cuda-nvrtc-cu12" version = "12.4.127" requires_python = ">=3" summary = "NVRTC native runtime libraries" +groups = ["default"] +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ + {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0eedf14185e04b76aa05b1fea04133e59f465b6f960c0cbf4e37c3cb6b0ea198"}, {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a178759ebb095827bd30ef56598ec182b85547f1508941a3d560eb7ea1fbf338"}, + {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:a961b2f1d5f17b14867c619ceb99ef6fcec12e46612711bcec78eb05068a60ec"}, ] [[package]] @@ -1158,8 +1540,12 @@ name = "nvidia-cuda-runtime-cu12" version = "12.4.127" requires_python = ">=3" summary = "CUDA Runtime native Libraries" +groups = ["default"] +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ + {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:961fe0e2e716a2a1d967aab7caee97512f71767f852f67432d572e36cb3a11f3"}, {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:64403288fa2136ee8e467cdc9c9427e0434110899d07c779f25b5c068934faa5"}, + {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:09c2e35f48359752dfa822c09918211844a3d93c100a715d79b59591130c5e1e"}, ] [[package]] @@ -1167,6 +1553,8 @@ name = "nvidia-cudnn-cu12" version = "9.1.0.70" requires_python = ">=3" summary = "cuDNN runtime libraries" +groups = ["default"] +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" dependencies = [ "nvidia-cublas-cu12", ] @@ -1180,8 +1568,15 @@ name = "nvidia-cufft-cu12" version = "11.2.1.3" requires_python = ">=3" summary = "CUFFT native runtime libraries" +groups = ["default"] +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +dependencies = [ + "nvidia-nvjitlink-cu12", +] files = [ + {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5dad8008fc7f92f5ddfa2101430917ce2ffacd86824914c82e28990ad7f00399"}, {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f083fc24912aa410be21fa16d157fed2055dab1cc4b6934a0e03cba69eb242b9"}, + {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-win_amd64.whl", hash = "sha256:d802f4954291101186078ccbe22fc285a902136f974d369540fd4a5333d1440b"}, ] [[package]] @@ -1189,8 +1584,12 @@ name = "nvidia-curand-cu12" version = "10.3.5.147" requires_python = ">=3" summary = "CURAND native runtime libraries" +groups = ["default"] +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ + {file = "nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1f173f09e3e3c76ab084aba0de819c49e56614feae5c12f69883f4ae9bb5fad9"}, {file = "nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a88f583d4e0bb643c49743469964103aa59f7f708d862c3ddb0fc07f851e3b8b"}, + {file = "nvidia_curand_cu12-10.3.5.147-py3-none-win_amd64.whl", hash = "sha256:f307cc191f96efe9e8f05a87096abc20d08845a841889ef78cb06924437f6771"}, ] [[package]] @@ -1198,13 +1597,17 @@ name = "nvidia-cusolver-cu12" version = "11.6.1.9" requires_python = ">=3" summary = "CUDA solver native runtime libraries" +groups = ["default"] +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" dependencies = [ "nvidia-cublas-cu12", "nvidia-cusparse-cu12", "nvidia-nvjitlink-cu12", ] files = [ + {file = "nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d338f155f174f90724bbde3758b7ac375a70ce8e706d70b018dd3375545fc84e"}, {file = "nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:19e33fa442bcfd085b3086c4ebf7e8debc07cfe01e11513cc6d332fd918ac260"}, + {file = "nvidia_cusolver_cu12-11.6.1.9-py3-none-win_amd64.whl", hash = "sha256:e77314c9d7b694fcebc84f58989f3aa4fb4cb442f12ca1a9bde50f5e8f6d1b9c"}, ] [[package]] @@ -1212,19 +1615,27 @@ name = "nvidia-cusparse-cu12" version = "12.3.1.170" requires_python = ">=3" summary = "CUSPARSE native runtime libraries" +groups = ["default"] +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" dependencies = [ "nvidia-nvjitlink-cu12", ] files = [ + {file = "nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9d32f62896231ebe0480efd8a7f702e143c98cfaa0e8a76df3386c1ba2b54df3"}, {file = "nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ea4f11a2904e2a8dc4b1833cc1b5181cde564edd0d5cd33e3c168eff2d1863f1"}, + {file = "nvidia_cusparse_cu12-12.3.1.170-py3-none-win_amd64.whl", hash = "sha256:9bc90fb087bc7b4c15641521f31c0371e9a612fc2ba12c338d3ae032e6b6797f"}, ] [[package]] name = "nvidia-cusparselt-cu12" version = "0.6.2" summary = "NVIDIA cuSPARSELt" +groups = ["default"] +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ + {file = "nvidia_cusparselt_cu12-0.6.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:067a7f6d03ea0d4841c85f0c6f1991c5dda98211f6302cb83a4ab234ee95bef8"}, {file = "nvidia_cusparselt_cu12-0.6.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:df2c24502fd76ebafe7457dbc4716b2fec071aabaed4fb7691a201cde03704d9"}, + {file = "nvidia_cusparselt_cu12-0.6.2-py3-none-win_amd64.whl", hash = "sha256:0057c91d230703924c0422feabe4ce768841f9b4b44d28586b6f6d2eb86fbe70"}, ] [[package]] @@ -1232,6 +1643,8 @@ name = "nvidia-nccl-cu12" version = "2.21.5" requires_python = ">=3" summary = "NVIDIA Collective Communication Library (NCCL) Runtime" +groups = ["default"] +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8579076d30a8c24988834445f8d633c697d42397e92ffc3f63fa26766d25e0a0"}, ] @@ -1241,8 +1654,12 @@ name = "nvidia-nvjitlink-cu12" version = "12.4.127" requires_python = ">=3" summary = "Nvidia JIT LTO Library" +groups = ["default"] +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ + {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4abe7fef64914ccfa909bc2ba39739670ecc9e820c83ccc7a6ed414122599b83"}, {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57"}, + {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:fd9020c501d27d135f983c6d3e244b197a7ccad769e34df53a42e276b0e25fa1"}, ] [[package]] @@ -1250,8 +1667,12 @@ name = "nvidia-nvtx-cu12" version = "12.4.127" requires_python = ">=3" summary = "NVIDIA Tools Extension" +groups = ["default"] +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ + {file = "nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7959ad635db13edf4fc65c06a6e9f9e55fc2f92596db928d169c0bb031e88ef3"}, {file = "nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a"}, + {file = "nvidia_nvtx_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:641dccaaa1139f3ffb0d3164b4b84f9d253397e38246a4f2f36728b48566d485"}, ] [[package]] @@ -1259,6 +1680,7 @@ name = "oauthlib" version = "3.2.2" requires_python = ">=3.6" summary = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +groups = ["default"] files = [ {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, @@ -1269,9 +1691,11 @@ name = "omegaconf" version = "2.3.0" requires_python = ">=3.6" summary = "A flexible configuration library" +groups = ["default"] dependencies = [ "PyYAML>=5.1.0", "antlr4-python3-runtime==4.9.*", + "dataclasses; python_version == \"3.6\"", ] files = [ {file = "omegaconf-2.3.0-py3-none-any.whl", hash = "sha256:7b4df175cdb08ba400f45cae3bdcae7ba8365db4d165fc65fd04b050ab63b46b"}, @@ -1283,183 +1707,216 @@ name = "opencv-python-headless" version = "4.11.0.86" requires_python = ">=3.6" summary = "Wrapper package for OpenCV python bindings." +groups = ["default"] dependencies = [ + "numpy>=1.13.3; python_version < \"3.7\"", "numpy>=1.17.0; python_version >= \"3.7\"", "numpy>=1.17.3; python_version >= \"3.8\"", + "numpy>=1.19.3; python_version >= \"3.6\" and platform_system == \"Linux\" and platform_machine == \"aarch64\"", "numpy>=1.19.3; python_version >= \"3.9\"", + "numpy>=1.21.0; python_version <= \"3.9\" and platform_system == \"Darwin\" and platform_machine == \"arm64\"", "numpy>=1.21.2; python_version >= \"3.10\"", + "numpy>=1.21.4; python_version >= \"3.10\" and platform_system == \"Darwin\"", "numpy>=1.23.5; python_version >= \"3.11\"", + "numpy>=1.26.0; python_version >= \"3.12\"", ] files = [ {file = "opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798"}, + {file = "opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca"}, + {file = "opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81"}, + {file = "opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6efabcaa9df731f29e5ea9051776715b1bdd1845d7c9530065c7951d2a2899eb"}, {file = "opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b"}, + {file = "opencv_python_headless-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:f447d8acbb0b6f2808da71fddd29c1cdd448d2bc98f72d9bb78a7a898fc9621b"}, + {file = "opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca"}, ] [[package]] name = "openslide-python" -version = "1.3.1" +version = "1.4.1" requires_python = ">=3.8" summary = "Python interface to OpenSlide" +groups = ["default"] dependencies = [ "Pillow", ] files = [ - {file = "openslide-python-1.3.1.tar.gz", hash = "sha256:0909c6257cd8decfbbd0082e8c0cd94bbe3a89ad31e142cfa9accc8bb959294e"}, - {file = "openslide_python-1.3.1-cp311-cp311-win32.whl", hash = "sha256:c4720598ba39e7b879e757eff31195f8b80d4638dcb0fbb297ca9823039724ae"}, - {file = "openslide_python-1.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:53a468cd92bdd17cf5b56592242709519c0c7d7028b2f466d20d75264471cc6d"}, - {file = "openslide_python-1.3.1-cp312-cp312-win32.whl", hash = "sha256:d10caf1a1c1e1f598d80e7a5e1a266979ed9bccf9ba8bf45aa34cf04639d9f9e"}, - {file = "openslide_python-1.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:d834fbca0824b902da9d8541f7c34a3e62496823a42de5ac7bf6c35e4c799678"}, + {file = "openslide_python-1.4.1-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:3715fbed151ce448998a83d4a1c3defb1992c5e7e73d66fa05294bfecec0740c"}, + {file = "openslide_python-1.4.1-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a63a4c01dfc15ceebaf4cd1d14c97e3f4e6658ef1f83b003d7e4e85a1a1aad39"}, + {file = "openslide_python-1.4.1-cp311-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f3015d4f591471d5c39fd7f853e8d3308eb29eff0e4be3938a8c9d97d0b72256"}, + {file = "openslide_python-1.4.1-cp311-abi3-win_amd64.whl", hash = "sha256:57ec66edf3e8f22dd8712071227f2e256fa04a542c99d72bb211d00285ccf0a2"}, + {file = "openslide_python-1.4.1.tar.gz", hash = "sha256:95da570994abd8a02db18b8dac68da0b3d3f7eba733bdeeb2a8b52e40e1de1c8"}, ] [[package]] name = "opentelemetry-api" -version = "1.27.0" +version = "1.30.0" requires_python = ">=3.8" summary = "OpenTelemetry Python API" +groups = ["default"] dependencies = [ "deprecated>=1.2.6", - "importlib-metadata<=8.4.0,>=6.0", + "importlib-metadata<=8.5.0,>=6.0", ] files = [ - {file = "opentelemetry_api-1.27.0-py3-none-any.whl", hash = "sha256:953d5871815e7c30c81b56d910c707588000fff7a3ca1c73e6531911d53065e7"}, - {file = "opentelemetry_api-1.27.0.tar.gz", hash = "sha256:ed673583eaa5f81b5ce5e86ef7cdaf622f88ef65f0b9aab40b843dcae5bef342"}, + {file = "opentelemetry_api-1.30.0-py3-none-any.whl", hash = "sha256:d5f5284890d73fdf47f843dda3210edf37a38d66f44f2b5aedc1e89ed455dc09"}, + {file = "opentelemetry_api-1.30.0.tar.gz", hash = "sha256:375893400c1435bf623f7dfb3bcd44825fe6b56c34d0667c542ea8257b1a1240"}, ] [[package]] name = "opentelemetry-sdk" -version = "1.27.0" +version = "1.30.0" requires_python = ">=3.8" summary = "OpenTelemetry Python SDK" +groups = ["default"] dependencies = [ - "opentelemetry-api==1.27.0", - "opentelemetry-semantic-conventions==0.48b0", + "opentelemetry-api==1.30.0", + "opentelemetry-semantic-conventions==0.51b0", "typing-extensions>=3.7.4", ] files = [ - {file = "opentelemetry_sdk-1.27.0-py3-none-any.whl", hash = "sha256:365f5e32f920faf0fd9e14fdfd92c086e317eaa5f860edba9cdc17a380d9197d"}, - {file = "opentelemetry_sdk-1.27.0.tar.gz", hash = "sha256:d525017dea0ccce9ba4e0245100ec46ecdc043f2d7b8315d56b19aff0904fa6f"}, + {file = "opentelemetry_sdk-1.30.0-py3-none-any.whl", hash = "sha256:14fe7afc090caad881addb6926cec967129bd9260c4d33ae6a217359f6b61091"}, + {file = "opentelemetry_sdk-1.30.0.tar.gz", hash = "sha256:c9287a9e4a7614b9946e933a67168450b9ab35f08797eb9bc77d998fa480fa18"}, ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.48b0" +version = "0.51b0" requires_python = ">=3.8" summary = "OpenTelemetry Semantic Conventions" +groups = ["default"] dependencies = [ "deprecated>=1.2.6", - "opentelemetry-api==1.27.0", + "opentelemetry-api==1.30.0", ] files = [ - {file = "opentelemetry_semantic_conventions-0.48b0-py3-none-any.whl", hash = "sha256:a0de9f45c413a8669788a38569c7e0a11ce6ce97861a628cca785deecdc32a1f"}, - {file = "opentelemetry_semantic_conventions-0.48b0.tar.gz", hash = "sha256:12d74983783b6878162208be57c9effcb89dc88691c64992d70bb89dc00daa1a"}, + {file = "opentelemetry_semantic_conventions-0.51b0-py3-none-any.whl", hash = "sha256:fdc777359418e8d06c86012c3dc92c88a6453ba662e941593adb062e48c2eeae"}, + {file = "opentelemetry_semantic_conventions-0.51b0.tar.gz", hash = "sha256:3fabf47f35d1fd9aebcdca7e6802d86bd5ebc3bc3408b7e3248dde6e87a18c47"}, ] [[package]] name = "packaging" -version = "24.1" +version = "24.2" requires_python = ">=3.8" summary = "Core utilities for Python packages" +groups = ["default"] files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] name = "pandas" -version = "2.2.2" +version = "2.2.3" requires_python = ">=3.9" summary = "Powerful data structures for data analysis, time series, and statistics" +groups = ["default"] dependencies = [ + "numpy>=1.22.4; python_version < \"3.11\"", "numpy>=1.23.2; python_version == \"3.11\"", + "numpy>=1.26.0; python_version >= \"3.12\"", "python-dateutil>=2.8.2", "pytz>=2020.1", "tzdata>=2022.7", ] files = [ - {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, - {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, - {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, - {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, - {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, - {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, - {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, - {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, - {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, - {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, - {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, - {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, - {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"}, + {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"}, + {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"}, + {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"}, + {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"}, ] [[package]] name = "pillow" -version = "10.4.0" -requires_python = ">=3.8" +version = "11.1.0" +requires_python = ">=3.9" summary = "Python Imaging Library (Fork)" -files = [ - {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, - {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, - {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, - {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, - {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, - {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, - {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, - {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, - {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, - {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, - {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, - {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, - {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, - {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, - {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, - {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, - {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, - {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, - {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, - {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, - {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, - {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, -] - -[[package]] -name = "pkgconfig" -version = "1.5.5" -requires_python = ">=3.3,<4.0" -summary = "Interface Python with pkg-config" -files = [ - {file = "pkgconfig-1.5.5-py3-none-any.whl", hash = "sha256:d20023bbeb42ee6d428a0fac6e0904631f545985a10cdd71a20aa58bc47a4209"}, - {file = "pkgconfig-1.5.5.tar.gz", hash = "sha256:deb4163ef11f75b520d822d9505c1f462761b4309b1bb713d08689759ea8b899"}, +groups = ["default"] +files = [ + {file = "pillow-11.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e06695e0326d05b06833b40b7ef477e475d0b1ba3a6d27da1bb48c23209bf457"}, + {file = "pillow-11.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96f82000e12f23e4f29346e42702b6ed9a2f2fea34a740dd5ffffcc8c539eb35"}, + {file = "pillow-11.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3cd561ded2cf2bbae44d4605837221b987c216cff94f49dfeed63488bb228d2"}, + {file = "pillow-11.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f189805c8be5ca5add39e6f899e6ce2ed824e65fb45f3c28cb2841911da19070"}, + {file = "pillow-11.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dd0052e9db3474df30433f83a71b9b23bd9e4ef1de13d92df21a52c0303b8ab6"}, + {file = "pillow-11.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:837060a8599b8f5d402e97197d4924f05a2e0d68756998345c829c33186217b1"}, + {file = "pillow-11.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aa8dd43daa836b9a8128dbe7d923423e5ad86f50a7a14dc688194b7be5c0dea2"}, + {file = "pillow-11.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0a2f91f8a8b367e7a57c6e91cd25af510168091fb89ec5146003e424e1558a96"}, + {file = "pillow-11.1.0-cp311-cp311-win32.whl", hash = "sha256:c12fc111ef090845de2bb15009372175d76ac99969bdf31e2ce9b42e4b8cd88f"}, + {file = "pillow-11.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbd43429d0d7ed6533b25fc993861b8fd512c42d04514a0dd6337fb3ccf22761"}, + {file = "pillow-11.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f7955ecf5609dee9442cbface754f2c6e541d9e6eda87fad7f7a989b0bdb9d71"}, + {file = "pillow-11.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a"}, + {file = "pillow-11.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b"}, + {file = "pillow-11.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3"}, + {file = "pillow-11.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a"}, + {file = "pillow-11.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1"}, + {file = "pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f"}, + {file = "pillow-11.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91"}, + {file = "pillow-11.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c"}, + {file = "pillow-11.1.0-cp312-cp312-win32.whl", hash = "sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6"}, + {file = "pillow-11.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf"}, + {file = "pillow-11.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5"}, + {file = "pillow-11.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc"}, + {file = "pillow-11.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0"}, + {file = "pillow-11.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1"}, + {file = "pillow-11.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec"}, + {file = "pillow-11.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5"}, + {file = "pillow-11.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114"}, + {file = "pillow-11.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352"}, + {file = "pillow-11.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3"}, + {file = "pillow-11.1.0-cp313-cp313-win32.whl", hash = "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9"}, + {file = "pillow-11.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c"}, + {file = "pillow-11.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65"}, + {file = "pillow-11.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861"}, + {file = "pillow-11.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081"}, + {file = "pillow-11.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c"}, + {file = "pillow-11.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547"}, + {file = "pillow-11.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab"}, + {file = "pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9"}, + {file = "pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe"}, + {file = "pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756"}, + {file = "pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20"}, ] [[package]] name = "platformdirs" -version = "4.2.2" +version = "4.3.6" requires_python = ">=3.8" summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +groups = ["dev"] files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [[package]] name = "pre-commit" -version = "3.8.0" +version = "4.1.0" requires_python = ">=3.9" summary = "A framework for managing and maintaining multi-language pre-commit hooks." +groups = ["dev"] dependencies = [ "cfgv>=2.0.0", "identify>=1.0.0", @@ -1468,72 +1925,161 @@ dependencies = [ "virtualenv>=20.10.0", ] files = [ - {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, - {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, + {file = "pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b"}, + {file = "pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4"}, +] + +[[package]] +name = "propcache" +version = "0.3.0" +requires_python = ">=3.9" +summary = "Accelerated property cache" +groups = ["default"] +files = [ + {file = "propcache-0.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9ddd49258610499aab83b4f5b61b32e11fce873586282a0e972e5ab3bcadee51"}, + {file = "propcache-0.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2578541776769b500bada3f8a4eeaf944530516b6e90c089aa368266ed70c49e"}, + {file = "propcache-0.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8074c5dd61c8a3e915fa8fc04754fa55cfa5978200d2daa1e2d4294c1f136aa"}, + {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b58229a844931bca61b3a20efd2be2a2acb4ad1622fc026504309a6883686fbf"}, + {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e45377d5d6fefe1677da2a2c07b024a6dac782088e37c0b1efea4cfe2b1be19b"}, + {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ec5060592d83454e8063e487696ac3783cc48c9a329498bafae0d972bc7816c9"}, + {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15010f29fbed80e711db272909a074dc79858c6d28e2915704cfc487a8ac89c6"}, + {file = "propcache-0.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a254537b9b696ede293bfdbc0a65200e8e4507bc9f37831e2a0318a9b333c85c"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2b975528998de037dfbc10144b8aed9b8dd5a99ec547f14d1cb7c5665a43f075"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:19d36bb351ad5554ff20f2ae75f88ce205b0748c38b146c75628577020351e3c"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6032231d4a5abd67c7f71168fd64a47b6b451fbcb91c8397c2f7610e67683810"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6985a593417cdbc94c7f9c3403747335e450c1599da1647a5af76539672464d3"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6a1948df1bb1d56b5e7b0553c0fa04fd0e320997ae99689488201f19fa90d2e7"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8319293e85feadbbfe2150a5659dbc2ebc4afdeaf7d98936fb9a2f2ba0d4c35c"}, + {file = "propcache-0.3.0-cp311-cp311-win32.whl", hash = "sha256:63f26258a163c34542c24808f03d734b338da66ba91f410a703e505c8485791d"}, + {file = "propcache-0.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:cacea77ef7a2195f04f9279297684955e3d1ae4241092ff0cfcef532bb7a1c32"}, + {file = "propcache-0.3.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e53d19c2bf7d0d1e6998a7e693c7e87300dd971808e6618964621ccd0e01fe4e"}, + {file = "propcache-0.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a61a68d630e812b67b5bf097ab84e2cd79b48c792857dc10ba8a223f5b06a2af"}, + {file = "propcache-0.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fb91d20fa2d3b13deea98a690534697742029f4fb83673a3501ae6e3746508b5"}, + {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67054e47c01b7b349b94ed0840ccae075449503cf1fdd0a1fdd98ab5ddc2667b"}, + {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997e7b8f173a391987df40f3b52c423e5850be6f6df0dcfb5376365440b56667"}, + {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d663fd71491dde7dfdfc899d13a067a94198e90695b4321084c6e450743b8c7"}, + {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8884ba1a0fe7210b775106b25850f5e5a9dc3c840d1ae9924ee6ea2eb3acbfe7"}, + {file = "propcache-0.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa806bbc13eac1ab6291ed21ecd2dd426063ca5417dd507e6be58de20e58dfcf"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6f4d7a7c0aff92e8354cceca6fe223973ddf08401047920df0fcb24be2bd5138"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9be90eebc9842a93ef8335291f57b3b7488ac24f70df96a6034a13cb58e6ff86"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bf15fc0b45914d9d1b706f7c9c4f66f2b7b053e9517e40123e137e8ca8958b3d"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5a16167118677d94bb48bfcd91e420088854eb0737b76ec374b91498fb77a70e"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:41de3da5458edd5678b0f6ff66691507f9885f5fe6a0fb99a5d10d10c0fd2d64"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:728af36011bb5d344c4fe4af79cfe186729efb649d2f8b395d1572fb088a996c"}, + {file = "propcache-0.3.0-cp312-cp312-win32.whl", hash = "sha256:6b5b7fd6ee7b54e01759f2044f936dcf7dea6e7585f35490f7ca0420fe723c0d"}, + {file = "propcache-0.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:2d15bc27163cd4df433e75f546b9ac31c1ba7b0b128bfb1b90df19082466ff57"}, + {file = "propcache-0.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a2b9bf8c79b660d0ca1ad95e587818c30ccdb11f787657458d6f26a1ea18c568"}, + {file = "propcache-0.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b0c1a133d42c6fc1f5fbcf5c91331657a1ff822e87989bf4a6e2e39b818d0ee9"}, + {file = "propcache-0.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bb2f144c6d98bb5cbc94adeb0447cfd4c0f991341baa68eee3f3b0c9c0e83767"}, + {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1323cd04d6e92150bcc79d0174ce347ed4b349d748b9358fd2e497b121e03c8"}, + {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b812b3cb6caacd072276ac0492d249f210006c57726b6484a1e1805b3cfeea0"}, + {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:742840d1d0438eb7ea4280f3347598f507a199a35a08294afdcc560c3739989d"}, + {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c6e7e4f9167fddc438cd653d826f2222222564daed4116a02a184b464d3ef05"}, + {file = "propcache-0.3.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a94ffc66738da99232ddffcf7910e0f69e2bbe3a0802e54426dbf0714e1c2ffe"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c6ec957025bf32b15cbc6b67afe233c65b30005e4c55fe5768e4bb518d712f1"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:549722908de62aa0b47a78b90531c022fa6e139f9166be634f667ff45632cc92"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5d62c4f6706bff5d8a52fd51fec6069bef69e7202ed481486c0bc3874912c787"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:24c04f8fbf60094c531667b8207acbae54146661657a1b1be6d3ca7773b7a545"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7c5f5290799a3f6539cc5e6f474c3e5c5fbeba74a5e1e5be75587746a940d51e"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4fa0e7c9c3cf7c276d4f6ab9af8adddc127d04e0fcabede315904d2ff76db626"}, + {file = "propcache-0.3.0-cp313-cp313-win32.whl", hash = "sha256:ee0bd3a7b2e184e88d25c9baa6a9dc609ba25b76daae942edfb14499ac7ec374"}, + {file = "propcache-0.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1c8f7d896a16da9455f882870a507567d4f58c53504dc2d4b1e1d386dfe4588a"}, + {file = "propcache-0.3.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e560fd75aaf3e5693b91bcaddd8b314f4d57e99aef8a6c6dc692f935cc1e6bbf"}, + {file = "propcache-0.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:65a37714b8ad9aba5780325228598a5b16c47ba0f8aeb3dc0514701e4413d7c0"}, + {file = "propcache-0.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:07700939b2cbd67bfb3b76a12e1412405d71019df00ca5697ce75e5ef789d829"}, + {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c0fdbdf6983526e269e5a8d53b7ae3622dd6998468821d660d0daf72779aefa"}, + {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:794c3dd744fad478b6232289c866c25406ecdfc47e294618bdf1697e69bd64a6"}, + {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4544699674faf66fb6b4473a1518ae4999c1b614f0b8297b1cef96bac25381db"}, + {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fddb8870bdb83456a489ab67c6b3040a8d5a55069aa6f72f9d872235fbc52f54"}, + {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f857034dc68d5ceb30fb60afb6ff2103087aea10a01b613985610e007053a121"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:02df07041e0820cacc8f739510078f2aadcfd3fc57eaeeb16d5ded85c872c89e"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f47d52fd9b2ac418c4890aad2f6d21a6b96183c98021f0a48497a904199f006e"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9ff4e9ecb6e4b363430edf2c6e50173a63e0820e549918adef70515f87ced19a"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ecc2920630283e0783c22e2ac94427f8cca29a04cfdf331467d4f661f4072dac"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:c441c841e82c5ba7a85ad25986014be8d7849c3cfbdb6004541873505929a74e"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6c929916cbdb540d3407c66f19f73387f43e7c12fa318a66f64ac99da601bcdf"}, + {file = "propcache-0.3.0-cp313-cp313t-win32.whl", hash = "sha256:0c3e893c4464ebd751b44ae76c12c5f5c1e4f6cbd6fbf67e3783cd93ad221863"}, + {file = "propcache-0.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:75e872573220d1ee2305b35c9813626e620768248425f58798413e9c39741f46"}, + {file = "propcache-0.3.0-py3-none-any.whl", hash = "sha256:67dda3c7325691c2081510e92c561f465ba61b975f481735aefdfc845d2cd043"}, + {file = "propcache-0.3.0.tar.gz", hash = "sha256:a8fd93de4e1d278046345f49e2238cdb298589325849b2645d4a94c53faeffc5"}, ] [[package]] name = "protobuf" -version = "5.28.0" +version = "5.29.3" requires_python = ">=3.8" summary = "" +groups = ["default"] files = [ - {file = "protobuf-5.28.0-cp310-abi3-win32.whl", hash = "sha256:66c3edeedb774a3508ae70d87b3a19786445fe9a068dd3585e0cefa8a77b83d0"}, - {file = "protobuf-5.28.0-cp310-abi3-win_amd64.whl", hash = "sha256:6d7cc9e60f976cf3e873acb9a40fed04afb5d224608ed5c1a105db4a3f09c5b6"}, - {file = "protobuf-5.28.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:532627e8fdd825cf8767a2d2b94d77e874d5ddb0adefb04b237f7cc296748681"}, - {file = "protobuf-5.28.0-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:018db9056b9d75eb93d12a9d35120f97a84d9a919bcab11ed56ad2d399d6e8dd"}, - {file = "protobuf-5.28.0-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:6206afcb2d90181ae8722798dcb56dc76675ab67458ac24c0dd7d75d632ac9bd"}, - {file = "protobuf-5.28.0-py3-none-any.whl", hash = "sha256:510ed78cd0980f6d3218099e874714cdf0d8a95582e7b059b06cabad855ed0a0"}, - {file = "protobuf-5.28.0.tar.gz", hash = "sha256:dde74af0fa774fa98892209992295adbfb91da3fa98c8f67a88afe8f5a349add"}, + {file = "protobuf-5.29.3-cp310-abi3-win32.whl", hash = "sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888"}, + {file = "protobuf-5.29.3-cp310-abi3-win_amd64.whl", hash = "sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a"}, + {file = "protobuf-5.29.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e"}, + {file = "protobuf-5.29.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84"}, + {file = "protobuf-5.29.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f"}, + {file = "protobuf-5.29.3-py3-none-any.whl", hash = "sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f"}, + {file = "protobuf-5.29.3.tar.gz", hash = "sha256:5da0f41edaf117bde316404bad1a486cb4ededf8e4a54891296f648e8e076620"}, ] [[package]] name = "pyarrow" -version = "17.0.0" -requires_python = ">=3.8" +version = "19.0.1" +requires_python = ">=3.9" summary = "Python library for Apache Arrow" -dependencies = [ - "numpy>=1.16.6", -] -files = [ - {file = "pyarrow-17.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:1c8856e2ef09eb87ecf937104aacfa0708f22dfeb039c363ec99735190ffb977"}, - {file = "pyarrow-17.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e19f569567efcbbd42084e87f948778eb371d308e137a0f97afe19bb860ccb3"}, - {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b244dc8e08a23b3e352899a006a26ae7b4d0da7bb636872fa8f5884e70acf15"}, - {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b72e87fe3e1db343995562f7fff8aee354b55ee83d13afba65400c178ab2597"}, - {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dc5c31c37409dfbc5d014047817cb4ccd8c1ea25d19576acf1a001fe07f5b420"}, - {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e3343cb1e88bc2ea605986d4b94948716edc7a8d14afd4e2c097232f729758b4"}, - {file = "pyarrow-17.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:a27532c38f3de9eb3e90ecab63dfda948a8ca859a66e3a47f5f42d1e403c4d03"}, - {file = "pyarrow-17.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:9b8a823cea605221e61f34859dcc03207e52e409ccf6354634143e23af7c8d22"}, - {file = "pyarrow-17.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1e70de6cb5790a50b01d2b686d54aaf73da01266850b05e3af2a1bc89e16053"}, - {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0071ce35788c6f9077ff9ecba4858108eebe2ea5a3f7cf2cf55ebc1dbc6ee24a"}, - {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:757074882f844411fcca735e39aae74248a1531367a7c80799b4266390ae51cc"}, - {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9ba11c4f16976e89146781a83833df7f82077cdab7dc6232c897789343f7891a"}, - {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b0c6ac301093b42d34410b187bba560b17c0330f64907bfa4f7f7f2444b0cf9b"}, - {file = "pyarrow-17.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7"}, - {file = "pyarrow-17.0.0.tar.gz", hash = "sha256:4beca9521ed2c0921c1023e68d097d0299b62c362639ea315572a58f3f50fd28"}, +groups = ["default"] +files = [ + {file = "pyarrow-19.0.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:cc55d71898ea30dc95900297d191377caba257612f384207fe9f8293b5850f90"}, + {file = "pyarrow-19.0.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:7a544ec12de66769612b2d6988c36adc96fb9767ecc8ee0a4d270b10b1c51e00"}, + {file = "pyarrow-19.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0148bb4fc158bfbc3d6dfe5001d93ebeed253793fff4435167f6ce1dc4bddeae"}, + {file = "pyarrow-19.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f24faab6ed18f216a37870d8c5623f9c044566d75ec586ef884e13a02a9d62c5"}, + {file = "pyarrow-19.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:4982f8e2b7afd6dae8608d70ba5bd91699077323f812a0448d8b7abdff6cb5d3"}, + {file = "pyarrow-19.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:49a3aecb62c1be1d822f8bf629226d4a96418228a42f5b40835c1f10d42e4db6"}, + {file = "pyarrow-19.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:008a4009efdb4ea3d2e18f05cd31f9d43c388aad29c636112c2966605ba33466"}, + {file = "pyarrow-19.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:80b2ad2b193e7d19e81008a96e313fbd53157945c7be9ac65f44f8937a55427b"}, + {file = "pyarrow-19.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:ee8dec072569f43835932a3b10c55973593abc00936c202707a4ad06af7cb294"}, + {file = "pyarrow-19.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d5d1ec7ec5324b98887bdc006f4d2ce534e10e60f7ad995e7875ffa0ff9cb14"}, + {file = "pyarrow-19.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ad4c0eb4e2a9aeb990af6c09e6fa0b195c8c0e7b272ecc8d4d2b6574809d34"}, + {file = "pyarrow-19.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d383591f3dcbe545f6cc62daaef9c7cdfe0dff0fb9e1c8121101cabe9098cfa6"}, + {file = "pyarrow-19.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b4c4156a625f1e35d6c0b2132635a237708944eb41df5fbe7d50f20d20c17832"}, + {file = "pyarrow-19.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:5bd1618ae5e5476b7654c7b55a6364ae87686d4724538c24185bbb2952679960"}, + {file = "pyarrow-19.0.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e45274b20e524ae5c39d7fc1ca2aa923aab494776d2d4b316b49ec7572ca324c"}, + {file = "pyarrow-19.0.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:d9dedeaf19097a143ed6da37f04f4051aba353c95ef507764d344229b2b740ae"}, + {file = "pyarrow-19.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ebfb5171bb5f4a52319344ebbbecc731af3f021e49318c74f33d520d31ae0c4"}, + {file = "pyarrow-19.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a21d39fbdb948857f67eacb5bbaaf36802de044ec36fbef7a1c8f0dd3a4ab2"}, + {file = "pyarrow-19.0.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:99bc1bec6d234359743b01e70d4310d0ab240c3d6b0da7e2a93663b0158616f6"}, + {file = "pyarrow-19.0.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1b93ef2c93e77c442c979b0d596af45e4665d8b96da598db145b0fec014b9136"}, + {file = "pyarrow-19.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:d9d46e06846a41ba906ab25302cf0fd522f81aa2a85a71021826f34639ad31ef"}, + {file = "pyarrow-19.0.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:c0fe3dbbf054a00d1f162fda94ce236a899ca01123a798c561ba307ca38af5f0"}, + {file = "pyarrow-19.0.1-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:96606c3ba57944d128e8a8399da4812f56c7f61de8c647e3470b417f795d0ef9"}, + {file = "pyarrow-19.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f04d49a6b64cf24719c080b3c2029a3a5b16417fd5fd7c4041f94233af732f3"}, + {file = "pyarrow-19.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a9137cf7e1640dce4c190551ee69d478f7121b5c6f323553b319cac936395f6"}, + {file = "pyarrow-19.0.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:7c1bca1897c28013db5e4c83944a2ab53231f541b9e0c3f4791206d0c0de389a"}, + {file = "pyarrow-19.0.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:58d9397b2e273ef76264b45531e9d552d8ec8a6688b7390b5be44c02a37aade8"}, + {file = "pyarrow-19.0.1.tar.gz", hash = "sha256:3bf266b485df66a400f282ac0b6d1b500b9d2ae73314a153dbe97d6d5cc8a99e"}, ] [[package]] name = "pyasn1" -version = "0.6.0" +version = "0.6.1" requires_python = ">=3.8" summary = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +groups = ["default"] files = [ - {file = "pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473"}, - {file = "pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c"}, + {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, + {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, ] [[package]] name = "pyasn1-modules" -version = "0.4.0" +version = "0.4.1" requires_python = ">=3.8" summary = "A collection of ASN.1-based protocols modules" +groups = ["default"] dependencies = [ "pyasn1<0.7.0,>=0.4.6", ] files = [ - {file = "pyasn1_modules-0.4.0-py3-none-any.whl", hash = "sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b"}, - {file = "pyasn1_modules-0.4.0.tar.gz", hash = "sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6"}, + {file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"}, + {file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"}, ] [[package]] @@ -1541,6 +2087,7 @@ name = "pycparser" version = "2.22" requires_python = ">=3.8" summary = "C parser in Python" +groups = ["default"] files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -1551,6 +2098,7 @@ name = "pydantic" version = "2.10.6" requires_python = ">=3.8" summary = "Data validation using Python type hints" +groups = ["default"] dependencies = [ "annotated-types>=0.6.0", "pydantic-core==2.27.2", @@ -1566,22 +2114,65 @@ name = "pydantic-core" version = "2.27.2" requires_python = ">=3.8" summary = "Core functionality for Pydantic validation and serialization" +groups = ["default"] dependencies = [ "typing-extensions!=4.7.0,>=4.6.0", ] files = [ + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, ] [[package]] name = "pyparsing" -version = "3.1.4" -requires_python = ">=3.6.8" +version = "3.2.1" +requires_python = ">=3.9" summary = "pyparsing module - Classes and methods to define and execute parsing grammars" +groups = ["default"] files = [ - {file = "pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c"}, - {file = "pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032"}, + {file = "pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1"}, + {file = "pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a"}, ] [[package]] @@ -1589,6 +2180,7 @@ name = "python-dateutil" version = "2.9.0.post0" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" summary = "Extensions to the standard Python datetime module" +groups = ["default"] dependencies = [ "six>=1.5", ] @@ -1599,9 +2191,10 @@ files = [ [[package]] name = "pytorch-lightning" -version = "2.4.0" -requires_python = ">=3.8" +version = "2.5.0.post0" +requires_python = ">=3.9" summary = "PyTorch Lightning is the lightweight PyTorch wrapper for ML researchers. Scale your models. Write less boilerplate." +groups = ["default"] dependencies = [ "PyYAML>=5.4", "fsspec[http]>=2022.5.0", @@ -1613,36 +2206,56 @@ dependencies = [ "typing-extensions>=4.4.0", ] files = [ - {file = "pytorch-lightning-2.4.0.tar.gz", hash = "sha256:6aa897fd9d6dfa7b7b49f37c2f04e13592861831d08deae584dfda423fdb71c8"}, - {file = "pytorch_lightning-2.4.0-py3-none-any.whl", hash = "sha256:9ac7935229ac022ef06994c928217ed37f525ac6700f7d4fc57009624570e655"}, + {file = "pytorch_lightning-2.5.0.post0-py3-none-any.whl", hash = "sha256:c86bf4fded58b386f312f75337696a9b2d57077b858b3b9524400a03a0179b3a"}, + {file = "pytorch_lightning-2.5.0.post0.tar.gz", hash = "sha256:347235bf8573b4ebcf507a0dd755fcb9ce58c420c77220a9756a6edca0418532"}, ] [[package]] name = "pytz" -version = "2024.1" +version = "2025.1" summary = "World timezone definitions, modern and historical" +groups = ["default"] files = [ - {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, - {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, + {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"}, + {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, ] [[package]] name = "pyvips" version = "2.2.3" -summary = "binding for the libvips image processing library, API mode" +summary = "binding for the libvips image processing library, ABI mode" +groups = ["default"] dependencies = [ "cffi>=1.0.0", - "pkgconfig", ] files = [ {file = "pyvips-2.2.3.tar.gz", hash = "sha256:43bceced0db492654c93008246a58a508e0373ae1621116b87b322f2ac72212f"}, ] +[[package]] +name = "pywin32" +version = "308" +summary = "Python for Window Extensions" +groups = ["default"] +marker = "sys_platform == \"win32\"" +files = [ + {file = "pywin32-308-cp311-cp311-win32.whl", hash = "sha256:5d8c8015b24a7d6855b1550d8e660d8daa09983c80e5daf89a273e5c6fb5095a"}, + {file = "pywin32-308-cp311-cp311-win_amd64.whl", hash = "sha256:575621b90f0dc2695fec346b2d6302faebd4f0f45c05ea29404cefe35d89442b"}, + {file = "pywin32-308-cp311-cp311-win_arm64.whl", hash = "sha256:100a5442b7332070983c4cd03f2e906a5648a5104b8a7f50175f7906efd16bb6"}, + {file = "pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897"}, + {file = "pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47"}, + {file = "pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091"}, + {file = "pywin32-308-cp313-cp313-win32.whl", hash = "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed"}, + {file = "pywin32-308-cp313-cp313-win_amd64.whl", hash = "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4"}, + {file = "pywin32-308-cp313-cp313-win_arm64.whl", hash = "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd"}, +] + [[package]] name = "pyyaml" version = "6.0.2" requires_python = ">=3.8" summary = "YAML parser and emitter for Python" +groups = ["default", "dev"] files = [ {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, @@ -1681,6 +2294,7 @@ requires_python = ">=3.11" git = "ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/kube-jobs.git" revision = "7ce565cc35bfd1abe30db2e1e4e606e65dda8c84" summary = "A simple CLI tool for managing Kubernetes jobs." +groups = ["default"] dependencies = [ "click>=8.1.7", "kubernetes>=31.0.0", @@ -1688,11 +2302,12 @@ dependencies = [ [[package]] name = "rationai-masks" -version = "1.0.0" +version = "1.1.0" requires_python = ">=3.11" git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git" -revision = "7d56f2ba3c7b411bdf6043ae1224676c745317d2" +revision = "91b2a2a2e288417e9bdd0fd83ddd4f04af449dec" summary = "" +groups = ["default"] dependencies = [ "numpy>=1.9.0", "openslide-python>=1.3.1", @@ -1700,47 +2315,53 @@ dependencies = [ "pillow>=10.4.0", "pyvips>=2.2.3", "ray>=2.34.0", + "torch>=2.5.1", "tqdm>=4.66.5", ] [[package]] name = "rationai-mlkit" -version = "0.1.15" +version = "0.2.0" requires_python = ">=3.11" git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/mlkit.git" -revision = "19712cca6931f11d25292cd35c5678b53592b5e7" +revision = "233ead782353b3f23eee75702576f5c004cf300e" summary = "" +groups = ["default"] dependencies = [ "GitPython>=3.1.43", "hydra-core>=1.3.2", "lightning>=2.2.4", "mlflow>=2.12.1", "openslide-python>=1.3.1", + "rationai-masks @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git", "torch>=2.2.0", ] [[package]] name = "rationai-tiling" -version = "1.0.0" +version = "1.1.0" requires_python = ">=3.11" git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git" -revision = "5889fc70093e097684f6014dab8ad5d0616e14fd" +revision = "9096ada81542c172908dff5b2766cf729786471b" summary = "" +groups = ["default"] dependencies = [ "mlflow>=2.15.1", "numpy>=1.9.0", "openslide-python>=1.3.1", "pandas>=2.2.2", "pyvips>=2.2.3", + "rationai-masks @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git", "ray>=2.34.0", "tqdm>=4.66.5", ] [[package]] name = "ray" -version = "2.38.0" +version = "2.43.0" requires_python = ">=3.9" summary = "Ray provides a simple, universal API for building distributed applications." +groups = ["default"] dependencies = [ "aiosignal", "click>=7.0", @@ -1754,21 +2375,32 @@ dependencies = [ "requests", ] files = [ - {file = "ray-2.38.0-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:ece802cf3a1c102b53f63b8bc90d947971c4b387deaf233c224ed8ef34a1f3cb"}, + {file = "ray-2.43.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:fde8a81280f07af983bc3769c9941db5db273ce10e92abb3348e41bed023d735"}, + {file = "ray-2.43.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e93c32ad0cb67f1f7da76fac409d87d5cd5ea3eb03b836830e9ef5cc810bc2c0"}, + {file = "ray-2.43.0-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:476ec3e1fa2464ddd5f049c0f2758ff9dfecc21fb8df4266f1df01b2780c6653"}, + {file = "ray-2.43.0-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:78c3bdbf182b4d019fa9a8aabd55c39bf705bb630aea064f768f305fc472d1eb"}, + {file = "ray-2.43.0-cp311-cp311-win_amd64.whl", hash = "sha256:e2b0fa0272ade67bad2e83d7de996795bfb4f10f4b895476b95fbfda6d3c3ed6"}, + {file = "ray-2.43.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:5121fdf4bcbcb0fda3b9b71164dd6c8fcc79a2e258022a2a3957e401018913fb"}, + {file = "ray-2.43.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f26f7b72da04c3c4422269c31b067abd15cb38424b7012d812ddfb2c77462ea"}, + {file = "ray-2.43.0-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:73770d4c8a989730985ff2b4292129249e28c1e29e84589470c9ba1ae91ca832"}, + {file = "ray-2.43.0-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:b45f478d29ce5df3fc19861df64fef9ed5c25f1e83fa10028d33fadefdeca095"}, + {file = "ray-2.43.0-cp312-cp312-win_amd64.whl", hash = "sha256:1c3a9880112a8d561280a34e8ef9471070f81ca467e08b669e5e77a85e173c9c"}, ] [[package]] name = "referencing" -version = "0.35.1" -requires_python = ">=3.8" +version = "0.36.2" +requires_python = ">=3.9" summary = "JSON Referencing + Python" +groups = ["default"] dependencies = [ "attrs>=22.2.0", "rpds-py>=0.7.0", + "typing-extensions>=4.4.0; python_version < \"3.13\"", ] files = [ - {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, - {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, + {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, + {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, ] [[package]] @@ -1776,6 +2408,7 @@ name = "requests" version = "2.32.3" requires_python = ">=3.8" summary = "Python HTTP for Humans." +groups = ["default"] dependencies = [ "certifi>=2017.4.17", "charset-normalizer<4,>=2", @@ -1792,6 +2425,7 @@ name = "requests-oauthlib" version = "2.0.0" requires_python = ">=3.4" summary = "OAuthlib authentication support for Requests." +groups = ["default"] dependencies = [ "oauthlib>=3.0.0", "requests>=2.0.0", @@ -1803,12 +2437,64 @@ files = [ [[package]] name = "rpds-py" -version = "0.21.0" +version = "0.23.1" requires_python = ">=3.9" summary = "Python bindings to Rust's persistent data structures (rpds)" -files = [ - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de609a6f1b682f70bb7163da745ee815d8f230d97276db049ab447767466a09d"}, - {file = "rpds_py-0.21.0.tar.gz", hash = "sha256:ed6378c9d66d0de903763e7706383d60c33829581f0adff47b6535f1802fa6db"}, +groups = ["default"] +files = [ + {file = "rpds_py-0.23.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b79f5ced71efd70414a9a80bbbfaa7160da307723166f09b69773153bf17c590"}, + {file = "rpds_py-0.23.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c9e799dac1ffbe7b10c1fd42fe4cd51371a549c6e108249bde9cd1200e8f59b4"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721f9c4011b443b6e84505fc00cc7aadc9d1743f1c988e4c89353e19c4a968ee"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f88626e3f5e57432e6191cd0c5d6d6b319b635e70b40be2ffba713053e5147dd"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:285019078537949cecd0190f3690a0b0125ff743d6a53dfeb7a4e6787af154f5"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b92f5654157de1379c509b15acec9d12ecf6e3bc1996571b6cb82a4302060447"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e768267cbe051dd8d1c5305ba690bb153204a09bf2e3de3ae530de955f5b5580"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c5334a71f7dc1160382d45997e29f2637c02f8a26af41073189d79b95d3321f1"}, + {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6adb81564af0cd428910f83fa7da46ce9ad47c56c0b22b50872bc4515d91966"}, + {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:cafa48f2133d4daa028473ede7d81cd1b9f9e6925e9e4003ebdf77010ee02f35"}, + {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fced9fd4a07a1ded1bac7e961ddd9753dd5d8b755ba8e05acba54a21f5f1522"}, + {file = "rpds_py-0.23.1-cp311-cp311-win32.whl", hash = "sha256:243241c95174b5fb7204c04595852fe3943cc41f47aa14c3828bc18cd9d3b2d6"}, + {file = "rpds_py-0.23.1-cp311-cp311-win_amd64.whl", hash = "sha256:11dd60b2ffddba85715d8a66bb39b95ddbe389ad2cfcf42c833f1bcde0878eaf"}, + {file = "rpds_py-0.23.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3902df19540e9af4cc0c3ae75974c65d2c156b9257e91f5101a51f99136d834c"}, + {file = "rpds_py-0.23.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66f8d2a17e5838dd6fb9be6baaba8e75ae2f5fa6b6b755d597184bfcd3cb0eba"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:112b8774b0b4ee22368fec42749b94366bd9b536f8f74c3d4175d4395f5cbd31"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0df046f2266e8586cf09d00588302a32923eb6386ced0ca5c9deade6af9a149"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3288930b947cbebe767f84cf618d2cbe0b13be476e749da0e6a009f986248c"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce473a2351c018b06dd8d30d5da8ab5a0831056cc53b2006e2a8028172c37ce5"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d550d7e9e7d8676b183b37d65b5cd8de13676a738973d330b59dc8312df9c5dc"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e14f86b871ea74c3fddc9a40e947d6a5d09def5adc2076ee61fb910a9014fb35"}, + {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf5be5ba34e19be579ae873da515a2836a2166d8d7ee43be6ff909eda42b72b"}, + {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7031d493c4465dbc8d40bd6cafefef4bd472b17db0ab94c53e7909ee781b9ef"}, + {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55ff4151cfd4bc635e51cfb1c59ac9f7196b256b12e3a57deb9e5742e65941ad"}, + {file = "rpds_py-0.23.1-cp312-cp312-win32.whl", hash = "sha256:a9d3b728f5a5873d84cba997b9d617c6090ca5721caaa691f3b1a78c60adc057"}, + {file = "rpds_py-0.23.1-cp312-cp312-win_amd64.whl", hash = "sha256:b03a8d50b137ee758e4c73638b10747b7c39988eb8e6cd11abb7084266455165"}, + {file = "rpds_py-0.23.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:4caafd1a22e5eaa3732acb7672a497123354bef79a9d7ceed43387d25025e935"}, + {file = "rpds_py-0.23.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:178f8a60fc24511c0eb756af741c476b87b610dba83270fce1e5a430204566a4"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c632419c3870507ca20a37c8f8f5352317aca097639e524ad129f58c125c61c6"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:698a79d295626ee292d1730bc2ef6e70a3ab135b1d79ada8fde3ed0047b65a10"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:271fa2184cf28bdded86bb6217c8e08d3a169fe0bbe9be5e8d96e8476b707122"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b91cceb5add79ee563bd1f70b30896bd63bc5f78a11c1f00a1e931729ca4f1f4"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a6cb95074777f1ecda2ca4fa7717caa9ee6e534f42b7575a8f0d4cb0c24013"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:50fb62f8d8364978478b12d5f03bf028c6bc2af04082479299139dc26edf4c64"}, + {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c8f7e90b948dc9dcfff8003f1ea3af08b29c062f681c05fd798e36daa3f7e3e8"}, + {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5b98b6c953e5c2bda51ab4d5b4f172617d462eebc7f4bfdc7c7e6b423f6da957"}, + {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2893d778d4671ee627bac4037a075168b2673c57186fb1a57e993465dbd79a93"}, + {file = "rpds_py-0.23.1-cp313-cp313-win32.whl", hash = "sha256:2cfa07c346a7ad07019c33fb9a63cf3acb1f5363c33bc73014e20d9fe8b01cdd"}, + {file = "rpds_py-0.23.1-cp313-cp313-win_amd64.whl", hash = "sha256:3aaf141d39f45322e44fc2c742e4b8b4098ead5317e5f884770c8df0c332da70"}, + {file = "rpds_py-0.23.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:759462b2d0aa5a04be5b3e37fb8183615f47014ae6b116e17036b131985cb731"}, + {file = "rpds_py-0.23.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3e9212f52074fc9d72cf242a84063787ab8e21e0950d4d6709886fb62bcb91d5"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e9f3a3ac919406bc0414bbbd76c6af99253c507150191ea79fab42fdb35982a"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c04ca91dda8a61584165825907f5c967ca09e9c65fe8966ee753a3f2b019fe1e"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ab923167cfd945abb9b51a407407cf19f5bee35001221f2911dc85ffd35ff4f"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed6f011bedca8585787e5082cce081bac3d30f54520097b2411351b3574e1219"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6959bb9928c5c999aba4a3f5a6799d571ddc2c59ff49917ecf55be2bbb4e3722"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ed7de3c86721b4e83ac440751329ec6a1102229aa18163f84c75b06b525ad7e"}, + {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5fb89edee2fa237584e532fbf78f0ddd1e49a47c7c8cfa153ab4849dc72a35e6"}, + {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7e5413d2e2d86025e73f05510ad23dad5950ab8417b7fc6beaad99be8077138b"}, + {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d31ed4987d72aabdf521eddfb6a72988703c091cfc0064330b9e5f8d6a042ff5"}, + {file = "rpds_py-0.23.1-cp313-cp313t-win32.whl", hash = "sha256:f3429fb8e15b20961efca8c8b21432623d85db2228cc73fe22756c6637aa39e7"}, + {file = "rpds_py-0.23.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d6f6512a90bd5cd9030a6237f5346f046c6f0e40af98657568fa45695d4de59d"}, + {file = "rpds_py-0.23.1.tar.gz", hash = "sha256:7f3240dcfa14d198dba24b8b9cb3b108c06b68d45b7babd9eefc1038fdf7e707"}, ] [[package]] @@ -1816,6 +2502,7 @@ name = "rsa" version = "4.9" requires_python = ">=3.6,<4" summary = "Pure-Python RSA implementation" +groups = ["default"] dependencies = [ "pyasn1>=0.1.3", ] @@ -1826,28 +2513,65 @@ files = [ [[package]] name = "ruff" -version = "0.6.0" +version = "0.9.9" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." -files = [ - {file = "ruff-0.6.0-py3-none-linux_armv6l.whl", hash = "sha256:92dcce923e5df265781e5fc76f9a1edad52201a7aafe56e586b90988d5239013"}, - {file = "ruff-0.6.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:31b90ff9dc79ed476c04e957ba7e2b95c3fceb76148f2079d0d68a908d2cfae7"}, - {file = "ruff-0.6.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6d834a9ec9f8287dd6c3297058b3a265ed6b59233db22593379ee38ebc4b9768"}, - {file = "ruff-0.6.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2089267692696aba342179471831a085043f218706e642564812145df8b8d0d"}, - {file = "ruff-0.6.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aa62b423ee4bbd8765f2c1dbe8f6aac203e0583993a91453dc0a449d465c84da"}, - {file = "ruff-0.6.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7344e1a964b16b1137ea361d6516ce4ee61a0403fa94252a1913ecc1311adcae"}, - {file = "ruff-0.6.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:487f3a35c3f33bf82be212ce15dc6278ea854e35573a3f809442f73bec8b2760"}, - {file = "ruff-0.6.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75db409984077a793cf344d499165298a6f65449e905747ac65983b12e3e64b1"}, - {file = "ruff-0.6.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84908bd603533ecf1db456d8fc2665d1f4335d722e84bc871d3bbd2d1116c272"}, - {file = "ruff-0.6.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f1749a0aef3ec41ed91a0e2127a6ae97d2e2853af16dbd4f3c00d7a3af726c5"}, - {file = "ruff-0.6.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:016fea751e2bcfbbd2f8cb19b97b37b3fd33148e4df45b526e87096f4e17354f"}, - {file = "ruff-0.6.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6ae80f141b53b2e36e230017e64f5ea2def18fac14334ffceaae1b780d70c4f7"}, - {file = "ruff-0.6.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:eaaaf33ea4b3f63fd264d6a6f4a73fa224bbfda4b438ffea59a5340f4afa2bb5"}, - {file = "ruff-0.6.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7667ddd1fc688150a7ca4137140867584c63309695a30016880caf20831503a0"}, - {file = "ruff-0.6.0-py3-none-win32.whl", hash = "sha256:ae48365aae60d40865a412356f8c6f2c0be1c928591168111eaf07eaefa6bea3"}, - {file = "ruff-0.6.0-py3-none-win_amd64.whl", hash = "sha256:774032b507c96f0c803c8237ce7d2ef3934df208a09c40fa809c2931f957fe5e"}, - {file = "ruff-0.6.0-py3-none-win_arm64.whl", hash = "sha256:a5366e8c3ae6b2dc32821749b532606c42e609a99b0ae1472cf601da931a048c"}, - {file = "ruff-0.6.0.tar.gz", hash = "sha256:272a81830f68f9bd19d49eaf7fa01a5545c5a2e86f32a9935bb0e4bb9a1db5b8"}, +groups = ["dev"] +files = [ + {file = "ruff-0.9.9-py3-none-linux_armv6l.whl", hash = "sha256:628abb5ea10345e53dff55b167595a159d3e174d6720bf19761f5e467e68d367"}, + {file = "ruff-0.9.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6cd1428e834b35d7493354723543b28cc11dc14d1ce19b685f6e68e07c05ec7"}, + {file = "ruff-0.9.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5ee162652869120ad260670706f3cd36cd3f32b0c651f02b6da142652c54941d"}, + {file = "ruff-0.9.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aa0f6b75082c9be1ec5a1db78c6d4b02e2375c3068438241dc19c7c306cc61a"}, + {file = "ruff-0.9.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:584cc66e89fb5f80f84b05133dd677a17cdd86901d6479712c96597a3f28e7fe"}, + {file = "ruff-0.9.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf3369325761a35aba75cd5c55ba1b5eb17d772f12ab168fbfac54be85cf18c"}, + {file = "ruff-0.9.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3403a53a32a90ce929aa2f758542aca9234befa133e29f4933dcef28a24317be"}, + {file = "ruff-0.9.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18454e7fa4e4d72cffe28a37cf6a73cb2594f81ec9f4eca31a0aaa9ccdfb1590"}, + {file = "ruff-0.9.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fadfe2c88724c9617339f62319ed40dcdadadf2888d5afb88bf3adee7b35bfb"}, + {file = "ruff-0.9.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6df104d08c442a1aabcfd254279b8cc1e2cbf41a605aa3e26610ba1ec4acf0b0"}, + {file = "ruff-0.9.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d7c62939daf5b2a15af48abbd23bea1efdd38c312d6e7c4cedf5a24e03207e17"}, + {file = "ruff-0.9.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9494ba82a37a4b81b6a798076e4a3251c13243fc37967e998efe4cce58c8a8d1"}, + {file = "ruff-0.9.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:4efd7a96ed6d36ef011ae798bf794c5501a514be369296c672dab7921087fa57"}, + {file = "ruff-0.9.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ab90a7944c5a1296f3ecb08d1cbf8c2da34c7e68114b1271a431a3ad30cb660e"}, + {file = "ruff-0.9.9-py3-none-win32.whl", hash = "sha256:6b4c376d929c25ecd6d87e182a230fa4377b8e5125a4ff52d506ee8c087153c1"}, + {file = "ruff-0.9.9-py3-none-win_amd64.whl", hash = "sha256:837982ea24091d4c1700ddb2f63b7070e5baec508e43b01de013dc7eff974ff1"}, + {file = "ruff-0.9.9-py3-none-win_arm64.whl", hash = "sha256:3ac78f127517209fe6d96ab00f3ba97cafe38718b23b1db3e96d8b2d39e37ddf"}, + {file = "ruff-0.9.9.tar.gz", hash = "sha256:0062ed13f22173e85f8f7056f9a24016e692efeea8704d1a5e8011b8aa850933"}, +] + +[[package]] +name = "scikit-image" +version = "0.25.2" +requires_python = ">=3.10" +summary = "Image processing in Python" +groups = ["default"] +dependencies = [ + "imageio!=2.35.0,>=2.33", + "lazy-loader>=0.4", + "networkx>=3.0", + "numpy>=1.24", + "packaging>=21", + "pillow>=10.1", + "scipy>=1.11.4", + "tifffile>=2022.8.12", +] +files = [ + {file = "scikit_image-0.25.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f4bac9196fb80d37567316581c6060763b0f4893d3aca34a9ede3825bc035b17"}, + {file = "scikit_image-0.25.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d989d64ff92e0c6c0f2018c7495a5b20e2451839299a018e0e5108b2680f71e0"}, + {file = "scikit_image-0.25.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2cfc96b27afe9a05bc92f8c6235321d3a66499995675b27415e0d0c76625173"}, + {file = "scikit_image-0.25.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24cc986e1f4187a12aa319f777b36008764e856e5013666a4a83f8df083c2641"}, + {file = "scikit_image-0.25.2-cp311-cp311-win_amd64.whl", hash = "sha256:b4f6b61fc2db6340696afe3db6b26e0356911529f5f6aee8c322aa5157490c9b"}, + {file = "scikit_image-0.25.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8db8dd03663112783221bf01ccfc9512d1cc50ac9b5b0fe8f4023967564719fb"}, + {file = "scikit_image-0.25.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:483bd8cc10c3d8a7a37fae36dfa5b21e239bd4ee121d91cad1f81bba10cfb0ed"}, + {file = "scikit_image-0.25.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d1e80107bcf2bf1291acfc0bf0425dceb8890abe9f38d8e94e23497cbf7ee0d"}, + {file = "scikit_image-0.25.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a17e17eb8562660cc0d31bb55643a4da996a81944b82c54805c91b3fe66f4824"}, + {file = "scikit_image-0.25.2-cp312-cp312-win_amd64.whl", hash = "sha256:bdd2b8c1de0849964dbc54037f36b4e9420157e67e45a8709a80d727f52c7da2"}, + {file = "scikit_image-0.25.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7efa888130f6c548ec0439b1a7ed7295bc10105458a421e9bf739b457730b6da"}, + {file = "scikit_image-0.25.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:dd8011efe69c3641920614d550f5505f83658fe33581e49bed86feab43a180fc"}, + {file = "scikit_image-0.25.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28182a9d3e2ce3c2e251383bdda68f8d88d9fff1a3ebe1eb61206595c9773341"}, + {file = "scikit_image-0.25.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8abd3c805ce6944b941cfed0406d88faeb19bab3ed3d4b50187af55cf24d147"}, + {file = "scikit_image-0.25.2-cp313-cp313-win_amd64.whl", hash = "sha256:64785a8acefee460ec49a354706db0b09d1f325674107d7fa3eadb663fb56d6f"}, + {file = "scikit_image-0.25.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:330d061bd107d12f8d68f1d611ae27b3b813b8cdb0300a71d07b1379178dd4cd"}, + {file = "scikit_image-0.25.2.tar.gz", hash = "sha256:e5a37e6cd4d0c018a7a55b9d601357e3382826d3888c10d0213fc63bff977dde"}, ] [[package]] @@ -1855,6 +2579,7 @@ name = "scikit-learn" version = "1.6.1" requires_python = ">=3.9" summary = "A set of python modules for machine learning and data mining" +groups = ["default"] dependencies = [ "joblib>=1.2.0", "numpy>=1.19.5", @@ -1862,128 +2587,279 @@ dependencies = [ "threadpoolctl>=3.1.0", ] files = [ + {file = "scikit_learn-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:72abc587c75234935e97d09aa4913a82f7b03ee0b74111dcc2881cba3c5a7b33"}, + {file = "scikit_learn-1.6.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b3b00cdc8f1317b5f33191df1386c0befd16625f49d979fe77a8d44cae82410d"}, + {file = "scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc4765af3386811c3ca21638f63b9cf5ecf66261cc4815c1db3f1e7dc7b79db2"}, {file = "scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25fc636bdaf1cc2f4a124a116312d837148b5e10872147bdaf4887926b8c03d8"}, + {file = "scikit_learn-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:fa909b1a36e000a03c382aade0bd2063fd5680ff8b8e501660c0f59f021a6415"}, + {file = "scikit_learn-1.6.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:926f207c804104677af4857b2c609940b743d04c4c35ce0ddc8ff4f053cddc1b"}, + {file = "scikit_learn-1.6.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2c2cae262064e6a9b77eee1c8e768fc46aa0b8338c6a8297b9b6759720ec0ff2"}, + {file = "scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1061b7c028a8663fb9a1a1baf9317b64a257fcb036dae5c8752b2abef31d136f"}, + {file = "scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e69fab4ebfc9c9b580a7a80111b43d214ab06250f8a7ef590a4edf72464dd86"}, + {file = "scikit_learn-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:70b1d7e85b1c96383f872a519b3375f92f14731e279a7b4c6cfd650cf5dffc52"}, + {file = "scikit_learn-1.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ffa1e9e25b3d93990e74a4be2c2fc61ee5af85811562f1288d5d055880c4322"}, + {file = "scikit_learn-1.6.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:dc5cf3d68c5a20ad6d571584c0750ec641cc46aeef1c1507be51300e6003a7e1"}, + {file = "scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c06beb2e839ecc641366000ca84f3cf6fa9faa1777e29cf0c04be6e4d096a348"}, + {file = "scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8ca8cb270fee8f1f76fa9bfd5c3507d60c6438bbee5687f81042e2bb98e5a97"}, + {file = "scikit_learn-1.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:7a1c43c8ec9fde528d664d947dc4c0789be4077a3647f232869f41d9bf50e0fb"}, + {file = "scikit_learn-1.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a17c1dea1d56dcda2fac315712f3651a1fea86565b64b48fa1bc090249cbf236"}, + {file = "scikit_learn-1.6.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6a7aa5f9908f0f28f4edaa6963c0a6183f1911e63a69aa03782f0d924c830a35"}, + {file = "scikit_learn-1.6.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0650e730afb87402baa88afbf31c07b84c98272622aaba002559b614600ca691"}, + {file = "scikit_learn-1.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:3f59fe08dc03ea158605170eb52b22a105f238a5d512c4470ddeca71feae8e5f"}, {file = "scikit_learn-1.6.1.tar.gz", hash = "sha256:b4fc2525eca2c69a59260f583c56a7557c6ccdf8deafdba6e060f94c1c59738e"}, ] [[package]] name = "scipy" -version = "1.14.1" +version = "1.15.2" requires_python = ">=3.10" summary = "Fundamental algorithms for scientific computing in Python" -dependencies = [ - "numpy<2.3,>=1.23.5", -] -files = [ - {file = "scipy-1.14.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675"}, - {file = "scipy-1.14.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2"}, - {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617"}, - {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8"}, - {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37"}, - {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2"}, - {file = "scipy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2"}, - {file = "scipy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94"}, - {file = "scipy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d"}, - {file = "scipy-1.14.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07"}, - {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5"}, - {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc"}, - {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310"}, - {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066"}, - {file = "scipy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1"}, - {file = "scipy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f"}, - {file = "scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79"}, - {file = "scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e"}, - {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73"}, - {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e"}, - {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d"}, - {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e"}, - {file = "scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06"}, - {file = "scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84"}, - {file = "scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417"}, +groups = ["default"] +dependencies = [ + "numpy<2.5,>=1.23.5", +] +files = [ + {file = "scipy-1.15.2-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:92233b2df6938147be6fa8824b8136f29a18f016ecde986666be5f4d686a91a4"}, + {file = "scipy-1.15.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:62ca1ff3eb513e09ed17a5736929429189adf16d2d740f44e53270cc800ecff1"}, + {file = "scipy-1.15.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4c6676490ad76d1c2894d77f976144b41bd1a4052107902238047fb6a473e971"}, + {file = "scipy-1.15.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a8bf5cb4a25046ac61d38f8d3c3426ec11ebc350246a4642f2f315fe95bda655"}, + {file = "scipy-1.15.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a8e34cf4c188b6dd004654f88586d78f95639e48a25dfae9c5e34a6dc34547e"}, + {file = "scipy-1.15.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28a0d2c2075946346e4408b211240764759e0fabaeb08d871639b5f3b1aca8a0"}, + {file = "scipy-1.15.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:42dabaaa798e987c425ed76062794e93a243be8f0f20fff6e7a89f4d61cb3d40"}, + {file = "scipy-1.15.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6f5e296ec63c5da6ba6fa0343ea73fd51b8b3e1a300b0a8cae3ed4b1122c7462"}, + {file = "scipy-1.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:597a0c7008b21c035831c39927406c6181bcf8f60a73f36219b69d010aa04737"}, + {file = "scipy-1.15.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c4697a10da8f8765bb7c83e24a470da5797e37041edfd77fd95ba3811a47c4fd"}, + {file = "scipy-1.15.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:869269b767d5ee7ea6991ed7e22b3ca1f22de73ab9a49c44bad338b725603301"}, + {file = "scipy-1.15.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:bad78d580270a4d32470563ea86c6590b465cb98f83d760ff5b0990cb5518a93"}, + {file = "scipy-1.15.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b09ae80010f52efddb15551025f9016c910296cf70adbf03ce2a8704f3a5ad20"}, + {file = "scipy-1.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a6fd6eac1ce74a9f77a7fc724080d507c5812d61e72bd5e4c489b042455865e"}, + {file = "scipy-1.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b871df1fe1a3ba85d90e22742b93584f8d2b8e6124f8372ab15c71b73e428b8"}, + {file = "scipy-1.15.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:03205d57a28e18dfd39f0377d5002725bf1f19a46f444108c29bdb246b6c8a11"}, + {file = "scipy-1.15.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:601881dfb761311045b03114c5fe718a12634e5608c3b403737ae463c9885d53"}, + {file = "scipy-1.15.2-cp312-cp312-win_amd64.whl", hash = "sha256:e7c68b6a43259ba0aab737237876e5c2c549a031ddb7abc28c7b47f22e202ded"}, + {file = "scipy-1.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01edfac9f0798ad6b46d9c4c9ca0e0ad23dbf0b1eb70e96adb9fa7f525eff0bf"}, + {file = "scipy-1.15.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:08b57a9336b8e79b305a143c3655cc5bdbe6d5ece3378578888d2afbb51c4e37"}, + {file = "scipy-1.15.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:54c462098484e7466362a9f1672d20888f724911a74c22ae35b61f9c5919183d"}, + {file = "scipy-1.15.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:cf72ff559a53a6a6d77bd8eefd12a17995ffa44ad86c77a5df96f533d4e6c6bb"}, + {file = "scipy-1.15.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9de9d1416b3d9e7df9923ab23cd2fe714244af10b763975bea9e4f2e81cebd27"}, + {file = "scipy-1.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb530e4794fc8ea76a4a21ccb67dea33e5e0e60f07fc38a49e821e1eae3b71a0"}, + {file = "scipy-1.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5ea7ed46d437fc52350b028b1d44e002646e28f3e8ddc714011aaf87330f2f32"}, + {file = "scipy-1.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11e7ad32cf184b74380f43d3c0a706f49358b904fa7d5345f16ddf993609184d"}, + {file = "scipy-1.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:a5080a79dfb9b78b768cebf3c9dcbc7b665c5875793569f48bf0e2b1d7f68f6f"}, + {file = "scipy-1.15.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:447ce30cee6a9d5d1379087c9e474628dab3db4a67484be1b7dc3196bfb2fac9"}, + {file = "scipy-1.15.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:c90ebe8aaa4397eaefa8455a8182b164a6cc1d59ad53f79943f266d99f68687f"}, + {file = "scipy-1.15.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:def751dd08243934c884a3221156d63e15234a3155cf25978b0a668409d45eb6"}, + {file = "scipy-1.15.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:302093e7dfb120e55515936cb55618ee0b895f8bcaf18ff81eca086c17bd80af"}, + {file = "scipy-1.15.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cd5b77413e1855351cdde594eca99c1f4a588c2d63711388b6a1f1c01f62274"}, + {file = "scipy-1.15.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d0194c37037707b2afa7a2f2a924cf7bac3dc292d51b6a925e5fcb89bc5c776"}, + {file = "scipy-1.15.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:bae43364d600fdc3ac327db99659dcb79e6e7ecd279a75fe1266669d9a652828"}, + {file = "scipy-1.15.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f031846580d9acccd0044efd1a90e6f4df3a6e12b4b6bd694a7bc03a89892b28"}, + {file = "scipy-1.15.2-cp313-cp313t-win_amd64.whl", hash = "sha256:fe8a9eb875d430d81755472c5ba75e84acc980e4a8f6204d402849234d3017db"}, + {file = "scipy-1.15.2.tar.gz", hash = "sha256:cd58a314d92838f7e6f755c8a2167ead4f27e1fd5c1251fd54289569ef3495ec"}, ] [[package]] name = "setuptools" -version = "72.2.0" -requires_python = ">=3.8" +version = "75.8.2" +requires_python = ">=3.9" summary = "Easily download, build, install, upgrade, and uninstall Python packages" +groups = ["default"] files = [ - {file = "setuptools-72.2.0-py3-none-any.whl", hash = "sha256:f11dd94b7bae3a156a95ec151f24e4637fb4fa19c878e4d191bfb8b2d82728c4"}, - {file = "setuptools-72.2.0.tar.gz", hash = "sha256:80aacbf633704e9c8bfa1d99fa5dd4dc59573efcf9e4042c13d3bcef91ac2ef9"}, + {file = "setuptools-75.8.2-py3-none-any.whl", hash = "sha256:558e47c15f1811c1fa7adbd0096669bf76c1d3f433f58324df69f3f5ecac4e8f"}, + {file = "setuptools-75.8.2.tar.gz", hash = "sha256:4880473a969e5f23f2a2be3646b2dfd84af9028716d398e46192f84bc36900d2"}, ] [[package]] name = "simsimd" version = "6.2.1" summary = "Portable mixed-precision BLAS-like vector math library for x86 and ARM" -files = [ +groups = ["default"] +files = [ + {file = "simsimd-6.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e9614309af75be4d08a051dc61ed5cf41b5239b8303b37dc2f9c8a7223534392"}, + {file = "simsimd-6.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ea4f0f68be5f85bbcf4322bfdd1b449176cf5fdd99960c546514457635632443"}, + {file = "simsimd-6.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:12a8d60ccc8991dfbbf056c221ce4f02135f5892492894972f421a6f155015d9"}, + {file = "simsimd-6.2.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a74142ea21a6fd3ec5c64e4d4acf1ec6f4d80c0bb1a5989d68af6e84f7ac612e"}, + {file = "simsimd-6.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298f7c793fc2a1eeedcefa1278eb2ef6f52ce0b36aaa8780885f96a39ce1a4e8"}, + {file = "simsimd-6.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4025ebad36fb3fa5cffcd48d33375d5e5decc59c1129a259b74fed097eab1ab5"}, + {file = "simsimd-6.2.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:f486682aa7a8918d86df411d3c11c635db4b67d514cb6bb499c0edab7fb8ec58"}, {file = "simsimd-6.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:173e66699597a4fcf6fa50b52cced40216fdcfba15f60b761a2bd9cb1d98a444"}, + {file = "simsimd-6.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b5c6f79f797cc020a2ff64950162dfb6d130c51a07cdac5ad97ec836e85ce50"}, + {file = "simsimd-6.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:25812637f43feaef1a33ae00b81a4d2b0116aadae3a08267486c1e57236fc368"}, + {file = "simsimd-6.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:592a578c788a9cb7877eff41487cc7f50474e00f774de74bea8590fa95c804ae"}, + {file = "simsimd-6.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:191c020f312350ac06eee829376b11d8c1282da8fefb4381fe0625edfb678d8d"}, + {file = "simsimd-6.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9ad2c247ed58ba9bb170a01295cb315a45c817775cc7e51ad342f70978a1057"}, + {file = "simsimd-6.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0ff603134600da12175e66b842b7a7331c827fa070d1d8b63386a40bc8d09fcd"}, + {file = "simsimd-6.2.1-cp311-cp311-win32.whl", hash = "sha256:99dff4e04663c82284152ecc2e8bf76b2825f3f17e179abf7892e06196061056"}, + {file = "simsimd-6.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:0efc6343c440a26cf16463c4c667655af9597bcbd55ad66f33a80b2b84de7412"}, + {file = "simsimd-6.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:2d364f2c24dd38578bf0eec436c4b901c900ae1893680f46eb5632e01330d814"}, + {file = "simsimd-6.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9b3315e41bb759dc038ecd6f4fa7bcf278bf72ee7d982f752482cdc732aea271"}, + {file = "simsimd-6.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8d476c874bafa0d12d4c8c5c47faf17407f3c96140616384421c2aa980342b6f"}, + {file = "simsimd-6.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9d4f15c06cc221d29e181197c7bbf92c5e829220cbeb3cd1cf080de78b04f2a"}, + {file = "simsimd-6.2.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d286fd4538cb1a1c70e69da00a3acee301519d578931b41161f4f1379d1195c6"}, + {file = "simsimd-6.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:050f68cfa85f1fb2cfa156280928e42926e3977034b755023ce1315bf59e87ff"}, + {file = "simsimd-6.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67bb4b17e04919545f29c7b708faaccbe027f164f8b5c9f4328604fa8f5560ea"}, + {file = "simsimd-6.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3d6bffd999dbb36e606b065e0180365efac2606049c4f7818e4cba2d34c3678f"}, + {file = "simsimd-6.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:25adb244fb75dbf49af0d1bcac4ed4a3fef8e847d78449faa5595af0a3e20d61"}, + {file = "simsimd-6.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b4542cee77e801a9c27370fc36ae271514fc0fb2ce14a35f8b25f47989e3d267"}, + {file = "simsimd-6.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:4f665228f8ff4911790b485e74b00fa9586a141dde6011970be71bb303b5a22f"}, + {file = "simsimd-6.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:783b4308f80ae00763b0eaa0dac26196958f9c2df60d35a0347ebd2f82ece46d"}, + {file = "simsimd-6.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:95055e72cfe313c1c8694783bf8a631cc15673b3b775abef367e396d931db0b8"}, + {file = "simsimd-6.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a98f2b383f51b4f4ee568a637fc7958a347fdae0bd184cff8faa8030b6454a39"}, + {file = "simsimd-6.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2e474fd10ceb38e2c9f826108a7762f8ff7912974846d86f08c4e7b19cd35ed4"}, + {file = "simsimd-6.2.1-cp312-cp312-win32.whl", hash = "sha256:b2530ea44fffeab25e5752bec6a5991f30fbc430b04647980db5b195c0971d48"}, + {file = "simsimd-6.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:dc23283235d5b8f0373b95a547e26da2d7785647a5d0fa15c282fc8c49c0dcb0"}, + {file = "simsimd-6.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:5692ce7e56253178eea9dbd58191734918409b83d54b07cfdcecf868d0150a73"}, + {file = "simsimd-6.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:76b32fdc7142c9714e94651ece8bc00dd5139c554813211552aa358e44af0e07"}, + {file = "simsimd-6.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f44e5e2319427f94db658c6f75caae78850da505902874a1664a83ef5713f333"}, + {file = "simsimd-6.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:05323cbad7200592c2e53fbcc759e615594e8ca444ef5eddf9f3fb196ad4de9c"}, + {file = "simsimd-6.2.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1f3cbe5c39db2bb64f30999104de1215ba3805d6059af7bc5a9d662d50f4707"}, + {file = "simsimd-6.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaa94e0932ae2a48b7e4df8c29204dc9fe59f72b1faeb08e9d5015bf51fb9f21"}, + {file = "simsimd-6.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:508465f8d4e3e0fff07c939921aeedf55b0ade9f56f64e938c350c283dea42fb"}, + {file = "simsimd-6.2.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:ca67f6273ef544c74c48b134af756de7c98a711ccf69cd0791225f26dd449281"}, + {file = "simsimd-6.2.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:d470b43ce606f21f54a23fc19ad6928333e17d0956b02eb27b7b112edc156a10"}, + {file = "simsimd-6.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59518b9834c167a1dd8900600718e95cdadc9d74525452f426aa8455a38c55ef"}, + {file = "simsimd-6.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:59c2978c4e402097d8a4b38f076ff98cc43e6b059d53f89736404f26e9a9bd5a"}, + {file = "simsimd-6.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:edc68e727d53ed2866dcfb625f15e52be8f1e6809f4be2147bf8d2115a2542b7"}, + {file = "simsimd-6.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9e5e82551d75c0e2cd0d4b8af8db1cae7b5ac6dcc076c0c760870ff81f78135b"}, + {file = "simsimd-6.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2fa19f8c9786757d19afcbda9f8fb68de55e4f5562725ae8727f887d01bf0e4d"}, + {file = "simsimd-6.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5b0748aa6bd4df4c5a3f5e979aec14b26588f1b2e0d44075dcc9eaf4d555e15b"}, + {file = "simsimd-6.2.1-cp313-cp313-win32.whl", hash = "sha256:7f43721e1a4ebe8d2245b0e85dd7de7153d1bf22839579d5f69a345909c68d9e"}, + {file = "simsimd-6.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:6af1565e0ef7060bc52a38e3273a8e6e92aff47835965dc5311298563475935e"}, + {file = "simsimd-6.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:e690b41377c8dd157d585713b0bc35c845aee7742334bf12d1f087fc8a65b6c3"}, {file = "simsimd-6.2.1.tar.gz", hash = "sha256:5e202c5386a4141946b7aee05faac8ebc2e36bca0a360b24080e57b59bc4ef6a"}, ] [[package]] name = "six" -version = "1.16.0" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +version = "1.17.0" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" summary = "Python 2 and 3 compatibility utilities" +groups = ["default"] files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] [[package]] name = "smmap" -version = "5.0.1" +version = "5.0.2" requires_python = ">=3.7" summary = "A pure Python implementation of a sliding window memory map manager" +groups = ["default"] files = [ - {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, - {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, + {file = "smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e"}, + {file = "smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5"}, ] [[package]] name = "sqlalchemy" -version = "2.0.33" +version = "2.0.38" requires_python = ">=3.7" summary = "Database Abstraction Library" +groups = ["default"] dependencies = [ - "greenlet!=0.4.17; python_version < \"3.13\" and (platform_machine == \"aarch64\" or (platform_machine == \"ppc64le\" or (platform_machine == \"x86_64\" or (platform_machine == \"amd64\" or (platform_machine == \"AMD64\" or (platform_machine == \"win32\" or platform_machine == \"WIN32\"))))))", + "greenlet!=0.4.17; (platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\") and python_version < \"3.14\"", + "importlib-metadata; python_version < \"3.8\"", "typing-extensions>=4.6.0", ] files = [ - {file = "SQLAlchemy-2.0.33-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:570ec43e8c3c020abac4f0720baa5fe5187334e3f1e8e1777183c041962b61cc"}, - {file = "SQLAlchemy-2.0.33-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81759e77a4985abdbac068762a0eaf0f11860fe041ad6da170aae7615ea72531"}, - {file = "SQLAlchemy-2.0.33-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82c72da5be489c8d150deba70d5732398695418df5232bceb52ee323ddd9753b"}, - {file = "SQLAlchemy-2.0.33-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:30a3f55be76364b64c83788728faaba782ab282a24909e1994404c2146d39982"}, - {file = "SQLAlchemy-2.0.33-cp311-cp311-win32.whl", hash = "sha256:17d0c69f66392ad2db1609373a74d1f834b2e632f3f52d446747b8ec220aea53"}, - {file = "SQLAlchemy-2.0.33-cp311-cp311-win_amd64.whl", hash = "sha256:c5d5a733c6af7f392435e673d1b136f6bdf2366033abb35eed680400dc730840"}, - {file = "SQLAlchemy-2.0.33-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1d81e3aeab456fe24c3f0dcfd4f952a3a5ee45e9c14fc66d34c1d7a60cf7b698"}, - {file = "SQLAlchemy-2.0.33-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca8788dc1baee100f09110f33a01d928cf9df4483d2bfb25a37be31a659d46bb"}, - {file = "SQLAlchemy-2.0.33-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684aee5fd811091b2f48006fb3fe6c7f2de4a716ef8d294a2aab762099753133"}, - {file = "SQLAlchemy-2.0.33-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1109cc6dc5c9d1223c42186391e6a5509e6d4ab2c30fa629573c10184f742f2e"}, - {file = "SQLAlchemy-2.0.33-cp312-cp312-win32.whl", hash = "sha256:c633e2d2f8a7b88c06e276bbe16cb7e62fed815fcbeb69cd9752cea166ecb8e8"}, - {file = "SQLAlchemy-2.0.33-cp312-cp312-win_amd64.whl", hash = "sha256:77eaf8fdf305266b806a91ae4633edbf86ad37e13bd92ac85e305e7f654c19a5"}, - {file = "SQLAlchemy-2.0.33-py3-none-any.whl", hash = "sha256:ae294808afde1b14a1a69aa86a69cadfe391848bbb233a5332a8065e4081cabc"}, - {file = "sqlalchemy-2.0.33.tar.gz", hash = "sha256:91c93333c2b37ff721dc83b37e28c29de4c502b5612f2d093468037b86aa2be0"}, + {file = "SQLAlchemy-2.0.38-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bf89e0e4a30714b357f5d46b6f20e0099d38b30d45fa68ea48589faf5f12f62d"}, + {file = "SQLAlchemy-2.0.38-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8455aa60da49cb112df62b4721bd8ad3654a3a02b9452c783e651637a1f21fa2"}, + {file = "SQLAlchemy-2.0.38-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f53c0d6a859b2db58332e0e6a921582a02c1677cc93d4cbb36fdf49709b327b2"}, + {file = "SQLAlchemy-2.0.38-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3c4817dff8cef5697f5afe5fec6bc1783994d55a68391be24cb7d80d2dbc3a6"}, + {file = "SQLAlchemy-2.0.38-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c9cea5b756173bb86e2235f2f871b406a9b9d722417ae31e5391ccaef5348f2c"}, + {file = "SQLAlchemy-2.0.38-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:40e9cdbd18c1f84631312b64993f7d755d85a3930252f6276a77432a2b25a2f3"}, + {file = "SQLAlchemy-2.0.38-cp311-cp311-win32.whl", hash = "sha256:cb39ed598aaf102251483f3e4675c5dd6b289c8142210ef76ba24aae0a8f8aba"}, + {file = "SQLAlchemy-2.0.38-cp311-cp311-win_amd64.whl", hash = "sha256:f9d57f1b3061b3e21476b0ad5f0397b112b94ace21d1f439f2db472e568178ae"}, + {file = "SQLAlchemy-2.0.38-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12d5b06a1f3aeccf295a5843c86835033797fea292c60e72b07bcb5d820e6dd3"}, + {file = "SQLAlchemy-2.0.38-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e036549ad14f2b414c725349cce0772ea34a7ab008e9cd67f9084e4f371d1f32"}, + {file = "SQLAlchemy-2.0.38-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee3bee874cb1fadee2ff2b79fc9fc808aa638670f28b2145074538d4a6a5028e"}, + {file = "SQLAlchemy-2.0.38-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e185ea07a99ce8b8edfc788c586c538c4b1351007e614ceb708fd01b095ef33e"}, + {file = "SQLAlchemy-2.0.38-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b79ee64d01d05a5476d5cceb3c27b5535e6bb84ee0f872ba60d9a8cd4d0e6579"}, + {file = "SQLAlchemy-2.0.38-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:afd776cf1ebfc7f9aa42a09cf19feadb40a26366802d86c1fba080d8e5e74bdd"}, + {file = "SQLAlchemy-2.0.38-cp312-cp312-win32.whl", hash = "sha256:a5645cd45f56895cfe3ca3459aed9ff2d3f9aaa29ff7edf557fa7a23515a3725"}, + {file = "SQLAlchemy-2.0.38-cp312-cp312-win_amd64.whl", hash = "sha256:1052723e6cd95312f6a6eff9a279fd41bbae67633415373fdac3c430eca3425d"}, + {file = "SQLAlchemy-2.0.38-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ecef029b69843b82048c5b347d8e6049356aa24ed644006c9a9d7098c3bd3bfd"}, + {file = "SQLAlchemy-2.0.38-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c8bcad7fc12f0cc5896d8e10fdf703c45bd487294a986903fe032c72201596b"}, + {file = "SQLAlchemy-2.0.38-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a0ef3f98175d77180ffdc623d38e9f1736e8d86b6ba70bff182a7e68bed7727"}, + {file = "SQLAlchemy-2.0.38-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b0ac78898c50e2574e9f938d2e5caa8fe187d7a5b69b65faa1ea4648925b096"}, + {file = "SQLAlchemy-2.0.38-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9eb4fa13c8c7a2404b6a8e3772c17a55b1ba18bc711e25e4d6c0c9f5f541b02a"}, + {file = "SQLAlchemy-2.0.38-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5dba1cdb8f319084f5b00d41207b2079822aa8d6a4667c0f369fce85e34b0c86"}, + {file = "SQLAlchemy-2.0.38-cp313-cp313-win32.whl", hash = "sha256:eae27ad7580529a427cfdd52c87abb2dfb15ce2b7a3e0fc29fbb63e2ed6f8120"}, + {file = "SQLAlchemy-2.0.38-cp313-cp313-win_amd64.whl", hash = "sha256:b335a7c958bc945e10c522c069cd6e5804f4ff20f9a744dd38e748eb602cbbda"}, + {file = "SQLAlchemy-2.0.38-py3-none-any.whl", hash = "sha256:63178c675d4c80def39f1febd625a6333f44c0ba269edd8a468b156394b27753"}, + {file = "sqlalchemy-2.0.38.tar.gz", hash = "sha256:e5a4d82bdb4bf1ac1285a68eab02d253ab73355d9f0fe725a97e1e0fa689decb"}, ] [[package]] name = "sqlparse" -version = "0.5.1" +version = "0.5.3" requires_python = ">=3.8" summary = "A non-validating SQL parser." +groups = ["default"] files = [ - {file = "sqlparse-0.5.1-py3-none-any.whl", hash = "sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4"}, - {file = "sqlparse-0.5.1.tar.gz", hash = "sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e"}, + {file = "sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca"}, + {file = "sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272"}, ] [[package]] name = "stringzilla" -version = "3.12.1" +version = "3.12.2" summary = "SIMD-accelerated string search, sort, hashes, fingerprints, & edit distances" -files = [ - {file = "stringzilla-3.12.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef30bd15638f83aa12978a0d184d1830caf55a7f80ccbbab0528f73873d4a017"}, - {file = "stringzilla-3.12.1.tar.gz", hash = "sha256:b812c9fc9a0ff36594e847443443895be91d38aa460a7b02b4a6e3048b76e2c8"}, +groups = ["default"] +files = [ + {file = "stringzilla-3.12.2-cp311-cp311-macosx_10_11_universal2.whl", hash = "sha256:78363cfa4ac9a232b728dd40f59b0d8b0ef31629708751fbdee9c0117d06500f"}, + {file = "stringzilla-3.12.2-cp311-cp311-macosx_10_11_x86_64.whl", hash = "sha256:0ad4ae468411348532fd7cb35c7f0207b37f1a40fc7d7882f63e7b0cfc8ec117"}, + {file = "stringzilla-3.12.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6a4b3048d87637c13562aac4574cf5c422a0c242214466eef8f608ae7fbb3668"}, + {file = "stringzilla-3.12.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a8ba2ac1eb24942b436108cbeba1e21a4478a3711b30411f37d1b7ac5c6608bc"}, + {file = "stringzilla-3.12.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2d7cc27190cbad655b1afcd135b329aa236e76da0eb26bc0e3c5e5d8e2763824"}, + {file = "stringzilla-3.12.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab52ad4a78ad85c7269999641e2be030b76f907ff2838edd89dd0f312fb96984"}, + {file = "stringzilla-3.12.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e1d5d9ccfe33e0b7d24330630ea4847cc92b4017f8068c863772e7d469f3767"}, + {file = "stringzilla-3.12.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bda0b337a4f02caf8084227340538964b345f09e22dcee9f9834d535f2849546"}, + {file = "stringzilla-3.12.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15de752fa1e671193b67d3b88150c9d2702671088f64751a8d4932d0dd833263"}, + {file = "stringzilla-3.12.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:1f4761736ea1bf44720d2c5938f35a1a8949b477320cc64b0ce71648d61c72bf"}, + {file = "stringzilla-3.12.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:abebe012bacffdb1c6b3dda22059c392a680768b2bf0e152066d69d03bbc2231"}, + {file = "stringzilla-3.12.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:144246e778d9738a68e0b119b9ea98f2c6953b7b777abacb75a59ced8a418370"}, + {file = "stringzilla-3.12.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6076fdacb33a62709f122edbeb99f209dd6e805915f8bc727b0c0acacb58c095"}, + {file = "stringzilla-3.12.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:796efadc75fa1760af4bf860d49b4c00b155101f62563680cf0961af98a87d3a"}, + {file = "stringzilla-3.12.2-cp311-cp311-win32.whl", hash = "sha256:a823b1c5e384ebb35fe2f300fc5020c86781883c20895c3ab103f546d47c67ad"}, + {file = "stringzilla-3.12.2-cp311-cp311-win_amd64.whl", hash = "sha256:77d208fc5afbf7ab03d82c7555a0115f92d0cd8f5d1fac45c104b14472fbbe02"}, + {file = "stringzilla-3.12.2-cp311-cp311-win_arm64.whl", hash = "sha256:9f8ff669618c1d67e3ee19690be9b18afe26c0ba5578e0eceda7cb6abc4f15e8"}, + {file = "stringzilla-3.12.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:89404cabcc05ddd3e7a98497db0fd33d5cf1b1b4b6c7028129f54bfe280a8b00"}, + {file = "stringzilla-3.12.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5664f6144774f8f51b98d65ff221bd886e28d00bf9063e0312cd4306dec5d31d"}, + {file = "stringzilla-3.12.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3e433dd22c25a5bebd598610350946e4c49f160dd1a5c0259bf6903cb66184df"}, + {file = "stringzilla-3.12.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6096ba629f61bb31ca6644f45f988b0c3b84eefdbc2d5d3244b351cdb3698dd1"}, + {file = "stringzilla-3.12.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8d2e913adcd1373c0805eaa98c6083f0f28bf6a0d2a87363c75523e22396a64c"}, + {file = "stringzilla-3.12.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4a918b1b28314a06f2ab3f18832abaa7bd78d4125c0d8f6df66fe6a605b47643"}, + {file = "stringzilla-3.12.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edf6b66fa50f4134f74b5b9268659fee9fa313ad74044d178181dc2e951f6aaa"}, + {file = "stringzilla-3.12.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ca4f853b8d659e83c09afba3eae0892b9671966d4a2a0aadc8f615ac8c1d1da"}, + {file = "stringzilla-3.12.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d7b511c86f349c93b692fff8cc8cd01a0836dac267686a45ea3cfccc0d80e1dc"}, + {file = "stringzilla-3.12.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45d8a7dc944fa00b6578500389dfe82992ad64f2c47d5946328d54d2f1b576ff"}, + {file = "stringzilla-3.12.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f6fba19519d8c4710ac0ee69fe963474e82861a333949bd8351b3c34ef95256b"}, + {file = "stringzilla-3.12.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d2e8b2180d22cbb34c9d32f5d8aa200fb4b20d5855851933efb3d29f794f4ecd"}, + {file = "stringzilla-3.12.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9fc777c111ebb7d0d4f6b695f79c33d4c4233e53cb8e933ba602ec6a0739030d"}, + {file = "stringzilla-3.12.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2102058a1ed6f4a8a932cc75b14e246930b6377c120069ce814e220fdd0b2709"}, + {file = "stringzilla-3.12.2-cp312-cp312-win32.whl", hash = "sha256:49852d3f0334c5b90b4585f01797ac257e697f3fa0d9b490e1934523185138c3"}, + {file = "stringzilla-3.12.2-cp312-cp312-win_amd64.whl", hash = "sha256:3189580a878d3b861ce1c8b3783fe65cb835313fe27a32c751daac36d6d63160"}, + {file = "stringzilla-3.12.2-cp312-cp312-win_arm64.whl", hash = "sha256:d58333ceee04f514d2778d15ed0daca25b8f04aba519b9ea03c0447c5dc5f38b"}, + {file = "stringzilla-3.12.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:72721c1acaf9003d946f1e15cfcac6996a34640fdf2b3595a724a3c6f52ccb84"}, + {file = "stringzilla-3.12.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:038e9cbd1d91dc3bae32b95a2616aaf9516b65e0a1326d8b9b33afbd1b92b100"}, + {file = "stringzilla-3.12.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a7596bb471c92fb63871eaf8eb26461597772ad1c2026362b10bff9a4aa71f78"}, + {file = "stringzilla-3.12.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cecbd9046642778d0552c4d756f89d6b83d3ea7932a80b8fe59ad659e704fd62"}, + {file = "stringzilla-3.12.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7b2c484f37f9bcb7bb8a430125b9f62d007abfaea9d0f7c9140aa9231f137669"}, + {file = "stringzilla-3.12.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl", hash = "sha256:aad609515530159073c60ceb505fef9dd5c051794f4a742ca1a868baf61a652b"}, + {file = "stringzilla-3.12.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4730bd6051d4eccaae1efa94418542ef8e03c84280035ce0a63ac4ed9fc12225"}, + {file = "stringzilla-3.12.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a77a2da827bb89f5ba499d369393a4213bca6ea14f5a9c00e4822c33517b82e3"}, + {file = "stringzilla-3.12.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1091cc713a605ce4cd0f43f23e3f53c83797f3463cc34e2b25e75a1584e7a1ab"}, + {file = "stringzilla-3.12.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6f8f96b52850b59cc087051c90a6cdc03692c02d0b25906b9dd4da68ab44203d"}, + {file = "stringzilla-3.12.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c2e47828819c656dc1dd6c30c4535879bbe648cb26879073a83014c81916acd2"}, + {file = "stringzilla-3.12.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bacd129e39977c887406f476aa0eef377d5401f33b54518f52e20767a19cb82b"}, + {file = "stringzilla-3.12.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5f2e045cb443124c9f72a5e6353e707ce3e110bf1fc4359cf6cbd3c16dfdb6f6"}, + {file = "stringzilla-3.12.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0efa82e5daedfe32c6a5e862017763bae9a32d6f12b8e72d7c4eaa5a6819f357"}, + {file = "stringzilla-3.12.2-cp313-cp313-win32.whl", hash = "sha256:3920b8c7ad11e7d76763df5747bd0770c2b797ea5afb0169d3fef0a47b4f0a2a"}, + {file = "stringzilla-3.12.2-cp313-cp313-win_amd64.whl", hash = "sha256:b73b6c6ee7664fc2adf6a22861b3acefbd7d692ac4741299be1679cbda3710f4"}, + {file = "stringzilla-3.12.2-cp313-cp313-win_arm64.whl", hash = "sha256:7f821ddccc36f57ae14351f12818e278a24b0e161c076c464d2c8193f0d56db5"}, + {file = "stringzilla-3.12.2.tar.gz", hash = "sha256:0538d5de6ec5f9cd59b0254fb412254c43b421be4baf37740fa781e8c09b7663"}, ] [[package]] @@ -1991,6 +2867,8 @@ name = "sympy" version = "1.13.1" requires_python = ">=3.8" summary = "Computer algebra system (CAS) in Python" +groups = ["default"] +marker = "python_version >= \"3.9\"" dependencies = [ "mpmath<1.4,>=1.1.0", ] @@ -2004,16 +2882,32 @@ name = "threadpoolctl" version = "3.5.0" requires_python = ">=3.8" summary = "threadpoolctl" +groups = ["default"] files = [ {file = "threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467"}, {file = "threadpoolctl-3.5.0.tar.gz", hash = "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107"}, ] +[[package]] +name = "tifffile" +version = "2025.2.18" +requires_python = ">=3.10" +summary = "Read and write TIFF files" +groups = ["default"] +dependencies = [ + "numpy", +] +files = [ + {file = "tifffile-2025.2.18-py3-none-any.whl", hash = "sha256:54b36c4d5e5b8d8920134413edfe5a7cfb1c7617bb50cddf7e2772edb7149043"}, + {file = "tifffile-2025.2.18.tar.gz", hash = "sha256:8d731789e691b468746c1615d989bc550ac93cf753e9210865222e90a5a95d11"}, +] + [[package]] name = "torch" version = "2.6.0" requires_python = ">=3.9.0" summary = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" +groups = ["default"] dependencies = [ "filelock", "fsspec", @@ -2032,27 +2926,41 @@ dependencies = [ "nvidia-nccl-cu12==2.21.5; platform_system == \"Linux\" and platform_machine == \"x86_64\"", "nvidia-nvjitlink-cu12==12.4.127; platform_system == \"Linux\" and platform_machine == \"x86_64\"", "nvidia-nvtx-cu12==12.4.127; platform_system == \"Linux\" and platform_machine == \"x86_64\"", + "setuptools; python_version >= \"3.12\"", "sympy==1.13.1; python_version >= \"3.9\"", "triton==3.2.0; platform_system == \"Linux\" and platform_machine == \"x86_64\"", "typing-extensions>=4.10.0", ] files = [ {file = "torch-2.6.0-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:7979834102cd5b7a43cc64e87f2f3b14bd0e1458f06e9f88ffa386d07c7446e1"}, + {file = "torch-2.6.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:ccbd0320411fe1a3b3fec7b4d3185aa7d0c52adac94480ab024b5c8f74a0bf1d"}, + {file = "torch-2.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:46763dcb051180ce1ed23d1891d9b1598e07d051ce4c9d14307029809c4d64f7"}, + {file = "torch-2.6.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:94fc63b3b4bedd327af588696559f68c264440e2503cc9e6954019473d74ae21"}, + {file = "torch-2.6.0-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:2bb8987f3bb1ef2675897034402373ddfc8f5ef0e156e2d8cfc47cacafdda4a9"}, + {file = "torch-2.6.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b789069020c5588c70d5c2158ac0aa23fd24a028f34a8b4fcb8fcb4d7efcf5fb"}, + {file = "torch-2.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:7e1448426d0ba3620408218b50aa6ada88aeae34f7a239ba5431f6c8774b1239"}, + {file = "torch-2.6.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:9a610afe216a85a8b9bc9f8365ed561535c93e804c2a317ef7fabcc5deda0989"}, + {file = "torch-2.6.0-cp313-cp313-manylinux1_x86_64.whl", hash = "sha256:4874a73507a300a5d089ceaff616a569e7bb7c613c56f37f63ec3ffac65259cf"}, + {file = "torch-2.6.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a0d5e1b9874c1a6c25556840ab8920569a7a4137afa8a63a32cee0bc7d89bd4b"}, + {file = "torch-2.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:510c73251bee9ba02ae1cb6c9d4ee0907b3ce6020e62784e2d7598e0cfa4d6cc"}, + {file = "torch-2.6.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:ff96f4038f8af9f7ec4231710ed4549da1bdebad95923953a25045dcf6fd87e2"}, ] [[package]] name = "torchmetrics" -version = "1.4.1" -requires_python = ">=3.8" +version = "1.6.2" +requires_python = ">=3.9" summary = "PyTorch native Metrics" +groups = ["default"] dependencies = [ "lightning-utilities>=0.8.0", "numpy>1.20.0", "packaging>17.1", - "torch>=1.10.0", + "torch>=2.0.0", ] files = [ - {file = "torchmetrics-1.4.1-py3-none-any.whl", hash = "sha256:c2e7cd56dd8bdc60ae63d712f3bdce649f23bd174d9180bdd0b746e0230b865a"}, + {file = "torchmetrics-1.6.2-py3-none-any.whl", hash = "sha256:586b970aff33a2154bfb6ed4539a557e9a268b6dce408bbdef0fec99fef3c78b"}, + {file = "torchmetrics-1.6.2.tar.gz", hash = "sha256:a3fa6372dbf01183d0f6fda2159e9526fb62818aa3630660909c290425f67df6"}, ] [[package]] @@ -2060,31 +2968,51 @@ name = "torchvision" version = "0.21.0" requires_python = ">=3.9" summary = "image and video datasets and models for torch deep learning" +groups = ["default"] dependencies = [ "numpy", "pillow!=8.3.*,>=5.3.0", "torch==2.6.0", ] files = [ + {file = "torchvision-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110d115333524d60e9e474d53c7d20f096dbd8a080232f88dddb90566f90064c"}, {file = "torchvision-0.21.0-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:3891cd086c5071bda6b4ee9d266bb2ac39c998c045c2ebcd1e818b8316fb5d41"}, + {file = "torchvision-0.21.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:54454923a50104c66a9ab6bd8b73a11c2fc218c964b1006d5d1fe5b442c3dcb6"}, + {file = "torchvision-0.21.0-cp311-cp311-win_amd64.whl", hash = "sha256:49bcfad8cfe2c27dee116c45d4f866d7974bcf14a5a9fbef893635deae322f2f"}, + {file = "torchvision-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:97a5814a93c793aaf0179cfc7f916024f4b63218929aee977b645633d074a49f"}, + {file = "torchvision-0.21.0-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:b578bcad8a4083b40d34f689b19ca9f7c63e511758d806510ea03c29ac568f7b"}, + {file = "torchvision-0.21.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5083a5b1fec2351bf5ea9900a741d54086db75baec4b1d21e39451e00977f1b1"}, + {file = "torchvision-0.21.0-cp312-cp312-win_amd64.whl", hash = "sha256:6eb75d41e3bbfc2f7642d0abba9383cc9ae6c5a4ca8d6b00628c225e1eaa63b3"}, + {file = "torchvision-0.21.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:659b76c86757cb2ee4ca2db245e0740cfc3081fef46f0f1064d11adb4a8cee31"}, + {file = "torchvision-0.21.0-cp313-cp313-manylinux1_x86_64.whl", hash = "sha256:084ac3f5a1f50c70d630a488d19bf62f323018eae1b1c1232f2b7047d3a7b76d"}, + {file = "torchvision-0.21.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5045a3a5f21ec3eea6962fa5f2fa2d4283f854caec25ada493fcf4aab2925467"}, + {file = "torchvision-0.21.0-cp313-cp313-win_amd64.whl", hash = "sha256:9147f5e096a9270684e3befdee350f3cacafd48e0c54ab195f45790a9c146d67"}, ] [[package]] name = "tqdm" -version = "4.66.5" +version = "4.67.1" requires_python = ">=3.7" summary = "Fast, Extensible Progress Meter" +groups = ["default"] +dependencies = [ + "colorama; platform_system == \"Windows\"", +] files = [ - {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, - {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, + {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, + {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, ] [[package]] name = "triton" version = "3.2.0" summary = "A language and compiler for custom Deep Learning operations" +groups = ["default"] +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "triton-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8009a1fb093ee8546495e96731336a33fb8856a38e45bb4ab6affd6dbc3ba220"}, + {file = "triton-3.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d9b215efc1c26fa7eefb9a157915c92d52e000d2bf83e5f69704047e63f125c"}, + {file = "triton-3.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5dfa23ba84541d7c0a531dfce76d8bcd19159d50a4a8b14ad01e91734a5c1b0"}, ] [[package]] @@ -2092,6 +3020,7 @@ name = "typing-extensions" version = "4.12.2" requires_python = ">=3.8" summary = "Backported and Experimental Type Hints for Python 3.8+" +groups = ["default", "dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -2099,37 +3028,53 @@ files = [ [[package]] name = "tzdata" -version = "2024.1" +version = "2025.1" requires_python = ">=2" summary = "Provider of IANA time zone data" +groups = ["default"] files = [ - {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, - {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, + {file = "tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639"}, + {file = "tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694"}, ] [[package]] name = "urllib3" -version = "2.2.2" -requires_python = ">=3.8" +version = "2.3.0" +requires_python = ">=3.9" summary = "HTTP library with thread-safe connection pooling, file post, and more." +groups = ["default"] files = [ - {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, - {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, + {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, + {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, ] [[package]] name = "virtualenv" -version = "20.26.3" -requires_python = ">=3.7" +version = "20.29.3" +requires_python = ">=3.8" summary = "Virtual Python Environment builder" +groups = ["dev"] dependencies = [ "distlib<1,>=0.3.7", "filelock<4,>=3.12.2", + "importlib-metadata>=6.6; python_version < \"3.8\"", "platformdirs<5,>=3.9.1", ] files = [ - {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, - {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, + {file = "virtualenv-20.29.3-py3-none-any.whl", hash = "sha256:3e3d00f5807e83b234dfb6122bf37cfadf4be216c53a49ac059d02414f819170"}, + {file = "virtualenv-20.29.3.tar.gz", hash = "sha256:95e39403fcf3940ac45bc717597dba16110b74506131845d9b687d5e73d947ac"}, +] + +[[package]] +name = "waitress" +version = "3.0.2" +requires_python = ">=3.9.0" +summary = "Waitress WSGI server" +groups = ["default"] +marker = "platform_system == \"Windows\"" +files = [ + {file = "waitress-3.0.2-py3-none-any.whl", hash = "sha256:c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e"}, + {file = "waitress-3.0.2.tar.gz", hash = "sha256:682aaaf2af0c44ada4abfb70ded36393f0e307f4ab9456a215ce0020baefc31f"}, ] [[package]] @@ -2137,6 +3082,7 @@ name = "websocket-client" version = "1.8.0" requires_python = ">=3.8" summary = "WebSocket client for Python with low level API options" +groups = ["default"] files = [ {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"}, @@ -2144,97 +3090,144 @@ files = [ [[package]] name = "werkzeug" -version = "3.0.4" -requires_python = ">=3.8" +version = "3.1.3" +requires_python = ">=3.9" summary = "The comprehensive WSGI web application library." +groups = ["default"] dependencies = [ "MarkupSafe>=2.1.1", ] files = [ - {file = "werkzeug-3.0.4-py3-none-any.whl", hash = "sha256:02c9eb92b7d6c06f31a782811505d2157837cea66aaede3e217c7c27c039476c"}, - {file = "werkzeug-3.0.4.tar.gz", hash = "sha256:34f2371506b250df4d4f84bfe7b0921e4762525762bbd936614909fe25cd7306"}, + {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, + {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, ] [[package]] name = "wrapt" -version = "1.16.0" -requires_python = ">=3.6" +version = "1.17.2" +requires_python = ">=3.8" summary = "Module for decorators, wrappers and monkey patching." -files = [ - {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, - {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, - {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, - {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, - {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, - {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, - {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, +groups = ["default"] +files = [ + {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58"}, + {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda"}, + {file = "wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438"}, + {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a"}, + {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000"}, + {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6"}, + {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b"}, + {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662"}, + {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72"}, + {file = "wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317"}, + {file = "wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3"}, + {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925"}, + {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392"}, + {file = "wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40"}, + {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d"}, + {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b"}, + {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98"}, + {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82"}, + {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae"}, + {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9"}, + {file = "wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9"}, + {file = "wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991"}, + {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125"}, + {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998"}, + {file = "wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5"}, + {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8"}, + {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6"}, + {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc"}, + {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2"}, + {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b"}, + {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504"}, + {file = "wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a"}, + {file = "wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845"}, + {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192"}, + {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b"}, + {file = "wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0"}, + {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306"}, + {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb"}, + {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681"}, + {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6"}, + {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6"}, + {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f"}, + {file = "wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555"}, + {file = "wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c"}, + {file = "wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8"}, + {file = "wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3"}, ] [[package]] name = "yarl" -version = "1.9.4" -requires_python = ">=3.7" +version = "1.18.3" +requires_python = ">=3.9" summary = "Yet another URL library" +groups = ["default"] dependencies = [ "idna>=2.0", "multidict>=4.0", -] -files = [ - {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, - {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, - {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, - {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, - {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, - {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, - {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, + "propcache>=0.2.0", +] +files = [ + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, + {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, + {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, + {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, + {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, + {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, + {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, + {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, + {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, ] [[package]] name = "zipp" -version = "3.20.1" -requires_python = ">=3.8" +version = "3.21.0" +requires_python = ">=3.9" summary = "Backport of pathlib-compatible object wrapper for zip files" +groups = ["default"] files = [ - {file = "zipp-3.20.1-py3-none-any.whl", hash = "sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064"}, - {file = "zipp-3.20.1.tar.gz", hash = "sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b"}, + {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, + {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] diff --git a/pyproject.toml b/pyproject.toml index 64d0f5c..bd5e1e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,21 +1,22 @@ [project] -name = "project-name" +name = "Stain Normalization" version = "0.1.0" -authors = [] +authors = ["Adam Lopatka"] requires-python = ">=3.11" readme = "README.md" license = { file = "LICENSE" } dependencies = [ - "torch>=2.4.0", - "lightning>=2.4.0", - "torchmetrics>=1.4.1", + "lightning>=2.0.0", + "albumentations>=1.4.14", + "torchmetrics>=1.4.14", + "torchvision>=0.17.2", + "torch>=2.2.2", + "tqdm>=4.66.5", "rationai-mlkit @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/mlkit.git", "rationai-masks @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git", "rationai-tiling @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git", - "albumentations>=2.0.4", - "scikit-learn>=1.6.1", - "torchvision>=0.21.0", "rationai-kube-jobs @ git+ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/kube-jobs.git", + "scikit-image>=0.25.2" ] [tool.pdm.dev-dependencies] @@ -25,19 +26,19 @@ dev = ["mypy", "pre-commit", "ruff"] mask_generate = "python preprocessing/mask_generator.py" tiler = "python preprocessing/tiler.py" -train = "python -m project_name mode=fit" -validate = "python -m project_name mode=validate" -test = "python -m project_name mode=test" -predict = "python -m project_name mode=predict" +train = "python -m stain_normalization mode=fit" +validate = "python -m stain_normalization mode=validate" +test = "python -m stain_normalization mode=test" +predict = "python -m stain_normalization mode=predict" l = { composite = ["lint", "format", "mypy"] } lint = "ruff check --fix" format = "ruff format" mypy = "mypy ." -post_install = { composite = [ - "pre-commit autoupdate", - "pre-commit install", - "pre-commit install --hook-type commit-msg", -] } +# post_install = { composite = [ +# "pre-commit autoupdate", +# "pre-commit install", +# "pre-commit install --hook-type commit-msg", +#] } [tool.pdm] distribution = false diff --git a/stain_normalization/main.py b/stain_normalization/__main__.py similarity index 100% rename from stain_normalization/main.py rename to stain_normalization/__main__.py diff --git a/stain_normalization/data/datasets/stain_normalization.py b/stain_normalization/data/datasets/stain_normalization.py index e2468c3..3d9dbb1 100644 --- a/stain_normalization/data/datasets/stain_normalization.py +++ b/stain_normalization/data/datasets/stain_normalization.py @@ -1,21 +1,21 @@ from collections.abc import Iterable import pandas as pd -from albumentations import TransformType +from albumentations import Transform3D from albumentations.pytorch import ToTensorV2 from rationai.mlkit.data.datasets import MetaTiledSlides, OpenSlideTilesDataset from torch.utils.data import Dataset -from stain_normalization.typing import Metadata, PredictSample, Sample +from stain_normalization.typing import PredictSample, Sample class StainNormalization(MetaTiledSlides[Sample]): def __init__( self, uris: Iterable[str], - modify:TransformType, - transforms: TransformType | None = None, - + modify: Transform3D, + transforms: Transform3D | None = None, + ) -> None: self.modify = modify self.transforms = transforms @@ -38,8 +38,8 @@ class StainNormalizationPredict(MetaTiledSlides[PredictSample]): def __init__( self, uris: Iterable[str], - modify:TransformType, - transforms: TransformType | None = None, + modify: Transform3D, + transforms: Transform3D | None = None, ) -> None: self.modify = modify @@ -65,8 +65,8 @@ def __init__( slide_metadata: pd.Series, tiles: pd.DataFrame, include_target: bool, - transforms: TransformType | None = None, - modify:TransformType | None = None, + modify: Transform3D, + transforms: Transform3D | None = None, ) -> None: super().__init__() self.slide_tiles = OpenSlideTilesDataset( @@ -90,8 +90,8 @@ def __getitem__(self, idx: int) -> Sample | PredictSample: if self.transforms is not None: original_image = self.transforms(image=original_image)["image"] - - modified_image = self.modify(original_image)["image"] + + modified_image = self.modify(image=original_image)["image"] # modification_name = "Original" # if self.modify: diff --git a/stain_normalization/modeling/__init__.py b/stain_normalization/modeling/__init__.py index 6e52155..9ae4c05 100644 --- a/stain_normalization/modeling/__init__.py +++ b/stain_normalization/modeling/__init__.py @@ -1,3 +1,2 @@ -from . import metrics from .l1ssim_loss import L1SSIMLoss __all__ = ["L1SSIMLoss", "metrics"] \ No newline at end of file diff --git a/stain_normalization/modeling/backbone/vgg16.py b/stain_normalization/modeling/backbone/vgg16.py index b621ed7..0864515 100644 --- a/stain_normalization/modeling/backbone/vgg16.py +++ b/stain_normalization/modeling/backbone/vgg16.py @@ -1,5 +1,5 @@ from torch import nn -from torchvision +import torchvision def vgg16(weights: str | None = None) -> nn.Module: return torchvision.models.vgg16(weights=weights).features diff --git a/stain_normalization/stain_normalization_model.py b/stain_normalization/stain_normalization_model.py index 19e626f..7aca27c 100644 --- a/stain_normalization/stain_normalization_model.py +++ b/stain_normalization/stain_normalization_model.py @@ -1,14 +1,12 @@ -from collections import defaultdict - -import torch from lightning import LightningModule from torch import Tensor, nn from torch.optim.optimizer import Optimizer from stain_normalization.modeling import L1SSIMLoss from stain_normalization.typing import Input, Outputs from torch.optim import Adam -from torch.optim.optimizer import Optimizer -from torchmetrics import PeakSignalNoiseRatio,StructuralSimilarityIndexMeasure, MetricCollection +from torch.optim.optimizer import Optimizer +from torchmetrics.image import PeakSignalNoiseRatio,StructuralSimilarityIndexMeasure +from torchmetrics import MetricCollection class StainNormalizationModel(LightningModule): def __init__(self, backbone: nn.Module, decode_head: nn.Module) -> None: From 9c6aad7538bda0a5696b0dd17ecf5f594056dd50 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 7 Mar 2025 01:11:07 +0000 Subject: [PATCH 30/85] chore: job config --- configs/default.yaml | 2 +- job.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/configs/default.yaml b/configs/default.yaml index 42230e2..749d17b 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -30,7 +30,7 @@ data: metadata: user: xlopatka experiment_name: Stain-Normalization - run_name: Testing #First Train Stain Normalization + run_name: First Train Stain Normalization description: Testing Stain Normalization hyperparams: {} diff --git a/job.py b/job.py index 20b4bf4..1064fdd 100644 --- a/job.py +++ b/job.py @@ -2,17 +2,17 @@ submit_job( - job_name="some name", - username="your name", + job_name="Stain Normalization Train", + username="xlopatka", cpu=10, memory="128Gi", gpu="A40", script=[ - "git clone https://gitlab.ics.muni.cz/rationai/digital-pathology/... workdir", - "cd workdir", - "git checkout develop", + "git clone https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git", + "cd staining", + "git checkout feature/ml-stain-normalization", "pdm sync --skip=post_install", - "pdm fit model/backbone=resnet18", + "pdm train", ], storage=Storage(mou=True), ) From a593c298b7d126f90aa67d102f13e8596946b0fb Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 7 Mar 2025 09:11:39 +0000 Subject: [PATCH 31/85] fix: job run parametrs --- configs/default.yaml | 2 +- job.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/default.yaml b/configs/default.yaml index 749d17b..6cefd09 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -24,7 +24,7 @@ trainer: mode: min data: - batch_size: 256 + batch_size: 126 num_workers: 8 metadata: diff --git a/job.py b/job.py index 1064fdd..69d00ae 100644 --- a/job.py +++ b/job.py @@ -2,11 +2,11 @@ submit_job( - job_name="Stain Normalization Train", + job_name="stain-normalization-train", username="xlopatka", cpu=10, memory="128Gi", - gpu="A40", + gpu="H100", script=[ "git clone https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git", "cd staining", From 4c9e89d46e6748dedf9432ac50bf371c43c59560 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 7 Mar 2025 09:17:34 +0000 Subject: [PATCH 32/85] fix: lower batch size --- configs/default.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/default.yaml b/configs/default.yaml index 6cefd09..a99ca43 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -24,7 +24,7 @@ trainer: mode: min data: - batch_size: 126 + batch_size: 64 num_workers: 8 metadata: From b4df4e3ba4bd3e4a24dbc66295595cc76a0b425a Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 7 Mar 2025 09:22:46 +0000 Subject: [PATCH 33/85] fix: lower workers --- configs/default.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/default.yaml b/configs/default.yaml index a99ca43..f716551 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -25,7 +25,7 @@ trainer: data: batch_size: 64 - num_workers: 8 + num_workers: 4 metadata: user: xlopatka From 4af7ac1d3b1a2e7badbbad1adfbe5e99d894574f Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 7 Mar 2025 09:42:18 +0000 Subject: [PATCH 34/85] fix: ensure float32 tensors --- stain_normalization/data/datasets/stain_normalization.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stain_normalization/data/datasets/stain_normalization.py b/stain_normalization/data/datasets/stain_normalization.py index 3d9dbb1..4205e0f 100644 --- a/stain_normalization/data/datasets/stain_normalization.py +++ b/stain_normalization/data/datasets/stain_normalization.py @@ -105,8 +105,8 @@ def __getitem__(self, idx: int) -> Sample | PredictSample: # modification=modification_name # ) - original_image = self.to_tensor(image=original_image)["image"] - modified_image = self.to_tensor(image=modified_image)["image"] + original_image = self.to_tensor(image=original_image)["image"].float() + modified_image = self.to_tensor(image=modified_image)["image"].float() if self.include_target: return modified_image, original_image From 07a4002bb24119f8c01849d51997809c6f9f40cc Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 7 Mar 2025 09:48:22 +0000 Subject: [PATCH 35/85] fix: missing () --- stain_normalization/stain_normalization_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stain_normalization/stain_normalization_model.py b/stain_normalization/stain_normalization_model.py index 7aca27c..7487419 100644 --- a/stain_normalization/stain_normalization_model.py +++ b/stain_normalization/stain_normalization_model.py @@ -12,7 +12,7 @@ class StainNormalizationModel(LightningModule): def __init__(self, backbone: nn.Module, decode_head: nn.Module) -> None: super().__init__() self.backbone = backbone - self.decode_head = decode_head + self.decode_head = decode_head() self.criterion = L1SSIMLoss() self.val_metrics = MetricCollection( From 1bc166cf300f5ed9b6d302df36dde5c6208ba6ff Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 7 Mar 2025 09:52:45 +0000 Subject: [PATCH 36/85] fix: decode_head settings --- configs/model/decode_head/unet2048.yaml | 5 ++--- configs/model/decode_head/unet512.yaml | 5 ++--- stain_normalization/stain_normalization_model.py | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/configs/model/decode_head/unet2048.yaml b/configs/model/decode_head/unet2048.yaml index 00a4921..dce8b92 100644 --- a/configs/model/decode_head/unet2048.yaml +++ b/configs/model/decode_head/unet2048.yaml @@ -1,4 +1,3 @@ -decode_head: - _target_: stain_normalization.modeling.decode_head.UNetDecoder - in_channels: 2048 +_target_: stain_normalization.modeling.decode_head.UNetDecoder +in_channels: 2048 diff --git a/configs/model/decode_head/unet512.yaml b/configs/model/decode_head/unet512.yaml index 531bf9f..6442711 100644 --- a/configs/model/decode_head/unet512.yaml +++ b/configs/model/decode_head/unet512.yaml @@ -1,4 +1,3 @@ -decode_head: - _target_: stain_normalization.modeling.decode_head.UNetDecoder - in_channels: 512 +_target_: stain_normalization.modeling.decode_head.UNetDecoder +in_channels: 512 diff --git a/stain_normalization/stain_normalization_model.py b/stain_normalization/stain_normalization_model.py index 7487419..7aca27c 100644 --- a/stain_normalization/stain_normalization_model.py +++ b/stain_normalization/stain_normalization_model.py @@ -12,7 +12,7 @@ class StainNormalizationModel(LightningModule): def __init__(self, backbone: nn.Module, decode_head: nn.Module) -> None: super().__init__() self.backbone = backbone - self.decode_head = decode_head() + self.decode_head = decode_head self.criterion = L1SSIMLoss() self.val_metrics = MetricCollection( From c258af2b605645e497cf849e302be8a2edfa6b5a Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 7 Mar 2025 14:22:41 +0000 Subject: [PATCH 37/85] chore: config change --- configs/default.yaml | 2 +- job.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/configs/default.yaml b/configs/default.yaml index f716551..5b71b24 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -24,7 +24,7 @@ trainer: mode: min data: - batch_size: 64 + batch_size: 128 num_workers: 4 metadata: diff --git a/job.py b/job.py index 69d00ae..6da6dee 100644 --- a/job.py +++ b/job.py @@ -6,7 +6,8 @@ username="xlopatka", cpu=10, memory="128Gi", - gpu="H100", + gpu="A40", + shm="16384Mi", script=[ "git clone https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git", "cd staining", From 8d8190d9d496ef392a950e37f075495997cf2b78 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Sun, 9 Mar 2025 00:32:15 +0000 Subject: [PATCH 38/85] fix: job settign optimalization --- configs/default.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/default.yaml b/configs/default.yaml index 5b71b24..5fd58bb 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -25,7 +25,7 @@ trainer: data: batch_size: 128 - num_workers: 4 + num_workers: 8 metadata: user: xlopatka From d9e331932cca9791197fad7ac9ed2882dc431d1a Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Thu, 13 Mar 2025 03:05:00 +0000 Subject: [PATCH 39/85] fix: image normalization --- .../datasets/stain_normalization/predict.yaml | 5 +-- .../datasets/stain_normalization/test.yaml | 5 +-- .../datasets/stain_normalization/train.yaml | 1 + .../datasets/stain_normalization/val.yaml | 3 +- configs/data/normalize/default.yaml | 10 ++++++ configs/default.yaml | 2 +- .../data/datasets/stain_normalization.py | 34 +++++++++++-------- 7 files changed, 40 insertions(+), 20 deletions(-) create mode 100644 configs/data/normalize/default.yaml diff --git a/configs/data/datasets/stain_normalization/predict.yaml b/configs/data/datasets/stain_normalization/predict.yaml index 7832a66..5da9773 100644 --- a/configs/data/datasets/stain_normalization/predict.yaml +++ b/configs/data/datasets/stain_normalization/predict.yaml @@ -1,6 +1,7 @@ defaults: - - /data/transforms@transforms: test + # - /data/transforms@transforms: test - /data/modify@modify: test - + - /data/normalize@normalize: default + _target_: stain_normalization.data.datasets.StainNormalizationPredict uris: ["mlflow-artifacts:"] diff --git a/configs/data/datasets/stain_normalization/test.yaml b/configs/data/datasets/stain_normalization/test.yaml index 78dabba..4904032 100644 --- a/configs/data/datasets/stain_normalization/test.yaml +++ b/configs/data/datasets/stain_normalization/test.yaml @@ -1,6 +1,7 @@ defaults: - - /data/transforms@transforms: test + # - /data/transforms@transforms: test - /data/modify@modify: test - + - /data/normalize@normalize: default + _target_: stain_normalization.data.datasets.StainNormalization uris: ["mlflow-artifacts:/79/bc7dfc2258164cd292091c3056609277/artifacts/Stain Normalization - test"] diff --git a/configs/data/datasets/stain_normalization/train.yaml b/configs/data/datasets/stain_normalization/train.yaml index 5a7feec..dfa39eb 100644 --- a/configs/data/datasets/stain_normalization/train.yaml +++ b/configs/data/datasets/stain_normalization/train.yaml @@ -1,6 +1,7 @@ defaults: # - /data/transforms@transforms: train - /data/modify@modify: train + - /data/normalize@normalize: default _target_: stain_normalization.data.datasets.StainNormalization uris: ["mlflow-artifacts:/79/bc7dfc2258164cd292091c3056609277/artifacts/Stain Normalization - train"] diff --git a/configs/data/datasets/stain_normalization/val.yaml b/configs/data/datasets/stain_normalization/val.yaml index 142cfb8..860ea53 100644 --- a/configs/data/datasets/stain_normalization/val.yaml +++ b/configs/data/datasets/stain_normalization/val.yaml @@ -1,6 +1,7 @@ defaults: - - /data/transforms@transforms: test + # - /data/transforms@transforms: test - /data/modify@modify: test + - /data/normalize@normalize: default _target_: stain_normalization.data.datasets.StainNormalization uris: ["mlflow-artifacts:/79/bc7dfc2258164cd292091c3056609277/artifacts/Stain Normalization - val"] diff --git a/configs/data/normalize/default.yaml b/configs/data/normalize/default.yaml new file mode 100644 index 0000000..08d72da --- /dev/null +++ b/configs/data/normalize/default.yaml @@ -0,0 +1,10 @@ +_target_: albumentations.Normalize +mean: + - 0.780361961 + - 0.614529804 + - 0.725567843 +std: + - 0.144428627 + - 0.183275686 + - 0.140768627 +max_pixel_value: 1 diff --git a/configs/default.yaml b/configs/default.yaml index 5fd58bb..998ba31 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -30,7 +30,7 @@ data: metadata: user: xlopatka experiment_name: Stain-Normalization - run_name: First Train Stain Normalization + run_name: Second Train Stain Normalization description: Testing Stain Normalization hyperparams: {} diff --git a/stain_normalization/data/datasets/stain_normalization.py b/stain_normalization/data/datasets/stain_normalization.py index 4205e0f..6c6f354 100644 --- a/stain_normalization/data/datasets/stain_normalization.py +++ b/stain_normalization/data/datasets/stain_normalization.py @@ -1,5 +1,6 @@ from collections.abc import Iterable +import numpy as np import pandas as pd from albumentations import Transform3D from albumentations.pytorch import ToTensorV2 @@ -14,11 +15,13 @@ def __init__( self, uris: Iterable[str], modify: Transform3D, + normalize: Transform3D | None = None, transforms: Transform3D | None = None, ) -> None: self.modify = modify self.transforms = transforms + self.normalize = normalize super().__init__(uris=uris) def generate_datasets(self) -> Iterable[Dataset[Sample]]: @@ -28,6 +31,7 @@ def generate_datasets(self) -> Iterable[Dataset[Sample]]: tiles=self.filter_tiles_by_slide(slide["id"]), include_target=True, modify=self.modify, + normalize=self.normalize, transforms=self.transforms, ) for _, slide in self.slides.iterrows() @@ -39,10 +43,12 @@ def __init__( self, uris: Iterable[str], modify: Transform3D, + normalize: Transform3D | None = None, transforms: Transform3D | None = None, ) -> None: self.modify = modify + self.normalize = normalize self.transforms = transforms super().__init__(uris=uris) @@ -53,6 +59,7 @@ def generate_datasets(self) -> Iterable[Dataset[PredictSample]]: tiles=self.filter_tiles_by_slide(slide["id"]), include_target=False, modify=self.modify, + normalize=self.normalize, transforms=self.transforms, ) for _, slide in self.slides.iterrows() @@ -66,6 +73,7 @@ def __init__( tiles: pd.DataFrame, include_target: bool, modify: Transform3D, + normalize: Transform3D | None = None, transforms: Transform3D | None = None, ) -> None: super().__init__() @@ -76,38 +84,36 @@ def __init__( tile_extent_y=slide_metadata.tile_extent_y, tiles=tiles, ) + self.modify = modify + self.normalize = normalize self.transforms = transforms self.include_target = include_target self.to_tensor = ToTensorV2() - self.modify = modify def __len__(self) -> int: return len(self.slide_tiles) def __getitem__(self, idx: int) -> Sample | PredictSample: original_image = self.slide_tiles[idx] - + # This is not distruptive transform as rotate, flip if self.transforms is not None: original_image = self.transforms(image=original_image)["image"] + # Create "wrong" image to use as input. Outputs image in float 0-1 modified_image = self.modify(image=original_image)["image"] + + if np.max(original_image) > 1.0: + original_image = original_image / 255.0 - # modification_name = "Original" - # if self.modify: - # out = self.modify(original_image) - # modified_image = out["image"] - # modification_name = out["modification_name"] - # metadata = Metadata( - # slide=self.slide_tiles.slide_path.stem, - # x=self.slide_tiles.tiles.iloc[idx]["x"], - # y=self.slide_tiles.tiles.iloc[idx]["y"], - # modification=modification_name - # ) + if self.normalize: + original_image = self.normalize(image=original_image)["image"] + modified_image = self.normalize(image=modified_image)["image"] original_image = self.to_tensor(image=original_image)["image"].float() modified_image = self.to_tensor(image=modified_image)["image"].float() - + + if self.include_target: return modified_image, original_image From 986667d07b43cb6cdba45e00463f7fadba34f0e2 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Sun, 23 Mar 2025 01:53:42 +0000 Subject: [PATCH 40/85] feat: tiles export --- pdm.lock | 30 ++++++++- pyproject.toml | 3 +- stain_normalization/__main__.py | 5 +- stain_normalization/callbacks/tiles_export.py | 62 +++++++++++++++++++ stain_normalization/data/data_module.py | 14 +++++ .../data/datasets/stain_normalization.py | 18 +++--- stain_normalization/data/utils/__init__.py | 4 ++ stain_normalization/data/utils/collate_fn.py | 6 ++ .../stain_normalization_model.py | 4 ++ stain_normalization/typing.py | 4 +- 10 files changed, 134 insertions(+), 16 deletions(-) create mode 100644 stain_normalization/callbacks/tiles_export.py create mode 100644 stain_normalization/data/utils/__init__.py create mode 100644 stain_normalization/data/utils/collate_fn.py diff --git a/pdm.lock b/pdm.lock index 57e651c..282e879 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:b6548b53106f41b522b88929821234d14bbde40c39c47dafe1e38851ca0ec4e9" +content_hash = "sha256:5332e3a0a1aebc8df232aacd3e71bbc5bdbaeec0efddc8d0137eafb3cd6c2c32" [[metadata.targets]] requires_python = ">=3.11" @@ -1730,6 +1730,20 @@ files = [ {file = "opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca"}, ] +[[package]] +name = "openslide-bin" +version = "4.0.0.6" +requires_python = ">=3.8" +summary = "Binary build of OpenSlide" +groups = ["default"] +files = [ + {file = "openslide-bin-4.0.0.6.tar.gz", hash = "sha256:baca4a590cb15c8685f1ee1905d496bb0a23468b969f7066a2126c24c9e9ef39"}, + {file = "openslide_bin-4.0.0.6-py3-none-macosx_11_0_universal2.whl", hash = "sha256:f9c4ed0da275202dce531b163d92e1b860d215e779e7620fdd0dd92b7c40764b"}, + {file = "openslide_bin-4.0.0.6-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:43af7aabab7aa6d7c21573736de8b59ccf2f4078f0ae26da70d86005d2ee662b"}, + {file = "openslide_bin-4.0.0.6-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:0bc2e6d624781b0ba24d2ef3d8623fd52de7645520911539e1279d599212ecb0"}, + {file = "openslide_bin-4.0.0.6-py3-none-win_amd64.whl", hash = "sha256:d9bb0659bce0384f6f961aa64c0cfdb882c8dacd6f4fbfaf84895e06fe571f40"}, +] + [[package]] name = "openslide-python" version = "1.4.1" @@ -1900,6 +1914,17 @@ files = [ {file = "pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20"}, ] +[[package]] +name = "pkgconfig" +version = "1.5.5" +requires_python = ">=3.3,<4.0" +summary = "Interface Python with pkg-config" +groups = ["default"] +files = [ + {file = "pkgconfig-1.5.5-py3-none-any.whl", hash = "sha256:d20023bbeb42ee6d428a0fac6e0904631f545985a10cdd71a20aa58bc47a4209"}, + {file = "pkgconfig-1.5.5.tar.gz", hash = "sha256:deb4163ef11f75b520d822d9505c1f462761b4309b1bb713d08689759ea8b899"}, +] + [[package]] name = "platformdirs" version = "4.3.6" @@ -2223,10 +2248,11 @@ files = [ [[package]] name = "pyvips" version = "2.2.3" -summary = "binding for the libvips image processing library, ABI mode" +summary = "binding for the libvips image processing library, API mode" groups = ["default"] dependencies = [ "cffi>=1.0.0", + "pkgconfig", ] files = [ {file = "pyvips-2.2.3.tar.gz", hash = "sha256:43bceced0db492654c93008246a58a508e0373ae1621116b87b322f2ac72212f"}, diff --git a/pyproject.toml b/pyproject.toml index bd5e1e3..83dd1f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,8 @@ dependencies = [ "rationai-masks @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git", "rationai-tiling @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git", "rationai-kube-jobs @ git+ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/kube-jobs.git", - "scikit-image>=0.25.2" + "scikit-image>=0.25.2", + "openslide-bin>=4.0.0.6" ] [tool.pdm.dev-dependencies] diff --git a/stain_normalization/__main__.py b/stain_normalization/__main__.py index 24659aa..c4e1155 100644 --- a/stain_normalization/__main__.py +++ b/stain_normalization/__main__.py @@ -6,6 +6,7 @@ from omegaconf import DictConfig, OmegaConf from rationai.mlkit import Trainer, autolog +from stain_normalization.callbacks.tiles_export import TilesExport from stain_normalization.data import DataModule from stain_normalization.stain_normalization_model import StainNormalizationModel @@ -27,7 +28,9 @@ def main(config: DictConfig, logger: Logger | None) -> None: ) model = hydra.utils.instantiate(config.model, _target_=StainNormalizationModel) - trainer = hydra.utils.instantiate(config.trainer, _target_=Trainer, logger=logger) + tile_export_callbeck = TilesExport(config.output_dir, config.data.predict.normalize) + + trainer = hydra.utils.instantiate(config.trainer, _target_=Trainer, logger=logger, callbacks=[tile_export_callbeck]) getattr(trainer, config.mode)(model, datamodule=data, ckpt_path=config.checkpoint) diff --git a/stain_normalization/callbacks/tiles_export.py b/stain_normalization/callbacks/tiles_export.py new file mode 100644 index 0000000..7166dcd --- /dev/null +++ b/stain_normalization/callbacks/tiles_export.py @@ -0,0 +1,62 @@ +from pathlib import Path +from lightning import Callback, LightningModule, Trainer +from PIL import Image +from omegaconf import DictConfig +import torch + + +class TilesExport(Callback): + def __init__(self, output_dir: str | Path, predict_normalization_config: DictConfig) -> None: + super().__init__() + self.output_dir = Path(output_dir) + self.output_dir.mkdir(parents=True, exist_ok=True) + + # Extract normalization parameters from the config (predict normalization) + normalization = predict_normalization_config + self.mean = torch.tensor(normalization.mean).view(3, 1, 1) + self.std = torch.tensor(normalization.std).view(3, 1, 1) + + + def denormalize(self, tensor: torch.Tensor) -> torch.Tensor: + device = tensor.device + + std = self.std.to(device) + mean = self.mean.to(device) + + return (tensor * std) + mean + + def tensor_to_image(self, tensor: torch.Tensor) -> Image.Image: + """Convert a PyTorch tensor to a PIL image .""" + tensor = self.denormalize(tensor) + tensor = tensor.clamp(0, 1) + tensor = (tensor * 255).byte() + return Image.fromarray(tensor.permute(1, 2, 0).cpu().numpy()) + + def on_predict_batch_end( + self, + trainer: Trainer, + pl_module: LightningModule, + outputs: list[torch.Tensor], + batch: tuple[torch.Tensor, list], + batch_idx: int, + dataloader_idx: int = 0, + ) -> None: + """ + Saves three images per sample: + - Original Image (ground truth) + - Modified Image (input to model) + - Predicted Image (model output) + """ + for b in range(len(outputs)): + predicted_image = outputs[b] + original_image = Image.fromarray(batch[1][b]["original_image"].astype("uint8")) + modified_image = Image.fromarray((batch[1][b]["modified_image"] * 255).astype("uint8")) + index = batch[1][b]["index"] + + predicted_image = self.tensor_to_image(predicted_image) + + # Save images + original_image.save(self.output_dir / f"{index}_original.png") + modified_image.save(self.output_dir / f"{index}_modified.png") + predicted_image.save(self.output_dir / f"{index}_predicted.png") + diff --git a/stain_normalization/data/data_module.py b/stain_normalization/data/data_module.py index 40147f6..88a9138 100644 --- a/stain_normalization/data/data_module.py +++ b/stain_normalization/data/data_module.py @@ -1,4 +1,6 @@ +from typing import Any from collections.abc import Iterable +from torch import Tensor from hydra.utils import instantiate from lightning import LightningDataModule @@ -6,6 +8,7 @@ from torch.utils.data import DataLoader from stain_normalization.typing import Input +from stain_normalization.data.utils import collate_fn class DataModule(LightningDataModule): @@ -26,6 +29,9 @@ def setup(self, stage: str) -> None: self.val = instantiate(self.datasets["val"]) case "test": self.test = instantiate(self.datasets["test"]) + case "predict": + # this need predict val is for debuging only + self.predict = instantiate(self.datasets["predict"]) def train_dataloader(self) -> Iterable[Input]: return DataLoader( @@ -49,3 +55,11 @@ def test_dataloader(self) -> Iterable[Input]: return DataLoader( self.test, batch_size=self.batch_size, num_workers=self.num_workers ) + + def predict_dataloader(self) -> Iterable[tuple[Tensor, list[dict[str, Any]]]]: + return DataLoader( + self.predict, + batch_size=self.batch_size, + num_workers=self.num_workers, + collate_fn=collate_fn, + ) \ No newline at end of file diff --git a/stain_normalization/data/datasets/stain_normalization.py b/stain_normalization/data/datasets/stain_normalization.py index 6c6f354..64aa680 100644 --- a/stain_normalization/data/datasets/stain_normalization.py +++ b/stain_normalization/data/datasets/stain_normalization.py @@ -94,27 +94,25 @@ def __len__(self) -> int: return len(self.slide_tiles) def __getitem__(self, idx: int) -> Sample | PredictSample: - original_image = self.slide_tiles[idx] - + original_image_255 = self.slide_tiles[idx] # This is not distruptive transform as rotate, flip if self.transforms is not None: - original_image = self.transforms(image=original_image)["image"] + original_image_255 = self.transforms(image=original_image)["image"] # Create "wrong" image to use as input. Outputs image in float 0-1 - modified_image = self.modify(image=original_image)["image"] - - if np.max(original_image) > 1.0: - original_image = original_image / 255.0 + modified_image_raw = self.modify(image=original_image_255)["image"] + modified_image = modified_image_raw + original_image = original_image_255 / 255.0 if self.normalize: original_image = self.normalize(image=original_image)["image"] modified_image = self.normalize(image=modified_image)["image"] - original_image = self.to_tensor(image=original_image)["image"].float() - modified_image = self.to_tensor(image=modified_image)["image"].float() + original_image = self.to_tensor(image=original_image)["image"] + modified_image = self.to_tensor(image=modified_image)["image"] if self.include_target: return modified_image, original_image - return modified_image + return modified_image, {"original_image":original_image_255, "modified_image":modified_image_raw,"index":idx} diff --git a/stain_normalization/data/utils/__init__.py b/stain_normalization/data/utils/__init__.py new file mode 100644 index 0000000..dca47bc --- /dev/null +++ b/stain_normalization/data/utils/__init__.py @@ -0,0 +1,4 @@ +from stain_normalization.data.utils.collate_fn import collate_fn + + +__all__ = ["collate_fn"] diff --git a/stain_normalization/data/utils/collate_fn.py b/stain_normalization/data/utils/collate_fn.py new file mode 100644 index 0000000..667897e --- /dev/null +++ b/stain_normalization/data/utils/collate_fn.py @@ -0,0 +1,6 @@ +from typing import Any +import torch +from torch import Tensor + +def collate_fn(batch: list[tuple[Tensor, Any]]) -> tuple[Tensor, list[Any]]: + return torch.stack([x[0] for x in batch]), [x[1] for x in batch] diff --git a/stain_normalization/stain_normalization_model.py b/stain_normalization/stain_normalization_model.py index 7aca27c..5103d70 100644 --- a/stain_normalization/stain_normalization_model.py +++ b/stain_normalization/stain_normalization_model.py @@ -7,6 +7,7 @@ from torch.optim.optimizer import Optimizer from torchmetrics.image import PeakSignalNoiseRatio,StructuralSimilarityIndexMeasure from torchmetrics import MetricCollection +from typing import Any class StainNormalizationModel(LightningModule): def __init__(self, backbone: nn.Module, decode_head: nn.Module) -> None: @@ -61,6 +62,9 @@ def test_step(self, batch: Input) -> None: on_epoch=True, ) + def predict_step(self, batch: tuple[Tensor, Any], batch_idx: int, dataloader_idx: int = 0) -> Outputs: + inputs = batch[0] + return self(inputs) def configure_optimizers(self) -> Optimizer: return Adam(self.parameters(), lr=1e-4) diff --git a/stain_normalization/typing.py b/stain_normalization/typing.py index 0216ae3..70c55c4 100644 --- a/stain_normalization/typing.py +++ b/stain_normalization/typing.py @@ -1,4 +1,4 @@ -from typing import TypeAlias, TypedDict +from typing import TypeAlias, TypedDict, Dict, Any from torch import Tensor @@ -10,7 +10,7 @@ # PredictSample: TypeAlias = tuple[Tensor, Metadata] # Sample: TypeAlias = tuple[Tensor, Tensor, Metadata] -PredictSample: TypeAlias = tuple[Tensor] +PredictSample: TypeAlias = tuple[Tensor, Dict[str, Any]] Sample: TypeAlias = tuple[Tensor, Tensor] Input: TypeAlias = Sample From 834a149241dfc24d0f5282e6c317efc6b886938c Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Sun, 23 Mar 2025 02:25:26 +0000 Subject: [PATCH 41/85] feat: update loss function --- .../datasets/stain_normalization/val.yaml | 2 +- configs/default.yaml | 4 +- stain_normalization/modeling/l1ssim_loss.py | 49 ++++++++++++++++--- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/configs/data/datasets/stain_normalization/val.yaml b/configs/data/datasets/stain_normalization/val.yaml index 860ea53..8a584fa 100644 --- a/configs/data/datasets/stain_normalization/val.yaml +++ b/configs/data/datasets/stain_normalization/val.yaml @@ -1,6 +1,6 @@ defaults: # - /data/transforms@transforms: test - - /data/modify@modify: test + - /data/modify@modify: train - /data/normalize@normalize: default _target_: stain_normalization.data.datasets.StainNormalization diff --git a/configs/default.yaml b/configs/default.yaml index 998ba31..c33f0f4 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -30,11 +30,11 @@ data: metadata: user: xlopatka experiment_name: Stain-Normalization - run_name: Second Train Stain Normalization + run_name: Third Train Stain Normalization description: Testing Stain Normalization hyperparams: {} - +output_dir: ./data diff --git a/stain_normalization/modeling/l1ssim_loss.py b/stain_normalization/modeling/l1ssim_loss.py index 16cf95d..2910f0b 100644 --- a/stain_normalization/modeling/l1ssim_loss.py +++ b/stain_normalization/modeling/l1ssim_loss.py @@ -5,22 +5,59 @@ from math import exp -class L1SSIMLoss(nn.Module): - def __init__(self, lambda_dssim: float = 0.5): +class L1SSIMLoss(nn.Module): + def __init__(self, lambda_dssim: float = 0.6, lambda_gdl: float = 0.2, lambda_hf: float = 0.1): super().__init__() self.lambda_dssim = lambda_dssim + self.lambda_gdl = lambda_gdl + self.lambda_hf = lambda_hf def forward(self, image: torch.Tensor, target_image: torch.Tensor) -> torch.Tensor: - - Ll1 = F.l1_loss(image, target_image, reduction='mean') + Ll1 = F.l1_loss(image, target_image, reduction='mean') # L1 loss + ssim_loss = 1.0 - ssim(image, target_image) # SSIM loss - ssim_loss = 1.0 - ssim(image, target_image) + gdl_loss = gradient_loss(image, target_image) # GDL for sharp edges + hf_loss = high_frequency_loss(image, target_image) # HF loss for textures total_loss = (1.0 - self.lambda_dssim) * Ll1 + \ - self.lambda_dssim * ssim_loss + self.lambda_dssim * ssim_loss + \ + self.lambda_gdl * gdl_loss + \ + self.lambda_hf * hf_loss + return total_loss +def laplacian_filter(image): + kernel = torch.tensor([[0, -1, 0], [-1, 4, -1], [0, -1, 0]], dtype=torch.float32) + kernel = kernel.view(1, 1, 3, 3).to(image.device) + + filtered = [] + for i in range(image.shape[1]): + filtered.append(F.conv2d(image[:, i:i+1, :, :], kernel, padding=1)) + return torch.cat(filtered, dim=1) + + +def high_frequency_loss(image, target_image): + image_lap = laplacian_filter(image) + target_lap = laplacian_filter(target_image) + + return F.l1_loss(image_lap, target_lap, reduction='mean') + + +def gradient_loss(image, target_image): + def gradient(x): + dx = torch.abs(x[:, :, :-1, :] - x[:, :, 1:, :]) # Horizontal gradient + dy = torch.abs(x[:, :, :, :-1] - x[:, :, :, 1:]) # Vertical gradient + return dx, dy + + image_dx, image_dy = gradient(image) + target_dx, target_dy = gradient(target_image) + + loss_x = F.l1_loss(image_dx, target_dx, reduction='mean') + loss_y = F.l1_loss(image_dy, target_dy, reduction='mean') + + return loss_x + loss_y + def gaussian(window_size, sigma): gauss = torch.Tensor([exp(-(x - window_size // 2) ** 2 / From 6c028397f001cd98a5b2428dda479e0a45995133 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Thu, 27 Mar 2025 21:23:05 +0000 Subject: [PATCH 42/85] fix: p=1 --- configs/data/modify/test.yaml | 1 + configs/data/modify/train.yaml | 1 + configs/data/transforms/train.yaml | 7 ++++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/configs/data/modify/test.yaml b/configs/data/modify/test.yaml index d69792c..8024799 100644 --- a/configs/data/modify/test.yaml +++ b/configs/data/modify/test.yaml @@ -1,4 +1,5 @@ _target_: albumentations.OneOf +p: 1.0 transforms: - _target_: stain_normalization.data.transformation.HEDFactor h_range: [0.8, 1.2] diff --git a/configs/data/modify/train.yaml b/configs/data/modify/train.yaml index d69792c..8024799 100644 --- a/configs/data/modify/train.yaml +++ b/configs/data/modify/train.yaml @@ -1,4 +1,5 @@ _target_: albumentations.OneOf +p: 1.0 transforms: - _target_: stain_normalization.data.transformation.HEDFactor h_range: [0.8, 1.2] diff --git a/configs/data/transforms/train.yaml b/configs/data/transforms/train.yaml index 4cec39f..5e357d6 100644 --- a/configs/data/transforms/train.yaml +++ b/configs/data/transforms/train.yaml @@ -4,4 +4,9 @@ transforms: p: 1.0 - _target_: albumentations.Flip p: 0.75 - + - _target_: albumentations.RandomGamma + p: 0.5 + - _target_: albumentations.RandomBrightnessContrast + p: 0.5 + - _target_: albumentations.HueSaturationValue + p: 0.5 \ No newline at end of file From 68c919dcba61f26147115295c51a373004bba9c9 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 28 Mar 2025 09:25:54 +0000 Subject: [PATCH 43/85] fix: more frequent validation --- configs/default.yaml | 1 + stain_normalization/modeling/l1ssim_loss.py | 82 ++------------------- 2 files changed, 7 insertions(+), 76 deletions(-) diff --git a/configs/default.yaml b/configs/default.yaml index c33f0f4..9f2532c 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -17,6 +17,7 @@ trainer: enable_checkpointing: True max_epochs: 30 log_every_n_steps: 5 + val_check_interval: 250 callbacks: - _target_: lightning.pytorch.callbacks.ModelCheckpoint save_top_k: 1 diff --git a/stain_normalization/modeling/l1ssim_loss.py b/stain_normalization/modeling/l1ssim_loss.py index 2910f0b..81638a9 100644 --- a/stain_normalization/modeling/l1ssim_loss.py +++ b/stain_normalization/modeling/l1ssim_loss.py @@ -3,47 +3,29 @@ import torch.nn as nn from torch.autograd import Variable from math import exp +from torchmetrics.image import StructuralSimilarityIndexMeasure as SSIM class L1SSIMLoss(nn.Module): - def __init__(self, lambda_dssim: float = 0.6, lambda_gdl: float = 0.2, lambda_hf: float = 0.1): + def __init__(self, lambda_dssim: float = 0.6, lambda_gdl: float = 0.2, ): super().__init__() self.lambda_dssim = lambda_dssim self.lambda_gdl = lambda_gdl - self.lambda_hf = lambda_hf def forward(self, image: torch.Tensor, target_image: torch.Tensor) -> torch.Tensor: Ll1 = F.l1_loss(image, target_image, reduction='mean') # L1 loss - ssim_loss = 1.0 - ssim(image, target_image) # SSIM loss + ssim_loss = 1.0 - SSIM(image, target_image) gdl_loss = gradient_loss(image, target_image) # GDL for sharp edges - hf_loss = high_frequency_loss(image, target_image) # HF loss for textures + # hf_loss = high_frequency_loss(image, target_image) # HF loss for textures total_loss = (1.0 - self.lambda_dssim) * Ll1 + \ self.lambda_dssim * ssim_loss + \ - self.lambda_gdl * gdl_loss + \ - self.lambda_hf * hf_loss - + self.lambda_gdl * gdl_loss + return total_loss -def laplacian_filter(image): - kernel = torch.tensor([[0, -1, 0], [-1, 4, -1], [0, -1, 0]], dtype=torch.float32) - kernel = kernel.view(1, 1, 3, 3).to(image.device) - - filtered = [] - for i in range(image.shape[1]): - filtered.append(F.conv2d(image[:, i:i+1, :, :], kernel, padding=1)) - return torch.cat(filtered, dim=1) - - -def high_frequency_loss(image, target_image): - image_lap = laplacian_filter(image) - target_lap = laplacian_filter(target_image) - - return F.l1_loss(image_lap, target_lap, reduction='mean') - - def gradient_loss(image, target_image): def gradient(x): dx = torch.abs(x[:, :, :-1, :] - x[:, :, 1:, :]) # Horizontal gradient @@ -58,55 +40,3 @@ def gradient(x): return loss_x + loss_y - -def gaussian(window_size, sigma): - gauss = torch.Tensor([exp(-(x - window_size // 2) ** 2 / - float(2 * sigma ** 2)) for x in range(window_size)]) - return gauss / gauss.sum() - - -def create_window(window_size, channel): - _1D_window = gaussian(window_size, 1.5).unsqueeze(1) - _2D_window = _1D_window.mm( - _1D_window.t()).float().unsqueeze(0).unsqueeze(0) - window = Variable(_2D_window.expand( - channel, 1, window_size, window_size).contiguous()) - return window - - -def ssim(img1, img2, window_size=11, size_average=True): - channel = img1.size(-3) - window = create_window(window_size, channel) - - if img1.is_cuda: - window = window.cuda(img1.get_device()) - window = window.type_as(img1) - - return _ssim(img1, img2, window, window_size, channel, size_average) - - -def _ssim(img1, img2, window, window_size, channel, size_average=True): - mu1 = F.conv2d(img1, window, padding=window_size // 2, groups=channel) - mu2 = F.conv2d(img2, window, padding=window_size // 2, groups=channel) - - mu1_sq = mu1.pow(2) - mu2_sq = mu2.pow(2) - mu1_mu2 = mu1 * mu2 - - sigma1_sq = F.conv2d(img1 * img1, window, - padding=window_size // 2, groups=channel) - mu1_sq - sigma2_sq = F.conv2d(img2 * img2, window, - padding=window_size // 2, groups=channel) - mu2_sq - sigma12 = F.conv2d(img1 * img2, window, - padding=window_size // 2, groups=channel) - mu1_mu2 - - C1 = 0.01 ** 2 - C2 = 0.03 ** 2 - - ssim_map = ((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) / \ - ((mu1_sq + mu2_sq + C1) * (sigma1_sq + sigma2_sq + C2)) - - if size_average: - return ssim_map.mean() - else: - return ssim_map.mean(1).mean(1).mean(1) From a2a5725a07a3f78fffc453b43573468f9152208e Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Sat, 29 Mar 2025 18:42:51 +0000 Subject: [PATCH 44/85] fix: val logging --- stain_normalization/stain_normalization_model.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stain_normalization/stain_normalization_model.py b/stain_normalization/stain_normalization_model.py index 5103d70..8e82da7 100644 --- a/stain_normalization/stain_normalization_model.py +++ b/stain_normalization/stain_normalization_model.py @@ -48,7 +48,8 @@ def validation_step(self, batch: Input) -> None: self.log_dict( self.val_metrics, batch_size=len(inputs), - on_epoch=True, + on_epoch=False, + on_step=True ) From 497328c3c2c17e9ae020fd5e9c5b958ada18a1b9 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Sat, 29 Mar 2025 18:45:04 +0000 Subject: [PATCH 45/85] fix: val logging --- stain_normalization/stain_normalization_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stain_normalization/stain_normalization_model.py b/stain_normalization/stain_normalization_model.py index 8e82da7..7a8783e 100644 --- a/stain_normalization/stain_normalization_model.py +++ b/stain_normalization/stain_normalization_model.py @@ -43,7 +43,7 @@ def validation_step(self, batch: Input) -> None: outputs = self(inputs) loss = self.criterion(outputs, targets) - self.log("validation/loss", loss, on_step=False, on_epoch=True) + self.log("validation/loss", loss, on_step=True, on_epoch=False) self.val_metrics.update(outputs, targets) self.log_dict( self.val_metrics, From 80b47732dab5e51ef92f4f96ac17f16471cac1dd Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Sat, 29 Mar 2025 19:33:46 +0000 Subject: [PATCH 46/85] fix: repair loss function --- stain_normalization/modeling/l1ssim_loss.py | 62 +++++++++++++++++-- .../stain_normalization_model.py | 6 +- 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/stain_normalization/modeling/l1ssim_loss.py b/stain_normalization/modeling/l1ssim_loss.py index 81638a9..e921916 100644 --- a/stain_normalization/modeling/l1ssim_loss.py +++ b/stain_normalization/modeling/l1ssim_loss.py @@ -3,26 +3,24 @@ import torch.nn as nn from torch.autograd import Variable from math import exp -from torchmetrics.image import StructuralSimilarityIndexMeasure as SSIM class L1SSIMLoss(nn.Module): - def __init__(self, lambda_dssim: float = 0.6, lambda_gdl: float = 0.2, ): + def __init__(self, lambda_dssim: float = 0.6, lambda_gdl: float = 0.2): super().__init__() self.lambda_dssim = lambda_dssim self.lambda_gdl = lambda_gdl def forward(self, image: torch.Tensor, target_image: torch.Tensor) -> torch.Tensor: Ll1 = F.l1_loss(image, target_image, reduction='mean') # L1 loss - ssim_loss = 1.0 - SSIM(image, target_image) + ssim_loss = 1.0 - ssim(image, target_image) # SSIM loss gdl_loss = gradient_loss(image, target_image) # GDL for sharp edges - # hf_loss = high_frequency_loss(image, target_image) # HF loss for textures total_loss = (1.0 - self.lambda_dssim) * Ll1 + \ self.lambda_dssim * ssim_loss + \ - self.lambda_gdl * gdl_loss - + self.lambda_gdl * gdl_loss + return total_loss @@ -40,3 +38,55 @@ def gradient(x): return loss_x + loss_y + +def gaussian(window_size, sigma): + gauss = torch.Tensor([exp(-(x - window_size // 2) ** 2 / + float(2 * sigma ** 2)) for x in range(window_size)]) + return gauss / gauss.sum() + + +def create_window(window_size, channel): + _1D_window = gaussian(window_size, 1.5).unsqueeze(1) + _2D_window = _1D_window.mm( + _1D_window.t()).float().unsqueeze(0).unsqueeze(0) + window = Variable(_2D_window.expand( + channel, 1, window_size, window_size).contiguous()) + return window + + +def ssim(img1, img2, window_size=11, size_average=True): + channel = img1.size(-3) + window = create_window(window_size, channel) + + if img1.is_cuda: + window = window.cuda(img1.get_device()) + window = window.type_as(img1) + + return _ssim(img1, img2, window, window_size, channel, size_average) + + +def _ssim(img1, img2, window, window_size, channel, size_average=True): + mu1 = F.conv2d(img1, window, padding=window_size // 2, groups=channel) + mu2 = F.conv2d(img2, window, padding=window_size // 2, groups=channel) + + mu1_sq = mu1.pow(2) + mu2_sq = mu2.pow(2) + mu1_mu2 = mu1 * mu2 + + sigma1_sq = F.conv2d(img1 * img1, window, + padding=window_size // 2, groups=channel) - mu1_sq + sigma2_sq = F.conv2d(img2 * img2, window, + padding=window_size // 2, groups=channel) - mu2_sq + sigma12 = F.conv2d(img1 * img2, window, + padding=window_size // 2, groups=channel) - mu1_mu2 + + C1 = 0.01 ** 2 + C2 = 0.03 ** 2 + + ssim_map = ((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) / \ + ((mu1_sq + mu2_sq + C1) * (sigma1_sq + sigma2_sq + C2)) + + if size_average: + return ssim_map.mean() + else: + return ssim_map.mean(1).mean(1).mean(1) diff --git a/stain_normalization/stain_normalization_model.py b/stain_normalization/stain_normalization_model.py index 7a8783e..387e8d5 100644 --- a/stain_normalization/stain_normalization_model.py +++ b/stain_normalization/stain_normalization_model.py @@ -43,13 +43,13 @@ def validation_step(self, batch: Input) -> None: outputs = self(inputs) loss = self.criterion(outputs, targets) - self.log("validation/loss", loss, on_step=True, on_epoch=False) + self.log("validation/loss", loss, on_step=True, on_epoch=True) self.val_metrics.update(outputs, targets) self.log_dict( self.val_metrics, batch_size=len(inputs), - on_epoch=False, - on_step=True + on_epoch=True, + on_step=True, ) From 9d60be840a3510de91f7727ecff993d1eb89587b Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Sat, 29 Mar 2025 19:56:50 +0000 Subject: [PATCH 47/85] fix: revert val logging --- stain_normalization/stain_normalization_model.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/stain_normalization/stain_normalization_model.py b/stain_normalization/stain_normalization_model.py index 387e8d5..98b858a 100644 --- a/stain_normalization/stain_normalization_model.py +++ b/stain_normalization/stain_normalization_model.py @@ -43,15 +43,13 @@ def validation_step(self, batch: Input) -> None: outputs = self(inputs) loss = self.criterion(outputs, targets) - self.log("validation/loss", loss, on_step=True, on_epoch=True) + self.log("validation/loss", loss, on_step=False, on_epoch=True) self.val_metrics.update(outputs, targets) self.log_dict( self.val_metrics, batch_size=len(inputs), - on_epoch=True, - on_step=True, + on_epoch=True, ) - def test_step(self, batch: Input) -> None: inputs, targets = batch From 20caad9a3b92a97d962a392463317c8246eef248 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Sat, 29 Mar 2025 20:31:27 +0000 Subject: [PATCH 48/85] fix: optimalise train data load --- .../data/datasets/stain_normalization.py | 72 ++++++++++++++----- 1 file changed, 55 insertions(+), 17 deletions(-) diff --git a/stain_normalization/data/datasets/stain_normalization.py b/stain_normalization/data/datasets/stain_normalization.py index 64aa680..9203f9e 100644 --- a/stain_normalization/data/datasets/stain_normalization.py +++ b/stain_normalization/data/datasets/stain_normalization.py @@ -17,7 +17,6 @@ def __init__( modify: Transform3D, normalize: Transform3D | None = None, transforms: Transform3D | None = None, - ) -> None: self.modify = modify self.transforms = transforms @@ -26,10 +25,9 @@ def __init__( def generate_datasets(self) -> Iterable[Dataset[Sample]]: return ( - _StainNormalizationSlideTiles( + _StainNormalizationTrainSlideTiles( slide, tiles=self.filter_tiles_by_slide(slide["id"]), - include_target=True, modify=self.modify, normalize=self.normalize, transforms=self.transforms, @@ -45,8 +43,7 @@ def __init__( modify: Transform3D, normalize: Transform3D | None = None, transforms: Transform3D | None = None, - - ) -> None: + ) -> None: self.modify = modify self.normalize = normalize self.transforms = transforms @@ -54,10 +51,9 @@ def __init__( def generate_datasets(self) -> Iterable[Dataset[PredictSample]]: return ( - _StainNormalizationSlideTiles( + _StainNormalizationPredictSlideTiles( slide, tiles=self.filter_tiles_by_slide(slide["id"]), - include_target=False, modify=self.modify, normalize=self.normalize, transforms=self.transforms, @@ -66,12 +62,11 @@ def generate_datasets(self) -> Iterable[Dataset[PredictSample]]: ) -class _StainNormalizationSlideTiles(Dataset[Sample | PredictSample]): +class _StainNormalizationTrainSlideTiles(Dataset[Sample]): def __init__( self, slide_metadata: pd.Series, tiles: pd.DataFrame, - include_target: bool, modify: Transform3D, normalize: Transform3D | None = None, transforms: Transform3D | None = None, @@ -87,17 +82,62 @@ def __init__( self.modify = modify self.normalize = normalize self.transforms = transforms - self.include_target = include_target self.to_tensor = ToTensorV2() def __len__(self) -> int: return len(self.slide_tiles) - def __getitem__(self, idx: int) -> Sample | PredictSample: + def __getitem__(self, idx: int) -> Sample: + original_image = self.slide_tiles[idx] + # Apply non-disruptive transformations such as rotation, flip + if self.transforms is not None: + original_image = self.transforms(image=original_image)["image"] + + # Create "wrong" image to use as input. Outputs image in float 0-1 + modified_image = self.modify(image=original_image)["image"] + original_image = original_image / 255.0 + + if self.normalize: + original_image = self.normalize(image=original_image)["image"] + modified_image = self.normalize(image=modified_image)["image"] + + original_image = self.to_tensor(image=original_image)["image"] + modified_image = self.to_tensor(image=modified_image)["image"] + + return modified_image, original_image + + + +class _StainNormalizationPredictSlideTiles(Dataset[PredictSample]): + def __init__( + self, + slide_metadata: pd.Series, + tiles: pd.DataFrame, + modify: Transform3D, + normalize: Transform3D | None = None, + transforms: Transform3D | None = None, + ) -> None: + super().__init__() + self.slide_tiles = OpenSlideTilesDataset( + slide_path=slide_metadata.path, + level=slide_metadata.level, + tile_extent_x=slide_metadata.tile_extent_x, + tile_extent_y=slide_metadata.tile_extent_y, + tiles=tiles, + ) + self.modify = modify + self.normalize = normalize + self.transforms = transforms + self.to_tensor = ToTensorV2() + + def __len__(self) -> int: + return len(self.slide_tiles) + + def __getitem__(self, idx: int) -> PredictSample: original_image_255 = self.slide_tiles[idx] - # This is not distruptive transform as rotate, flip + # Apply non-disruptive transformations such as rotation, flip if self.transforms is not None: - original_image_255 = self.transforms(image=original_image)["image"] + original_image_255 = self.transforms(image=original_image_255)["image"] # Create "wrong" image to use as input. Outputs image in float 0-1 modified_image_raw = self.modify(image=original_image_255)["image"] @@ -112,7 +152,5 @@ def __getitem__(self, idx: int) -> Sample | PredictSample: modified_image = self.to_tensor(image=modified_image)["image"] - if self.include_target: - return modified_image, original_image - - return modified_image, {"original_image":original_image_255, "modified_image":modified_image_raw,"index":idx} + return modified_image, {"original_image": original_image_255, "modified_image": modified_image_raw, "index": idx} + From 4da48cdf8109bc3a51cdfb3e437452d09df324b1 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Sat, 29 Mar 2025 21:54:38 +0000 Subject: [PATCH 49/85] fix: update val frequency --- configs/default.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/default.yaml b/configs/default.yaml index 9f2532c..29c3e7e 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -17,7 +17,7 @@ trainer: enable_checkpointing: True max_epochs: 30 log_every_n_steps: 5 - val_check_interval: 250 + val_check_interval: 0.25 callbacks: - _target_: lightning.pytorch.callbacks.ModelCheckpoint save_top_k: 1 From 0df039e4c2cf7bb291b0b9f5ec802d8efd6567f4 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 18 Apr 2025 22:43:33 +0000 Subject: [PATCH 50/85] feat: change to full unet --- stain_normalization/modeling/__init__.py | 3 +- stain_normalization/modeling/unet.py | 114 ++++++++++++++++++ .../stain_normalization_model.py | 12 +- 3 files changed, 121 insertions(+), 8 deletions(-) create mode 100644 stain_normalization/modeling/unet.py diff --git a/stain_normalization/modeling/__init__.py b/stain_normalization/modeling/__init__.py index 9ae4c05..2c4be67 100644 --- a/stain_normalization/modeling/__init__.py +++ b/stain_normalization/modeling/__init__.py @@ -1,2 +1,3 @@ from .l1ssim_loss import L1SSIMLoss -__all__ = ["L1SSIMLoss", "metrics"] \ No newline at end of file +from .unet import UNet +__all__ = ["L1SSIMLoss", "UNet"] \ No newline at end of file diff --git a/stain_normalization/modeling/unet.py b/stain_normalization/modeling/unet.py new file mode 100644 index 0000000..485bc9b --- /dev/null +++ b/stain_normalization/modeling/unet.py @@ -0,0 +1,114 @@ +""" +Model created using segmentation_models_pytorch library: +# Adapted U-Net implementation based on https://github.com/milesial/Pytorch-UNet + +Ronneberger, O., Fischer, P., & Brox, T. (2015). +U-Net: Convolutional Networks for Biomedical Image Segmentation. +arXiv:1505.04597 [cs.CV]. +Retrieved from https://arxiv.org/abs/1505.04597. +""" + +import torch +import torch.nn as nn +import torch.nn.functional as F + +class DoubleConv(nn.Module): + """(convolution => [BN] => ReLU) * 2""" + + def __init__(self, in_channels, out_channels, mid_channels=None): + super().__init__() + if not mid_channels: + mid_channels = out_channels + self.double_conv = nn.Sequential( + nn.Conv2d(in_channels, mid_channels, kernel_size=3, padding=1, bias=False), + nn.BatchNorm2d(mid_channels), + nn.ReLU(inplace=True), + nn.Conv2d(mid_channels, out_channels, kernel_size=3, padding=1, bias=False), + nn.BatchNorm2d(out_channels), + nn.ReLU(inplace=True) + ) + + def forward(self, x): + return self.double_conv(x) + + +class Down(nn.Module): + """Downscaling with maxpool then double conv""" + + def __init__(self, in_channels, out_channels): + super().__init__() + self.maxpool_conv = nn.Sequential( + nn.MaxPool2d(2), + DoubleConv(in_channels, out_channels) + ) + + def forward(self, x): + return self.maxpool_conv(x) + + +class Up(nn.Module): + """Upscaling then double conv""" + + def __init__(self, in_channels, out_channels, bilinear=True): + super().__init__() + + # if bilinear, use the normal convolutions to reduce the number of channels + if bilinear: + self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True) + self.conv = DoubleConv(in_channels, out_channels, in_channels // 2) + else: + self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=2, stride=2) + self.conv = DoubleConv(in_channels, out_channels) + + def forward(self, x1, x2): + x1 = self.up(x1) + # input is CHW + diffY = x2.size()[2] - x1.size()[2] + diffX = x2.size()[3] - x1.size()[3] + + x1 = F.pad(x1, [diffX // 2, diffX - diffX // 2, + diffY // 2, diffY - diffY // 2]) + # if you have padding issues, see + # https://github.com/HaiyongJiang/U-Net-Pytorch-Unstructured-Buggy/commit/0e854509c2cea854e247a9c615f175f76fbb2e3a + # https://github.com/xiaopeng-liao/Pytorch-UNet/commit/8ebac70e633bac59fc22bb5195e513d5832fb3bd + x = torch.cat([x2, x1], dim=1) + return self.conv(x) + + +class OutConv(nn.Module): + def __init__(self, in_channels, out_channels): + super(OutConv, self).__init__() + self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1) + + def forward(self, x): + return self.conv(x) + + +class UNet(nn.Module): + def __init__(self, in_channels=3, out_channels=3, bilinear=True): + super(UNet, self).__init__() + self.in_conv = DoubleConv(in_channels, 64) + self.down1 = Down(64, 128) + self.down2 = Down(128, 256) + self.down3 = Down(256, 512) + + factor = 2 if bilinear else 1 + self.down4 = (Down(512, 1024 // factor)) + self.up1 = (Up(1024, 512 // factor, bilinear)) + self.up2 = (Up(512, 256 // factor, bilinear)) + self.up3 = (Up(256, 128 // factor, bilinear)) + self.up4 = (Up(128, 64, bilinear)) + self.out_conv = OutConv(64, out_channels) + + def forward(self, x): + x1 = self.in_conv(x) + x2 = self.down1(x1) + x3 = self.down2(x2) + x4 = self.down3(x3) + x5 = self.down4(x4) + + x = self.up1(x5, x4) + x = self.up2(x, x3) + x = self.up3(x, x2) + x = self.up4(x, x1) + return self.out_conv(x) diff --git a/stain_normalization/stain_normalization_model.py b/stain_normalization/stain_normalization_model.py index 98b858a..9906481 100644 --- a/stain_normalization/stain_normalization_model.py +++ b/stain_normalization/stain_normalization_model.py @@ -1,7 +1,7 @@ from lightning import LightningModule from torch import Tensor, nn from torch.optim.optimizer import Optimizer -from stain_normalization.modeling import L1SSIMLoss +from stain_normalization.modeling import L1SSIMLoss, UNet from stain_normalization.typing import Input, Outputs from torch.optim import Adam from torch.optim.optimizer import Optimizer @@ -12,22 +12,20 @@ class StainNormalizationModel(LightningModule): def __init__(self, backbone: nn.Module, decode_head: nn.Module) -> None: super().__init__() - self.backbone = backbone - self.decode_head = decode_head + self.unet = UNet(in_channels=3,out_channels=3) self.criterion = L1SSIMLoss() self.val_metrics = MetricCollection( { "ssim": StructuralSimilarityIndexMeasure(), - "psnr": PeakSignalNoiseRatio() + # "psnr": PeakSignalNoiseRatio() } ) self.test_metrics = self.val_metrics.clone(prefix="test/") self.val_metrics.prefix = "validation/" def forward(self, x: Input) -> Outputs: - features = self.backbone(x) - return self.decode_head(features) + return self.unet(x) def training_step(self, batch: Input) -> Tensor: inputs, targets = batch @@ -43,7 +41,7 @@ def validation_step(self, batch: Input) -> None: outputs = self(inputs) loss = self.criterion(outputs, targets) - self.log("validation/loss", loss, on_step=False, on_epoch=True) + self.log("validation/loss", loss, on_step=False, on_epoch=True, logger=True) self.val_metrics.update(outputs, targets) self.log_dict( self.val_metrics, From c000c354c02eebcfaf120365f6bc31e11227a259 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 18 Apr 2025 22:50:16 +0000 Subject: [PATCH 51/85] chore: update job setup --- configs/default.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/default.yaml b/configs/default.yaml index 29c3e7e..b551854 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -17,7 +17,7 @@ trainer: enable_checkpointing: True max_epochs: 30 log_every_n_steps: 5 - val_check_interval: 0.25 + val_check_interval: 0.5 callbacks: - _target_: lightning.pytorch.callbacks.ModelCheckpoint save_top_k: 1 @@ -26,12 +26,12 @@ trainer: data: batch_size: 128 - num_workers: 8 + num_workers: 6 metadata: user: xlopatka experiment_name: Stain-Normalization - run_name: Third Train Stain Normalization + run_name: Unet Train Stain Normalization description: Testing Stain Normalization hyperparams: {} From 8522a27b32adf94eebe5fa1000ea05da9ef6f72b Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 18 Apr 2025 23:20:40 +0000 Subject: [PATCH 52/85] chore: update job setup --- configs/default.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/default.yaml b/configs/default.yaml index b551854..58b6d6b 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -25,7 +25,7 @@ trainer: mode: min data: - batch_size: 128 + batch_size: 64 num_workers: 6 metadata: From ad4bfd1b09bb15b737fa5dda345573b401bf4db4 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 16 May 2025 03:10:32 +0000 Subject: [PATCH 53/85] chore: code cleanup --- .../datasets/stain_normalization/predict.yaml | 6 +- .../datasets/stain_normalization/test.yaml | 3 +- .../datasets/stain_normalization/train.yaml | 3 +- .../datasets/stain_normalization/val.yaml | 3 +- configs/data/transforms/test.yaml | 4 - configs/data/transforms/train copy.yaml | 16 -- configs/data/transforms/train.yaml | 12 -- configs/default.yaml | 29 ++-- configs/experiment/.gitkeep | 0 configs/model/backbone/resnet50.yaml | 2 - configs/model/backbone/vgg16.yaml | 2 - configs/model/decode_head/unet2048.yaml | 3 - configs/model/decode_head/unet512.yaml | 3 - pyproject.toml | 3 +- stain_normalization/__main__.py | 8 +- stain_normalization/callbacks/__init__.py | 6 + .../callbacks/analysis_export.py | 117 +++++++++++++ stain_normalization/callbacks/save_wsi.py | 52 ++++++ stain_normalization/callbacks/tiles_export.py | 57 ++++--- stain_normalization/data/data_module.py | 6 +- stain_normalization/data/datasets/__init__.py | 7 +- .../data/datasets/predict_dataset.py | 74 +++++++++ .../data/datasets/stain_normalization.py | 156 ------------------ .../data/datasets/test_dataset.py | 80 +++++++++ .../data/datasets/train_dataset.py | 75 +++++++++ .../__init__.py | 0 .../combiend_modification.py | 0 .../exposure_adjustment.py | 11 +- .../hed_factor.py | 0 .../hvs_modification.py | 0 .../modeling/backbone/__init__.py | 5 - .../modeling/backbone/resnet50.py | 7 - .../modeling/backbone/vgg16.py | 6 - .../modeling/decode_head/__init__.py | 4 - .../modeling/decode_head/unet.py | 35 ---- stain_normalization/modeling/l1ssim_loss.py | 4 +- .../stain_normalization_model.py | 15 +- stain_normalization/typing.py | 8 - 38 files changed, 485 insertions(+), 337 deletions(-) delete mode 100644 configs/data/transforms/test.yaml delete mode 100644 configs/data/transforms/train copy.yaml delete mode 100644 configs/data/transforms/train.yaml delete mode 100644 configs/experiment/.gitkeep delete mode 100644 configs/model/backbone/resnet50.yaml delete mode 100644 configs/model/backbone/vgg16.yaml delete mode 100644 configs/model/decode_head/unet2048.yaml delete mode 100644 configs/model/decode_head/unet512.yaml create mode 100644 stain_normalization/callbacks/__init__.py create mode 100644 stain_normalization/callbacks/analysis_export.py create mode 100644 stain_normalization/callbacks/save_wsi.py create mode 100644 stain_normalization/data/datasets/predict_dataset.py delete mode 100644 stain_normalization/data/datasets/stain_normalization.py create mode 100644 stain_normalization/data/datasets/test_dataset.py create mode 100644 stain_normalization/data/datasets/train_dataset.py rename stain_normalization/data/{transformation => modification}/__init__.py (100%) rename stain_normalization/data/{transformation => modification}/combiend_modification.py (100%) rename stain_normalization/data/{transformation => modification}/exposure_adjustment.py (57%) rename stain_normalization/data/{transformation => modification}/hed_factor.py (100%) rename stain_normalization/data/{transformation => modification}/hvs_modification.py (100%) delete mode 100644 stain_normalization/modeling/backbone/__init__.py delete mode 100644 stain_normalization/modeling/backbone/resnet50.py delete mode 100644 stain_normalization/modeling/backbone/vgg16.py delete mode 100644 stain_normalization/modeling/decode_head/__init__.py delete mode 100644 stain_normalization/modeling/decode_head/unet.py diff --git a/configs/data/datasets/stain_normalization/predict.yaml b/configs/data/datasets/stain_normalization/predict.yaml index 5da9773..0cf3d2e 100644 --- a/configs/data/datasets/stain_normalization/predict.yaml +++ b/configs/data/datasets/stain_normalization/predict.yaml @@ -1,7 +1,5 @@ defaults: - # - /data/transforms@transforms: test - - /data/modify@modify: test - /data/normalize@normalize: default -_target_: stain_normalization.data.datasets.StainNormalizationPredict -uris: ["mlflow-artifacts:"] +_target_: stain_normalization.data.datasets.PredictDataset +uris: ["mlflow-artifacts:/79/ce91ce61ac8e4b4784b76a7193e59da9/artifacts/FNBrno_400/2"] diff --git a/configs/data/datasets/stain_normalization/test.yaml b/configs/data/datasets/stain_normalization/test.yaml index 4904032..c55810c 100644 --- a/configs/data/datasets/stain_normalization/test.yaml +++ b/configs/data/datasets/stain_normalization/test.yaml @@ -1,7 +1,6 @@ defaults: - # - /data/transforms@transforms: test - /data/modify@modify: test - /data/normalize@normalize: default -_target_: stain_normalization.data.datasets.StainNormalization +_target_: stain_normalization.data.datasets.TestDataset uris: ["mlflow-artifacts:/79/bc7dfc2258164cd292091c3056609277/artifacts/Stain Normalization - test"] diff --git a/configs/data/datasets/stain_normalization/train.yaml b/configs/data/datasets/stain_normalization/train.yaml index dfa39eb..e0f2b43 100644 --- a/configs/data/datasets/stain_normalization/train.yaml +++ b/configs/data/datasets/stain_normalization/train.yaml @@ -1,7 +1,6 @@ defaults: - # - /data/transforms@transforms: train - /data/modify@modify: train - /data/normalize@normalize: default -_target_: stain_normalization.data.datasets.StainNormalization +_target_: stain_normalization.data.datasets.TrainDataset uris: ["mlflow-artifacts:/79/bc7dfc2258164cd292091c3056609277/artifacts/Stain Normalization - train"] diff --git a/configs/data/datasets/stain_normalization/val.yaml b/configs/data/datasets/stain_normalization/val.yaml index 8a584fa..b39e255 100644 --- a/configs/data/datasets/stain_normalization/val.yaml +++ b/configs/data/datasets/stain_normalization/val.yaml @@ -1,8 +1,7 @@ defaults: - # - /data/transforms@transforms: test - /data/modify@modify: train - /data/normalize@normalize: default -_target_: stain_normalization.data.datasets.StainNormalization +_target_: stain_normalization.data.datasets.TrainDataset uris: ["mlflow-artifacts:/79/bc7dfc2258164cd292091c3056609277/artifacts/Stain Normalization - val"] diff --git a/configs/data/transforms/test.yaml b/configs/data/transforms/test.yaml deleted file mode 100644 index 7c0a147..0000000 --- a/configs/data/transforms/test.yaml +++ /dev/null @@ -1,4 +0,0 @@ -_target_: albumentations.Normalize -mean: [198.9923, 156.7051, 185.0198] -std: [36.8293, 46.7353, 35.8960] -max_pixel_value: 1 diff --git a/configs/data/transforms/train copy.yaml b/configs/data/transforms/train copy.yaml deleted file mode 100644 index 81162f1..0000000 --- a/configs/data/transforms/train copy.yaml +++ /dev/null @@ -1,16 +0,0 @@ -_target_: albumentations.Compose -transforms: - - _target_: albumentations.RandomRotate90 - p: 1.0 - - _target_: albumentations.Flip - p: 0.75 - - _target_: albumentations.RandomGamma - p: 0.5 - - _target_: albumentations.RandomBrightnessContrast - p: 0.5 - - _target_: albumentations.HueSaturationValue - p: 0.5 - - _target_: albumentations.Normalize - mean: [198.9923, 156.7051, 185.0198] - std: [36.8293, 46.7353, 35.8960] - max_pixel_value: 1 diff --git a/configs/data/transforms/train.yaml b/configs/data/transforms/train.yaml deleted file mode 100644 index 5e357d6..0000000 --- a/configs/data/transforms/train.yaml +++ /dev/null @@ -1,12 +0,0 @@ -_target_: albumentations.Compose -transforms: - - _target_: albumentations.RandomRotate90 - p: 1.0 - - _target_: albumentations.Flip - p: 0.75 - - _target_: albumentations.RandomGamma - p: 0.5 - - _target_: albumentations.RandomBrightnessContrast - p: 0.5 - - _target_: albumentations.HueSaturationValue - p: 0.5 \ No newline at end of file diff --git a/configs/default.yaml b/configs/default.yaml index 58b6d6b..0076175 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -5,37 +5,46 @@ defaults: - /data/datasets@data.val: stain_normalization/val - /data/datasets@data.test: stain_normalization/test - /data/datasets@data.predict: stain_normalization/predict - - /model/backbone: resnet50 - - /model/decode_head: unet2048 - _self_ seed: ${random_seed:} -mode: ??? -checkpoint: null +mode: predict +checkpoint: mlflow-artifacts:/79/f75808f6e14344bb814e5c0659321054/artifacts/checkpoints/epoch=5-step=86273/checkpoint.ckpt trainer: enable_checkpointing: True max_epochs: 30 log_every_n_steps: 5 - val_check_interval: 0.5 callbacks: - _target_: lightning.pytorch.callbacks.ModelCheckpoint save_top_k: 1 monitor: validation/loss mode: min + - _target_: stain_normalization.callbacks.TilesExport + output_dir: ${output_dir} + normalization_config: ${data.test.normalize} + + - _target_: stain_normalization.callbacks.AnalysisExport + output_dir: ${output_dir} + normalization_config: ${data.test.normalize} + + - _target_: stain_normalization.callbacks.SaveWSI + output_dir: ${output_dir} + normalization_config: ${data.predict.normalize} data: - batch_size: 64 - num_workers: 6 + batch_size: 64 + num_workers: 32 metadata: user: xlopatka experiment_name: Stain-Normalization - run_name: Unet Train Stain Normalization - description: Testing Stain Normalization + run_name: Debuging + description: Stain Normalization hyperparams: {} -output_dir: ./data + +output_dir: ./data/test-dataset diff --git a/configs/experiment/.gitkeep b/configs/experiment/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/configs/model/backbone/resnet50.yaml b/configs/model/backbone/resnet50.yaml deleted file mode 100644 index 4b6b5f2..0000000 --- a/configs/model/backbone/resnet50.yaml +++ /dev/null @@ -1,2 +0,0 @@ -_target_: stain_normalization.modeling.backbone.resnet50 -weights: null diff --git a/configs/model/backbone/vgg16.yaml b/configs/model/backbone/vgg16.yaml deleted file mode 100644 index 4e72da5..0000000 --- a/configs/model/backbone/vgg16.yaml +++ /dev/null @@ -1,2 +0,0 @@ -_target_: stain_normalization.modeling.backbone.vgg16 -weights: null diff --git a/configs/model/decode_head/unet2048.yaml b/configs/model/decode_head/unet2048.yaml deleted file mode 100644 index dce8b92..0000000 --- a/configs/model/decode_head/unet2048.yaml +++ /dev/null @@ -1,3 +0,0 @@ -_target_: stain_normalization.modeling.decode_head.UNetDecoder -in_channels: 2048 - diff --git a/configs/model/decode_head/unet512.yaml b/configs/model/decode_head/unet512.yaml deleted file mode 100644 index 6442711..0000000 --- a/configs/model/decode_head/unet512.yaml +++ /dev/null @@ -1,3 +0,0 @@ -_target_: stain_normalization.modeling.decode_head.UNetDecoder -in_channels: 512 - diff --git a/pyproject.toml b/pyproject.toml index 83dd1f8..e498650 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,8 @@ dependencies = [ "rationai-tiling @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git", "rationai-kube-jobs @ git+ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/kube-jobs.git", "scikit-image>=0.25.2", - "openslide-bin>=4.0.0.6" + "openslide-bin>=4.0.0.6", + "rationai-staining @ git+ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git" ] [tool.pdm.dev-dependencies] diff --git a/stain_normalization/__main__.py b/stain_normalization/__main__.py index c4e1155..f9b86dd 100644 --- a/stain_normalization/__main__.py +++ b/stain_normalization/__main__.py @@ -6,7 +6,6 @@ from omegaconf import DictConfig, OmegaConf from rationai.mlkit import Trainer, autolog -from stain_normalization.callbacks.tiles_export import TilesExport from stain_normalization.data import DataModule from stain_normalization.stain_normalization_model import StainNormalizationModel @@ -26,11 +25,8 @@ def main(config: DictConfig, logger: Logger | None) -> None: _recursive_=False, # to avoid instantiating all the datasets _target_=DataModule, ) - model = hydra.utils.instantiate(config.model, _target_=StainNormalizationModel) - - tile_export_callbeck = TilesExport(config.output_dir, config.data.predict.normalize) - - trainer = hydra.utils.instantiate(config.trainer, _target_=Trainer, logger=logger, callbacks=[tile_export_callbeck]) + model = StainNormalizationModel() + trainer = hydra.utils.instantiate(config.trainer, _target_=Trainer, logger=logger) getattr(trainer, config.mode)(model, datamodule=data, ckpt_path=config.checkpoint) diff --git a/stain_normalization/callbacks/__init__.py b/stain_normalization/callbacks/__init__.py new file mode 100644 index 0000000..f78f0d7 --- /dev/null +++ b/stain_normalization/callbacks/__init__.py @@ -0,0 +1,6 @@ +from stain_normalization.callbacks.analysis_export import AnalysisExport +from stain_normalization.callbacks.tiles_export import TilesExport +from stain_normalization.callbacks.save_wsi import SaveWSI + +__all__ = ["TileExport", "AnalysisExport", "SaveWSI"] + diff --git a/stain_normalization/callbacks/analysis_export.py b/stain_normalization/callbacks/analysis_export.py new file mode 100644 index 0000000..0abaa8f --- /dev/null +++ b/stain_normalization/callbacks/analysis_export.py @@ -0,0 +1,117 @@ +import mlflow +import pandas as pd +import torch +from pathlib import Path +from lightning import Callback, LightningModule, Trainer +import numpy as np +from skimage.metrics import structural_similarity as ssim +from rationai.staining import estimate_stain_vectors + + +class AnalysisExport(Callback): + def __init__(self, output_dir: str | Path, normalization_config) -> None: + super().__init__() + self.output_dir = Path(output_dir) + self.output_dir.mkdir(parents=True, exist_ok=True) + + normalization = normalization_config + self.mean = torch.tensor(normalization.mean).view(3, 1, 1) + self.std = torch.tensor(normalization.std).view(3, 1, 1) + + self.df_diff = pd.DataFrame() + self.df_raw = pd.DataFrame() + + def denormalize(self, tensor: torch.Tensor) -> torch.Tensor: + std = self.std.to(tensor.device) + mean = self.mean.to(tensor.device) + return (tensor * std) + mean + + def tensor_to_image(self, tensor: torch.Tensor) -> np.ndarray: + tensor = self.denormalize(tensor).clamp(0, 1) + return (tensor * 255).byte().permute(1, 2, 0).cpu().numpy() + + def _estimate_vectors(self, img: np.ndarray) -> np.ndarray: + return estimate_stain_vectors(img, i0=240, alpha=1, beta=0.15) + + def _compare_vectors(self, vec1: np.ndarray, vec2: np.ndarray) -> float: + return float(np.linalg.norm(vec1 - vec2)) + + def _compute_ssim(self, img1: np.ndarray, img2: np.ndarray) -> float: + return float(ssim(img1, img2, channel_axis=-1, data_range=255)) + + def _compute_nmi(self, img: np.ndarray): # normalized median intensity + avg_rgb = img.mean(axis=2) + median_val = np.median(avg_rgb) + p95_val = np.percentile(avg_rgb, 95) + nmi = median_val / p95_val if p95_val != 0 else 0 + return nmi + +def on_test_batch_end( + self, + trainer: Trainer, + pl_module: LightningModule, + outputs: list[torch.Tensor], + batch: tuple[torch.Tensor, list], + batch_idx: int, + dataloader_idx: int = 0, +) -> None: + for b in range(len(outputs)): + predicted_tensor = outputs[b] + original_np = batch[1][b]["original_image"].astype("uint8") + modified_np = (batch[1][b]["modified_image"] * 255).astype("uint8") + index = batch[1][b]["index"] + predicted_np = self.tensor_to_image(predicted_tensor) + + vec_original = self._estimate_vectors(original_np) + vec_modified = self._estimate_vectors(modified_np) + vec_predicted = self._estimate_vectors(predicted_np) + + vec_diff_mod = self._compare_vectors(vec_original, vec_modified) + vec_diff_pred = self._compare_vectors(vec_original, vec_predicted) + + ssim_mod = self._compute_ssim(original_np, modified_np) + ssim_pred = self._compute_ssim(original_np, predicted_np) + + + nmi_original = self.compute_nmi(original_np) + nmi_modified = self.compute_nmi(modified_np) + nmi_predicted = self.compute_nmi(predicted_np) + + diff_row = { + "index": index, + "vector_diff_modified_vs_original": vec_diff_mod, + "vector_diff_predicted_vs_original": vec_diff_pred, + "ssim_modified_vs_original": ssim_mod, + "ssim_predicted_vs_original": ssim_pred, + "nmi_diff_modified_vs_original": nmi_modified - nmi_original, + "nmi_diff_predicted_vs_original": nmi_predicted - nmi_original, + } + + raw_row = {"index": index} + for name, vec, nmi in zip( + ["original", "modified", "predicted"], + [vec_original, vec_modified, vec_predicted], + [nmi_original, nmi_modified, nmi_predicted] + ): + vectors = vec.flatten() + for j, val in enumerate(vectors): + raw_row[f"{name}_vec_{j}"] = val + raw_row[f"{name}_nmi"] = nmi + + self.df_diff = pd.concat([self.df_diff, pd.DataFrame([diff_row])], ignore_index=True) + self.df_raw = pd.concat([self.df_raw, pd.DataFrame([raw_row])], ignore_index=True) + + def on_predict_end( + self, + trainer: Trainer, + pl_module: LightningModule, + outputs: list, + ) -> None: + diff_path = self.output_dir / "analysis_differences.csv" + raw_path = self.output_dir / "analysis_raw_vectors.csv" + + self.df_diff.to_csv(diff_path, index=False) + self.df_raw.to_csv(raw_path, index=False) + + mlflow.log_artifact(str(diff_path), artifact_path="analysis_metrics") + mlflow.log_artifact(str(raw_path), artifact_path="analysis_metrics") diff --git a/stain_normalization/callbacks/save_wsi.py b/stain_normalization/callbacks/save_wsi.py new file mode 100644 index 0000000..614a466 --- /dev/null +++ b/stain_normalization/callbacks/save_wsi.py @@ -0,0 +1,52 @@ +from lightning.pytorch.callbacks import Callback +from pathlib import Path +import torch +from PIL import Image +from omegaconf import DictConfig + + +class SaveWSI(Callback): + def __init__(self, output_dir: str | Path, normalization_config: DictConfig) -> None: + super().__init__() + self.output_root = Path(output_dir) + self.mean = torch.tensor(normalization_config.mean).view(3, 1, 1) + self.std = torch.tensor(normalization_config.std).view(3, 1, 1) + + def denormalize(self, tensor: torch.Tensor) -> torch.Tensor: + device = tensor.device + std = self.std.to(device) + mean = self.mean.to(device) + return (tensor * std) + mean + + def on_predict_batch_end( + self, + trainer, + pl_module, + outputs: list[torch.Tensor], + batch: tuple[torch.Tensor, list], + batch_idx: int, + dataloader_idx: int = 0, + ) -> None: + + for b in range(len(outputs)): + predicted_image = outputs[b] + metadata = batch[1][b] + + slide_name = metadata["slide_name"] + level = metadata["level"] + original_image = Image.fromarray(metadata["original_image"].astype("uint8")) + filename = f'{metadata["xy"]}.png' + + base_folder = self.output_root / str(slide_name) / str(level) + predicted_folder = base_folder / "predicted" + original_folder = base_folder / "original" + + predicted_folder.mkdir(parents=True, exist_ok=True) + original_folder.mkdir(parents=True, exist_ok=True) + + original_image.save(original_folder / filename) + + predicted_image = self.denormalize(predicted_image).clamp(0, 1) + predicted_image = (predicted_image * 255).byte() + predicted_image = Image.fromarray(predicted_image.permute(1, 2, 0).cpu().numpy()) + predicted_image.save(predicted_folder / filename) diff --git a/stain_normalization/callbacks/tiles_export.py b/stain_normalization/callbacks/tiles_export.py index 7166dcd..d0897bd 100644 --- a/stain_normalization/callbacks/tiles_export.py +++ b/stain_normalization/callbacks/tiles_export.py @@ -6,57 +6,64 @@ class TilesExport(Callback): - def __init__(self, output_dir: str | Path, predict_normalization_config: DictConfig) -> None: + def __init__(self, output_dir: str | Path, normalization_config: DictConfig) -> None: super().__init__() self.output_dir = Path(output_dir) self.output_dir.mkdir(parents=True, exist_ok=True) - # Extract normalization parameters from the config (predict normalization) - normalization = predict_normalization_config + normalization = normalization_config self.mean = torch.tensor(normalization.mean).view(3, 1, 1) self.std = torch.tensor(normalization.std).view(3, 1, 1) - - + def denormalize(self, tensor: torch.Tensor) -> torch.Tensor: - device = tensor.device - + device = tensor.device std = self.std.to(device) mean = self.mean.to(device) - return (tensor * std) + mean - + def tensor_to_image(self, tensor: torch.Tensor) -> Image.Image: - """Convert a PyTorch tensor to a PIL image .""" tensor = self.denormalize(tensor) tensor = tensor.clamp(0, 1) tensor = (tensor * 255).byte() return Image.fromarray(tensor.permute(1, 2, 0).cpu().numpy()) + + def on_test_batch_end( + self, + trainer: Trainer, + pl_module: LightningModule, + outputs: list[torch.Tensor], + batch: tuple[torch.Tensor, list], + batch_idx: int, + dataloader_idx: int = 0, + ) -> None: + self._save_images(outputs, batch, is_predict=False) def on_predict_batch_end( self, trainer: Trainer, pl_module: LightningModule, - outputs: list[torch.Tensor], + outputs: list[torch.Tensor], batch: tuple[torch.Tensor, list], batch_idx: int, dataloader_idx: int = 0, ) -> None: - """ - Saves three images per sample: - - Original Image (ground truth) - - Modified Image (input to model) - - Predicted Image (model output) - """ + self._save_images(outputs, batch, is_predict=True) + + def _save_images(self, outputs, batch, is_predict: bool) -> None: + inputs, data = batch for b in range(len(outputs)): - predicted_image = outputs[b] - original_image = Image.fromarray(batch[1][b]["original_image"].astype("uint8")) - modified_image = Image.fromarray((batch[1][b]["modified_image"] * 255).astype("uint8")) - index = batch[1][b]["index"] + index = data[b].get("index", 0) + predicted_image = outputs[b] + name = data[b].get("name", str(index)) predicted_image = self.tensor_to_image(predicted_image) - # Save images - original_image.save(self.output_dir / f"{index}_original.png") - modified_image.save(self.output_dir / f"{index}_modified.png") - predicted_image.save(self.output_dir / f"{index}_predicted.png") + predicted_image.save(self.output_dir / f"{name}.png") + + if not is_predict: + original_image = Image.fromarray(data[b]["original_image"].astype("uint8")) + original_image.save(self.output_dir / f"{name}_original.png") + if "modified_image" in data[b]: + modified_image = Image.fromarray((data[b]["modified_image"] * 255).astype("uint8")) + modified_image.save(self.output_dir / f"{name}_modified.png") diff --git a/stain_normalization/data/data_module.py b/stain_normalization/data/data_module.py index 88a9138..c956a65 100644 --- a/stain_normalization/data/data_module.py +++ b/stain_normalization/data/data_module.py @@ -30,7 +30,6 @@ def setup(self, stage: str) -> None: case "test": self.test = instantiate(self.datasets["test"]) case "predict": - # this need predict val is for debuging only self.predict = instantiate(self.datasets["predict"]) def train_dataloader(self) -> Iterable[Input]: @@ -53,7 +52,10 @@ def val_dataloader(self) -> Iterable[Input]: def test_dataloader(self) -> Iterable[Input]: return DataLoader( - self.test, batch_size=self.batch_size, num_workers=self.num_workers + self.test, + batch_size=self.batch_size, + num_workers=self.num_workers, + collate_fn=collate_fn, ) def predict_dataloader(self) -> Iterable[tuple[Tensor, list[dict[str, Any]]]]: diff --git a/stain_normalization/data/datasets/__init__.py b/stain_normalization/data/datasets/__init__.py index 32125cf..7047900 100644 --- a/stain_normalization/data/datasets/__init__.py +++ b/stain_normalization/data/datasets/__init__.py @@ -1,4 +1,5 @@ -from stain_normalization.data.datasets.stain_normalization import StainNormalization, StainNormalizationPredict +from stain_normalization.data.datasets.train_dataset import TrainDataset +from stain_normalization.data.datasets.predict_dataset import PredictDataset +from stain_normalization.data.datasets.test_dataset import TestDataset - -__all__ = ["StainNormalization", "StainNormalizationPredict"] +__all__ = ["TrainDataset", "PredictDataset", "TestDataset"] diff --git a/stain_normalization/data/datasets/predict_dataset.py b/stain_normalization/data/datasets/predict_dataset.py new file mode 100644 index 0000000..2e06f88 --- /dev/null +++ b/stain_normalization/data/datasets/predict_dataset.py @@ -0,0 +1,74 @@ +from collections.abc import Iterable +import numpy as np +import pandas as pd +from albumentations import Transform3D +from albumentations.pytorch import ToTensorV2 +from rationai.mlkit.data.datasets import MetaTiledSlides, OpenSlideTilesDataset +from torch.utils.data import Dataset +from stain_normalization.typing import PredictSample + +class PredictDataset(MetaTiledSlides[PredictSample]): + def __init__( + self, + uris: Iterable[str], + normalize: Transform3D | None = None, + ) -> None: + self.normalize = normalize + super().__init__(uris=uris) + + def generate_datasets(self) -> Iterable[Dataset[PredictSample]]: + return ( + _PredictSlideTiles( + slide, + tiles=self.filter_tiles_by_slide(slide["id"]), + normalize=self.normalize, + ) + for _, slide in self.slides.iterrows() + ) + + +class _PredictSlideTiles(Dataset[PredictSample]): + def __init__( + self, + slide_metadata: pd.Series, + tiles: pd.DataFrame, + normalize: Transform3D | None = None, + ) -> None: + super().__init__() + self.slide_tiles = OpenSlideTilesDataset( + slide_path=slide_metadata.path, + level=slide_metadata.level, + tile_extent_x=slide_metadata.tile_extent_x, + tile_extent_y=slide_metadata.tile_extent_y, + tiles=tiles, + ) + + self.normalize = normalize + self.to_tensor = ToTensorV2() + + def __len__(self) -> int: + return len(self.slide_tiles) + + def __getitem__(self, idx: int) -> PredictSample: + input_image_255 = self.slide_tiles[idx] + slide_name = self.slide_tiles.slide_path.stem + level = self.slide_tiles.level + x = self.slide_tiles.tiles.iloc[idx]["x"] + y = self.slide_tiles.tiles.iloc[idx]["y"] + + input_image = input_image_255 / 255.0 + + if self.normalize: + input_image = self.normalize(image=input_image)["image"] + + input_image = self.to_tensor(image=input_image)["image"] + + return input_image, {"original_image": input_image_255, + "modified_image": None, + "slide_name": slide_name, + "level":level , + "index": idx, + "xy":f"{x}_{y}", + "name":f"{slide_name}-{x}-{y}"} + + diff --git a/stain_normalization/data/datasets/stain_normalization.py b/stain_normalization/data/datasets/stain_normalization.py deleted file mode 100644 index 9203f9e..0000000 --- a/stain_normalization/data/datasets/stain_normalization.py +++ /dev/null @@ -1,156 +0,0 @@ -from collections.abc import Iterable - -import numpy as np -import pandas as pd -from albumentations import Transform3D -from albumentations.pytorch import ToTensorV2 -from rationai.mlkit.data.datasets import MetaTiledSlides, OpenSlideTilesDataset -from torch.utils.data import Dataset - -from stain_normalization.typing import PredictSample, Sample - - -class StainNormalization(MetaTiledSlides[Sample]): - def __init__( - self, - uris: Iterable[str], - modify: Transform3D, - normalize: Transform3D | None = None, - transforms: Transform3D | None = None, - ) -> None: - self.modify = modify - self.transforms = transforms - self.normalize = normalize - super().__init__(uris=uris) - - def generate_datasets(self) -> Iterable[Dataset[Sample]]: - return ( - _StainNormalizationTrainSlideTiles( - slide, - tiles=self.filter_tiles_by_slide(slide["id"]), - modify=self.modify, - normalize=self.normalize, - transforms=self.transforms, - ) - for _, slide in self.slides.iterrows() - ) - - -class StainNormalizationPredict(MetaTiledSlides[PredictSample]): - def __init__( - self, - uris: Iterable[str], - modify: Transform3D, - normalize: Transform3D | None = None, - transforms: Transform3D | None = None, - ) -> None: - self.modify = modify - self.normalize = normalize - self.transforms = transforms - super().__init__(uris=uris) - - def generate_datasets(self) -> Iterable[Dataset[PredictSample]]: - return ( - _StainNormalizationPredictSlideTiles( - slide, - tiles=self.filter_tiles_by_slide(slide["id"]), - modify=self.modify, - normalize=self.normalize, - transforms=self.transforms, - ) - for _, slide in self.slides.iterrows() - ) - - -class _StainNormalizationTrainSlideTiles(Dataset[Sample]): - def __init__( - self, - slide_metadata: pd.Series, - tiles: pd.DataFrame, - modify: Transform3D, - normalize: Transform3D | None = None, - transforms: Transform3D | None = None, - ) -> None: - super().__init__() - self.slide_tiles = OpenSlideTilesDataset( - slide_path=slide_metadata.path, - level=slide_metadata.level, - tile_extent_x=slide_metadata.tile_extent_x, - tile_extent_y=slide_metadata.tile_extent_y, - tiles=tiles, - ) - self.modify = modify - self.normalize = normalize - self.transforms = transforms - self.to_tensor = ToTensorV2() - - def __len__(self) -> int: - return len(self.slide_tiles) - - def __getitem__(self, idx: int) -> Sample: - original_image = self.slide_tiles[idx] - # Apply non-disruptive transformations such as rotation, flip - if self.transforms is not None: - original_image = self.transforms(image=original_image)["image"] - - # Create "wrong" image to use as input. Outputs image in float 0-1 - modified_image = self.modify(image=original_image)["image"] - original_image = original_image / 255.0 - - if self.normalize: - original_image = self.normalize(image=original_image)["image"] - modified_image = self.normalize(image=modified_image)["image"] - - original_image = self.to_tensor(image=original_image)["image"] - modified_image = self.to_tensor(image=modified_image)["image"] - - return modified_image, original_image - - - -class _StainNormalizationPredictSlideTiles(Dataset[PredictSample]): - def __init__( - self, - slide_metadata: pd.Series, - tiles: pd.DataFrame, - modify: Transform3D, - normalize: Transform3D | None = None, - transforms: Transform3D | None = None, - ) -> None: - super().__init__() - self.slide_tiles = OpenSlideTilesDataset( - slide_path=slide_metadata.path, - level=slide_metadata.level, - tile_extent_x=slide_metadata.tile_extent_x, - tile_extent_y=slide_metadata.tile_extent_y, - tiles=tiles, - ) - self.modify = modify - self.normalize = normalize - self.transforms = transforms - self.to_tensor = ToTensorV2() - - def __len__(self) -> int: - return len(self.slide_tiles) - - def __getitem__(self, idx: int) -> PredictSample: - original_image_255 = self.slide_tiles[idx] - # Apply non-disruptive transformations such as rotation, flip - if self.transforms is not None: - original_image_255 = self.transforms(image=original_image_255)["image"] - - # Create "wrong" image to use as input. Outputs image in float 0-1 - modified_image_raw = self.modify(image=original_image_255)["image"] - modified_image = modified_image_raw - original_image = original_image_255 / 255.0 - - if self.normalize: - original_image = self.normalize(image=original_image)["image"] - modified_image = self.normalize(image=modified_image)["image"] - - original_image = self.to_tensor(image=original_image)["image"] - modified_image = self.to_tensor(image=modified_image)["image"] - - - return modified_image, {"original_image": original_image_255, "modified_image": modified_image_raw, "index": idx} - diff --git a/stain_normalization/data/datasets/test_dataset.py b/stain_normalization/data/datasets/test_dataset.py new file mode 100644 index 0000000..95363d4 --- /dev/null +++ b/stain_normalization/data/datasets/test_dataset.py @@ -0,0 +1,80 @@ +from collections.abc import Iterable + +import numpy as np +import pandas as pd +from albumentations import Transform3D +from albumentations.pytorch import ToTensorV2 +from rationai.mlkit.data.datasets import MetaTiledSlides, OpenSlideTilesDataset +from torch.utils.data import Dataset + +from stain_normalization.typing import PredictSample, Sample + +class TestDataset(MetaTiledSlides[PredictSample]): + def __init__( + self, + uris: Iterable[str], + modify: Transform3D, + normalize: Transform3D | None = None, + ) -> None: + self.modify = modify + self.normalize = normalize + super().__init__(uris=uris) + + def generate_datasets(self) -> Iterable[Dataset[PredictSample]]: + return ( + _TestSlideTiles( + slide, + tiles=self.filter_tiles_by_slide(slide["id"]), + modify=self.modify, + normalize=self.normalize, + ) + for _, slide in self.slides.iterrows() + ) + + +class _TestSlideTiles(Dataset[PredictSample]): + def __init__( + self, + slide_metadata: pd.Series, + tiles: pd.DataFrame, + modify: Transform3D, + normalize: Transform3D | None = None, + ) -> None: + super().__init__() + self.slide_tiles = OpenSlideTilesDataset( + slide_path=slide_metadata.path, + level=slide_metadata.level, + tile_extent_x=slide_metadata.tile_extent_x, + tile_extent_y=slide_metadata.tile_extent_y, + tiles=tiles, + ) + self.modify = modify + self.normalize = normalize + self.to_tensor = ToTensorV2() + + def __len__(self) -> int: + return len(self.slide_tiles) + + def __getitem__(self, idx: int) -> PredictSample: + original_image_255 = self.slide_tiles[idx] + + # Create "wrong" image to use as input. Outputs image in float 0-1 + modified_image_raw = self.modify(image=original_image_255)["image"] + modified_image = modified_image_raw + original_image = original_image_255 / 255.0 + + if self.normalize: + original_image = self.normalize(image=original_image)["image"] + modified_image = self.normalize(image=modified_image)["image"] + + original_image = self.to_tensor(image=original_image)["image"] + modified_image = self.to_tensor(image=modified_image)["image"] + + + return (modified_image, + {"original_image_tensor":original_image, + "original_image": original_image_255, + "modified_image": modified_image_raw, + "index": idx} + ) + diff --git a/stain_normalization/data/datasets/train_dataset.py b/stain_normalization/data/datasets/train_dataset.py new file mode 100644 index 0000000..45187d2 --- /dev/null +++ b/stain_normalization/data/datasets/train_dataset.py @@ -0,0 +1,75 @@ +from collections.abc import Iterable + +import numpy as np +import pandas as pd +from albumentations import Transform3D +from albumentations.pytorch import ToTensorV2 +from rationai.mlkit.data.datasets import MetaTiledSlides, OpenSlideTilesDataset +from torch.utils.data import Dataset + +from stain_normalization.typing import Sample + + +class TrainDataset(MetaTiledSlides[Sample]): + def __init__( + self, + uris: Iterable[str], + modify: Transform3D, + normalize: Transform3D | None = None, + ) -> None: + self.modify = modify + self.normalize = normalize + super().__init__(uris=uris) + + def generate_datasets(self) -> Iterable[Dataset[Sample]]: + return ( + _TrainSlideTiles( + slide, + tiles=self.filter_tiles_by_slide(slide["id"]), + modify=self.modify, + normalize=self.normalize, + ) + for _, slide in self.slides.iterrows() + ) + + +class _TrainSlideTiles(Dataset[Sample]): + def __init__( + self, + slide_metadata: pd.Series, + tiles: pd.DataFrame, + modify: Transform3D, + normalize: Transform3D | None = None, + ) -> None: + super().__init__() + self.slide_tiles = OpenSlideTilesDataset( + slide_path=slide_metadata.path, + level=slide_metadata.level, + tile_extent_x=slide_metadata.tile_extent_x, + tile_extent_y=slide_metadata.tile_extent_y, + tiles=tiles, + ) + self.modify = modify + self.normalize = normalize + self.to_tensor = ToTensorV2() + + def __len__(self) -> int: + return len(self.slide_tiles) + + def __getitem__(self, idx: int) -> Sample: + original_image = self.slide_tiles[idx] + + # Create "wrong" image to use as input. Outputs image in float 0-1 + modified_image = self.modify(image=original_image)["image"] + original_image = original_image / 255.0 + + if self.normalize: + original_image = self.normalize(image=original_image)["image"] + modified_image = self.normalize(image=modified_image)["image"] + + original_image = self.to_tensor(image=original_image)["image"] + modified_image = self.to_tensor(image=modified_image)["image"] + + return modified_image, original_image + + diff --git a/stain_normalization/data/transformation/__init__.py b/stain_normalization/data/modification/__init__.py similarity index 100% rename from stain_normalization/data/transformation/__init__.py rename to stain_normalization/data/modification/__init__.py diff --git a/stain_normalization/data/transformation/combiend_modification.py b/stain_normalization/data/modification/combiend_modification.py similarity index 100% rename from stain_normalization/data/transformation/combiend_modification.py rename to stain_normalization/data/modification/combiend_modification.py diff --git a/stain_normalization/data/transformation/exposure_adjustment.py b/stain_normalization/data/modification/exposure_adjustment.py similarity index 57% rename from stain_normalization/data/transformation/exposure_adjustment.py rename to stain_normalization/data/modification/exposure_adjustment.py index 4cc9a41..fe25768 100644 --- a/stain_normalization/data/transformation/exposure_adjustment.py +++ b/stain_normalization/data/modification/exposure_adjustment.py @@ -9,13 +9,8 @@ def __init__(self, brightness_range: tuple[float, float] = (0.8, 1.2), always_ap self.brightness_range = brightness_range def apply(self, img, **params): - hed_img = separate_stains(img, hed_from_rgb) - brightness_factor = np.random.uniform(*self.brightness_range) - h, e, d = hed_img[:, :, 0], hed_img[:, :, 1], hed_img[:, :, 2] - h = np.clip(h * brightness_factor, 0, 1) - e = np.clip(e * brightness_factor, 0, 1) - d = np.clip(d * brightness_factor, 0, 1) + img = img.astype(np.float32) + img = np.clip(img*brightness_factor, 0.0, 1.0) - modified_rgb = combine_stains(np.stack((h, e, d), axis=-1), rgb_from_hed) - return modified_rgb + return img diff --git a/stain_normalization/data/transformation/hed_factor.py b/stain_normalization/data/modification/hed_factor.py similarity index 100% rename from stain_normalization/data/transformation/hed_factor.py rename to stain_normalization/data/modification/hed_factor.py diff --git a/stain_normalization/data/transformation/hvs_modification.py b/stain_normalization/data/modification/hvs_modification.py similarity index 100% rename from stain_normalization/data/transformation/hvs_modification.py rename to stain_normalization/data/modification/hvs_modification.py diff --git a/stain_normalization/modeling/backbone/__init__.py b/stain_normalization/modeling/backbone/__init__.py deleted file mode 100644 index 68da66e..0000000 --- a/stain_normalization/modeling/backbone/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from stain_normalization.modeling.backbone.resnet50 import resnet50 -from stain_normalization.modeling.backbone.vgg16 import vgg16 - - -__all__ = ["resnet50", "vgg16"] \ No newline at end of file diff --git a/stain_normalization/modeling/backbone/resnet50.py b/stain_normalization/modeling/backbone/resnet50.py deleted file mode 100644 index 7705385..0000000 --- a/stain_normalization/modeling/backbone/resnet50.py +++ /dev/null @@ -1,7 +0,0 @@ -import torchvision -from torch import nn - - -def resnet50(weights: str | None = None) -> nn.Module: - resnet = torchvision.models.resnet50(weights=weights) - return nn.Sequential(*(list(resnet.children())[:-2])) diff --git a/stain_normalization/modeling/backbone/vgg16.py b/stain_normalization/modeling/backbone/vgg16.py deleted file mode 100644 index 0864515..0000000 --- a/stain_normalization/modeling/backbone/vgg16.py +++ /dev/null @@ -1,6 +0,0 @@ -from torch import nn -import torchvision - -def vgg16(weights: str | None = None) -> nn.Module: - return torchvision.models.vgg16(weights=weights).features - diff --git a/stain_normalization/modeling/decode_head/__init__.py b/stain_normalization/modeling/decode_head/__init__.py deleted file mode 100644 index 3f19fab..0000000 --- a/stain_normalization/modeling/decode_head/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from stain_normalization.modeling.decode_head.unet import UNetDecoder - - -__all__ = ["UNetDecoder"] \ No newline at end of file diff --git a/stain_normalization/modeling/decode_head/unet.py b/stain_normalization/modeling/decode_head/unet.py deleted file mode 100644 index 57052aa..0000000 --- a/stain_normalization/modeling/decode_head/unet.py +++ /dev/null @@ -1,35 +0,0 @@ -import torch -import torch.nn as nn -import torch.nn.functional as F - -class UNetDecoder(nn.Module): - def __init__(self, in_channels=2048, out_channels=3): - super(UNetDecoder, self).__init__() - - # First upsampling block - self.upconv1 = nn.ConvTranspose2d(in_channels, 1024, kernel_size=2, stride=2) - self.conv1 = nn.Conv2d(1024, 512, kernel_size=3, padding=1) - - # Second upsampling block - self.upconv2 = nn.ConvTranspose2d(512, 256, kernel_size=2, stride=2) - self.conv2 = nn.Conv2d(256, 128, kernel_size=3, padding=1) - - # Third upsampling block - self.upconv3 = nn.ConvTranspose2d(128, 64, kernel_size=2, stride=2) - self.conv3 = nn.Conv2d(64, 64, kernel_size=3, padding=1) - - # Fourth upsampling block - self.upconv4 = nn.ConvTranspose2d(64, 32, kernel_size=2, stride=2) - self.conv4 = nn.Conv2d(32, 16, kernel_size=3, padding=1) - - # Final upsampling to match 512x512 resolution - self.upconv5 = nn.ConvTranspose2d(16, 8, kernel_size=2, stride=2) - self.conv5 = nn.Conv2d(8, out_channels, kernel_size=1) # 3-channel RGB output - - def forward(self, x): - x = F.relu(self.conv1(self.upconv1(x))) - x = F.relu(self.conv2(self.upconv2(x))) - x = F.relu(self.conv3(self.upconv3(x))) - x = F.relu(self.conv4(self.upconv4(x))) - x = self.conv5(self.upconv5(x)) # No activation; apply outside - return x diff --git a/stain_normalization/modeling/l1ssim_loss.py b/stain_normalization/modeling/l1ssim_loss.py index e921916..25e2e75 100644 --- a/stain_normalization/modeling/l1ssim_loss.py +++ b/stain_normalization/modeling/l1ssim_loss.py @@ -12,8 +12,8 @@ def __init__(self, lambda_dssim: float = 0.6, lambda_gdl: float = 0.2): self.lambda_gdl = lambda_gdl def forward(self, image: torch.Tensor, target_image: torch.Tensor) -> torch.Tensor: - Ll1 = F.l1_loss(image, target_image, reduction='mean') # L1 loss - ssim_loss = 1.0 - ssim(image, target_image) # SSIM loss + Ll1 = F.l1_loss(image, target_image, reduction='mean') + ssim_loss = 1.0 - ssim(image, target_image) gdl_loss = gradient_loss(image, target_image) # GDL for sharp edges diff --git a/stain_normalization/stain_normalization_model.py b/stain_normalization/stain_normalization_model.py index 9906481..7809a03 100644 --- a/stain_normalization/stain_normalization_model.py +++ b/stain_normalization/stain_normalization_model.py @@ -1,16 +1,16 @@ from lightning import LightningModule -from torch import Tensor, nn +from torch import Tensor, nn, stack from torch.optim.optimizer import Optimizer from stain_normalization.modeling import L1SSIMLoss, UNet from stain_normalization.typing import Input, Outputs from torch.optim import Adam from torch.optim.optimizer import Optimizer -from torchmetrics.image import PeakSignalNoiseRatio,StructuralSimilarityIndexMeasure +from torchmetrics.image import StructuralSimilarityIndexMeasure from torchmetrics import MetricCollection from typing import Any class StainNormalizationModel(LightningModule): - def __init__(self, backbone: nn.Module, decode_head: nn.Module) -> None: + def __init__(self) -> None: super().__init__() self.unet = UNet(in_channels=3,out_channels=3) self.criterion = L1SSIMLoss() @@ -18,7 +18,6 @@ def __init__(self, backbone: nn.Module, decode_head: nn.Module) -> None: self.val_metrics = MetricCollection( { "ssim": StructuralSimilarityIndexMeasure(), - # "psnr": PeakSignalNoiseRatio() } ) self.test_metrics = self.val_metrics.clone(prefix="test/") @@ -49,17 +48,19 @@ def validation_step(self, batch: Input) -> None: on_epoch=True, ) - def test_step(self, batch: Input) -> None: - inputs, targets = batch + def test_step(self, batch: Input) -> Outputs: + inputs, data = batch outputs = self(inputs) + targets = stack([item['original_image_tensor'] for item in data]) self.test_metrics.update(outputs, targets) self.log_dict( self.test_metrics, batch_size=len(inputs), on_epoch=True, ) + return outputs - def predict_step(self, batch: tuple[Tensor, Any], batch_idx: int, dataloader_idx: int = 0) -> Outputs: + def predict_step(self, batch: tuple[Tensor, Any], batch_idx: int) -> Outputs: inputs = batch[0] return self(inputs) diff --git a/stain_normalization/typing.py b/stain_normalization/typing.py index 70c55c4..15088b6 100644 --- a/stain_normalization/typing.py +++ b/stain_normalization/typing.py @@ -2,14 +2,6 @@ from torch import Tensor -# class Metadata(TypedDict): -# slide: str -# x: int -# y: int -# transormation: str -# PredictSample: TypeAlias = tuple[Tensor, Metadata] -# Sample: TypeAlias = tuple[Tensor, Tensor, Metadata] - PredictSample: TypeAlias = tuple[Tensor, Dict[str, Any]] Sample: TypeAlias = tuple[Tensor, Tensor] From 8beb39829e2cd61dbf790d0e1f5d601bd4d95766 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 16 May 2025 13:35:37 +0000 Subject: [PATCH 54/85] chore: formating fix --- job.py | 19 ------------ pdm.lock | 30 ++++++++++--------- preprocessing/mask_generator.py | 4 +-- preprocessing/tiler.py | 3 +- pyproject.toml | 1 - stain_normalization/callbacks/__init__.py | 5 ++-- .../callbacks/analysis_export.py | 9 +++--- stain_normalization/callbacks/save_wsi.py | 5 ++-- stain_normalization/callbacks/tiles_export.py | 5 ++-- stain_normalization/data/data_module.py | 6 ++-- stain_normalization/data/datasets/__init__.py | 5 ++-- .../data/datasets/predict_dataset.py | 4 ++- .../data/datasets/test_dataset.py | 4 +-- .../data/datasets/train_dataset.py | 1 - .../data/modification/__init__.py | 11 +++++-- .../modification/combiend_modification.py | 2 +- .../data/modification/exposure_adjustment.py | 1 - .../data/modification/hed_factor.py | 3 +- .../data/modification/hvs_modification.py | 2 +- stain_normalization/data/utils/collate_fn.py | 2 ++ stain_normalization/modeling/__init__.py | 2 ++ stain_normalization/modeling/l1ssim_loss.py | 5 ++-- stain_normalization/modeling/unet.py | 4 +-- .../stain_normalization_model.py | 14 +++++---- stain_normalization/typing.py | 5 ++-- 25 files changed, 77 insertions(+), 75 deletions(-) delete mode 100644 job.py diff --git a/job.py b/job.py deleted file mode 100644 index 6da6dee..0000000 --- a/job.py +++ /dev/null @@ -1,19 +0,0 @@ -from kube_jobs import Storage, submit_job - - -submit_job( - job_name="stain-normalization-train", - username="xlopatka", - cpu=10, - memory="128Gi", - gpu="A40", - shm="16384Mi", - script=[ - "git clone https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git", - "cd staining", - "git checkout feature/ml-stain-normalization", - "pdm sync --skip=post_install", - "pdm train", - ], - storage=Storage(mou=True), -) diff --git a/pdm.lock b/pdm.lock index 282e879..267b14f 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:5332e3a0a1aebc8df232aacd3e71bbc5bdbaeec0efddc8d0137eafb3cd6c2c32" +content_hash = "sha256:2f62924ed5f088a39fa44fe5eaa436688edca68488cf39f4b0f4da79ca23d64f" [[metadata.targets]] requires_python = ">=3.11" @@ -1914,17 +1914,6 @@ files = [ {file = "pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20"}, ] -[[package]] -name = "pkgconfig" -version = "1.5.5" -requires_python = ">=3.3,<4.0" -summary = "Interface Python with pkg-config" -groups = ["default"] -files = [ - {file = "pkgconfig-1.5.5-py3-none-any.whl", hash = "sha256:d20023bbeb42ee6d428a0fac6e0904631f545985a10cdd71a20aa58bc47a4209"}, - {file = "pkgconfig-1.5.5.tar.gz", hash = "sha256:deb4163ef11f75b520d822d9505c1f462761b4309b1bb713d08689759ea8b899"}, -] - [[package]] name = "platformdirs" version = "4.3.6" @@ -2248,11 +2237,10 @@ files = [ [[package]] name = "pyvips" version = "2.2.3" -summary = "binding for the libvips image processing library, API mode" +summary = "binding for the libvips image processing library, ABI mode" groups = ["default"] dependencies = [ "cffi>=1.0.0", - "pkgconfig", ] files = [ {file = "pyvips-2.2.3.tar.gz", hash = "sha256:43bceced0db492654c93008246a58a508e0373ae1621116b87b322f2ac72212f"}, @@ -2363,6 +2351,20 @@ dependencies = [ "torch>=2.2.0", ] +[[package]] +name = "rationai-staining" +version = "0.1.0" +requires_python = ">=3.11" +git = "ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git" +revision = "bd1d704d86468a42d2321e9041c746215255c099" +summary = "" +groups = ["default"] +dependencies = [ + "numpy>=1.26.4", + "pillow>=10.3.0", + "scikit-image>=0.23.2", +] + [[package]] name = "rationai-tiling" version = "1.1.0" diff --git a/preprocessing/mask_generator.py b/preprocessing/mask_generator.py index aa40f73..45cd285 100644 --- a/preprocessing/mask_generator.py +++ b/preprocessing/mask_generator.py @@ -1,9 +1,9 @@ from pathlib import Path + import pyvips import ray from openslide import PROPERTY_NAME_MPP_X, PROPERTY_NAME_MPP_Y, OpenSlide -from rationai.masks import tissue_mask, write_big_tiff -from rationai.masks import process_items +from rationai.masks import process_items, tissue_mask, write_big_tiff # folders: diff --git a/preprocessing/tiler.py b/preprocessing/tiler.py index fb54ac8..5ffb3a4 100644 --- a/preprocessing/tiler.py +++ b/preprocessing/tiler.py @@ -1,4 +1,5 @@ from pathlib import Path + import mlflow import ray from rationai.tiling import tiling @@ -98,7 +99,7 @@ def main() -> None: test_slides_df.to_csv("./data/datasets/test_slides.csv", index=False) test_tiles_df.to_csv("./data/datasets/test_tiles.csv", index=False) - mlflow.set_experiment(experiment_name="Stain Normalization") + mlflow.set_experiment(experiment_name="Stain-Normalization") with mlflow.start_run(run_name="Stain Normalization Dataset") as _: save_mlflow_dataset( slides=train_slides_df, diff --git a/pyproject.toml b/pyproject.toml index e498650..f8be213 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,6 @@ dependencies = [ "rationai-mlkit @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/mlkit.git", "rationai-masks @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git", "rationai-tiling @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git", - "rationai-kube-jobs @ git+ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/kube-jobs.git", "scikit-image>=0.25.2", "openslide-bin>=4.0.0.6", "rationai-staining @ git+ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git" diff --git a/stain_normalization/callbacks/__init__.py b/stain_normalization/callbacks/__init__.py index f78f0d7..344b24c 100644 --- a/stain_normalization/callbacks/__init__.py +++ b/stain_normalization/callbacks/__init__.py @@ -1,6 +1,7 @@ from stain_normalization.callbacks.analysis_export import AnalysisExport -from stain_normalization.callbacks.tiles_export import TilesExport from stain_normalization.callbacks.save_wsi import SaveWSI +from stain_normalization.callbacks.tiles_export import TilesExport + -__all__ = ["TileExport", "AnalysisExport", "SaveWSI"] +__all__ = ["AnalysisExport", "SaveWSI", "TileExport"] diff --git a/stain_normalization/callbacks/analysis_export.py b/stain_normalization/callbacks/analysis_export.py index 0abaa8f..bd48d21 100644 --- a/stain_normalization/callbacks/analysis_export.py +++ b/stain_normalization/callbacks/analysis_export.py @@ -1,11 +1,12 @@ +from pathlib import Path + import mlflow +import numpy as np import pandas as pd import torch -from pathlib import Path from lightning import Callback, LightningModule, Trainer -import numpy as np -from skimage.metrics import structural_similarity as ssim from rationai.staining import estimate_stain_vectors +from skimage.metrics import structural_similarity as ssim class AnalysisExport(Callback): @@ -91,7 +92,7 @@ def on_test_batch_end( for name, vec, nmi in zip( ["original", "modified", "predicted"], [vec_original, vec_modified, vec_predicted], - [nmi_original, nmi_modified, nmi_predicted] + [nmi_original, nmi_modified, nmi_predicted], strict=False ): vectors = vec.flatten() for j, val in enumerate(vectors): diff --git a/stain_normalization/callbacks/save_wsi.py b/stain_normalization/callbacks/save_wsi.py index 614a466..7bec981 100644 --- a/stain_normalization/callbacks/save_wsi.py +++ b/stain_normalization/callbacks/save_wsi.py @@ -1,8 +1,9 @@ -from lightning.pytorch.callbacks import Callback from pathlib import Path + import torch -from PIL import Image +from lightning.pytorch.callbacks import Callback from omegaconf import DictConfig +from PIL import Image class SaveWSI(Callback): diff --git a/stain_normalization/callbacks/tiles_export.py b/stain_normalization/callbacks/tiles_export.py index d0897bd..724d92e 100644 --- a/stain_normalization/callbacks/tiles_export.py +++ b/stain_normalization/callbacks/tiles_export.py @@ -1,8 +1,9 @@ from pathlib import Path + +import torch from lightning import Callback, LightningModule, Trainer -from PIL import Image from omegaconf import DictConfig -import torch +from PIL import Image class TilesExport(Callback): diff --git a/stain_normalization/data/data_module.py b/stain_normalization/data/data_module.py index c956a65..5fb8521 100644 --- a/stain_normalization/data/data_module.py +++ b/stain_normalization/data/data_module.py @@ -1,14 +1,14 @@ -from typing import Any from collections.abc import Iterable -from torch import Tensor +from typing import Any from hydra.utils import instantiate from lightning import LightningDataModule from omegaconf import DictConfig +from torch import Tensor from torch.utils.data import DataLoader -from stain_normalization.typing import Input from stain_normalization.data.utils import collate_fn +from stain_normalization.typing import Input class DataModule(LightningDataModule): diff --git a/stain_normalization/data/datasets/__init__.py b/stain_normalization/data/datasets/__init__.py index 7047900..a767f70 100644 --- a/stain_normalization/data/datasets/__init__.py +++ b/stain_normalization/data/datasets/__init__.py @@ -1,5 +1,6 @@ -from stain_normalization.data.datasets.train_dataset import TrainDataset from stain_normalization.data.datasets.predict_dataset import PredictDataset from stain_normalization.data.datasets.test_dataset import TestDataset +from stain_normalization.data.datasets.train_dataset import TrainDataset + -__all__ = ["TrainDataset", "PredictDataset", "TestDataset"] +__all__ = ["PredictDataset", "TestDataset", "TrainDataset"] diff --git a/stain_normalization/data/datasets/predict_dataset.py b/stain_normalization/data/datasets/predict_dataset.py index 2e06f88..1871f94 100644 --- a/stain_normalization/data/datasets/predict_dataset.py +++ b/stain_normalization/data/datasets/predict_dataset.py @@ -1,12 +1,14 @@ from collections.abc import Iterable -import numpy as np + import pandas as pd from albumentations import Transform3D from albumentations.pytorch import ToTensorV2 from rationai.mlkit.data.datasets import MetaTiledSlides, OpenSlideTilesDataset from torch.utils.data import Dataset + from stain_normalization.typing import PredictSample + class PredictDataset(MetaTiledSlides[PredictSample]): def __init__( self, diff --git a/stain_normalization/data/datasets/test_dataset.py b/stain_normalization/data/datasets/test_dataset.py index 95363d4..4779d06 100644 --- a/stain_normalization/data/datasets/test_dataset.py +++ b/stain_normalization/data/datasets/test_dataset.py @@ -1,13 +1,13 @@ from collections.abc import Iterable -import numpy as np import pandas as pd from albumentations import Transform3D from albumentations.pytorch import ToTensorV2 from rationai.mlkit.data.datasets import MetaTiledSlides, OpenSlideTilesDataset from torch.utils.data import Dataset -from stain_normalization.typing import PredictSample, Sample +from stain_normalization.typing import PredictSample + class TestDataset(MetaTiledSlides[PredictSample]): def __init__( diff --git a/stain_normalization/data/datasets/train_dataset.py b/stain_normalization/data/datasets/train_dataset.py index 45187d2..d64e26f 100644 --- a/stain_normalization/data/datasets/train_dataset.py +++ b/stain_normalization/data/datasets/train_dataset.py @@ -1,6 +1,5 @@ from collections.abc import Iterable -import numpy as np import pandas as pd from albumentations import Transform3D from albumentations.pytorch import ToTensorV2 diff --git a/stain_normalization/data/modification/__init__.py b/stain_normalization/data/modification/__init__.py index b817f7b..a43111a 100644 --- a/stain_normalization/data/modification/__init__.py +++ b/stain_normalization/data/modification/__init__.py @@ -1,7 +1,12 @@ +from stain_normalization.data.transformation.combiend_modification import ( + CombinedModifications, +) +from stain_normalization.data.transformation.exposure_adjustment import ( + ExposureAdjustment, +) from stain_normalization.data.transformation.hed_factor import HEDFactor -from stain_normalization.data.transformation.exposure_adjustment import ExposureAdjustment from stain_normalization.data.transformation.hvs_modification import HVSModification -from stain_normalization.data.transformation.combiend_modification import CombinedModifications -__all__ = ["HEDFactor", "ExposureAdjustment", "HVSModification", "CombinedModifications"] + +__all__ = ["CombinedModifications", "ExposureAdjustment", "HEDFactor", "HVSModification"] diff --git a/stain_normalization/data/modification/combiend_modification.py b/stain_normalization/data/modification/combiend_modification.py index 610ebcb..773d804 100644 --- a/stain_normalization/data/modification/combiend_modification.py +++ b/stain_normalization/data/modification/combiend_modification.py @@ -1,7 +1,7 @@ import numpy as np from albumentations import ImageOnlyTransform from skimage import exposure -from skimage.color import separate_stains, combine_stains, hed_from_rgb, rgb_from_hed +from skimage.color import combine_stains, hed_from_rgb, rgb_from_hed, separate_stains class CombinedModifications(ImageOnlyTransform): diff --git a/stain_normalization/data/modification/exposure_adjustment.py b/stain_normalization/data/modification/exposure_adjustment.py index fe25768..bd0cc1a 100644 --- a/stain_normalization/data/modification/exposure_adjustment.py +++ b/stain_normalization/data/modification/exposure_adjustment.py @@ -1,6 +1,5 @@ import numpy as np from albumentations import ImageOnlyTransform -from skimage.color import separate_stains, combine_stains, hed_from_rgb, rgb_from_hed class ExposureAdjustment(ImageOnlyTransform): diff --git a/stain_normalization/data/modification/hed_factor.py b/stain_normalization/data/modification/hed_factor.py index 80af22d..6fe8c40 100644 --- a/stain_normalization/data/modification/hed_factor.py +++ b/stain_normalization/data/modification/hed_factor.py @@ -1,6 +1,7 @@ import numpy as np from albumentations import ImageOnlyTransform -from skimage.color import separate_stains, combine_stains, hed_from_rgb, rgb_from_hed +from skimage.color import combine_stains, hed_from_rgb, rgb_from_hed, separate_stains + class HEDFactor(ImageOnlyTransform): def __init__(self, diff --git a/stain_normalization/data/modification/hvs_modification.py b/stain_normalization/data/modification/hvs_modification.py index 21e36d5..71f2138 100644 --- a/stain_normalization/data/modification/hvs_modification.py +++ b/stain_normalization/data/modification/hvs_modification.py @@ -2,7 +2,7 @@ import numpy as np from albumentations import ImageOnlyTransform -from skimage.color import rgb2hsv, hsv2rgb +from skimage.color import hsv2rgb, rgb2hsv class HVSModification(ImageOnlyTransform): diff --git a/stain_normalization/data/utils/collate_fn.py b/stain_normalization/data/utils/collate_fn.py index 667897e..3455e79 100644 --- a/stain_normalization/data/utils/collate_fn.py +++ b/stain_normalization/data/utils/collate_fn.py @@ -1,6 +1,8 @@ from typing import Any + import torch from torch import Tensor + def collate_fn(batch: list[tuple[Tensor, Any]]) -> tuple[Tensor, list[Any]]: return torch.stack([x[0] for x in batch]), [x[1] for x in batch] diff --git a/stain_normalization/modeling/__init__.py b/stain_normalization/modeling/__init__.py index 2c4be67..e199cc5 100644 --- a/stain_normalization/modeling/__init__.py +++ b/stain_normalization/modeling/__init__.py @@ -1,3 +1,5 @@ from .l1ssim_loss import L1SSIMLoss from .unet import UNet + + __all__ = ["L1SSIMLoss", "UNet"] \ No newline at end of file diff --git a/stain_normalization/modeling/l1ssim_loss.py b/stain_normalization/modeling/l1ssim_loss.py index 25e2e75..263ffaf 100644 --- a/stain_normalization/modeling/l1ssim_loss.py +++ b/stain_normalization/modeling/l1ssim_loss.py @@ -1,8 +1,9 @@ -import torch.nn.functional as F +from math import exp + import torch import torch.nn as nn +import torch.nn.functional as F from torch.autograd import Variable -from math import exp class L1SSIMLoss(nn.Module): diff --git a/stain_normalization/modeling/unet.py b/stain_normalization/modeling/unet.py index 485bc9b..f83735d 100644 --- a/stain_normalization/modeling/unet.py +++ b/stain_normalization/modeling/unet.py @@ -1,5 +1,4 @@ -""" -Model created using segmentation_models_pytorch library: +"""Model created using segmentation_models_pytorch library: # Adapted U-Net implementation based on https://github.com/milesial/Pytorch-UNet Ronneberger, O., Fischer, P., & Brox, T. (2015). @@ -12,6 +11,7 @@ import torch.nn as nn import torch.nn.functional as F + class DoubleConv(nn.Module): """(convolution => [BN] => ReLU) * 2""" diff --git a/stain_normalization/stain_normalization_model.py b/stain_normalization/stain_normalization_model.py index 7809a03..f4a0b05 100644 --- a/stain_normalization/stain_normalization_model.py +++ b/stain_normalization/stain_normalization_model.py @@ -1,13 +1,15 @@ +from typing import Any + from lightning import LightningModule -from torch import Tensor, nn, stack +from torch import Tensor, stack +from torch.optim import Adam from torch.optim.optimizer import Optimizer +from torchmetrics import MetricCollection +from torchmetrics.image import StructuralSimilarityIndexMeasure + from stain_normalization.modeling import L1SSIMLoss, UNet from stain_normalization.typing import Input, Outputs -from torch.optim import Adam -from torch.optim.optimizer import Optimizer -from torchmetrics.image import StructuralSimilarityIndexMeasure -from torchmetrics import MetricCollection -from typing import Any + class StainNormalizationModel(LightningModule): def __init__(self) -> None: diff --git a/stain_normalization/typing.py b/stain_normalization/typing.py index 15088b6..c507266 100644 --- a/stain_normalization/typing.py +++ b/stain_normalization/typing.py @@ -1,8 +1,9 @@ -from typing import TypeAlias, TypedDict, Dict, Any +from typing import Any, TypeAlias from torch import Tensor -PredictSample: TypeAlias = tuple[Tensor, Dict[str, Any]] + +PredictSample: TypeAlias = tuple[Tensor, dict[str, Any]] Sample: TypeAlias = tuple[Tensor, Tensor] Input: TypeAlias = Sample From 082e5e13b07193b1faaea75374baab458df0d6cc Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 16 May 2025 14:05:33 +0000 Subject: [PATCH 55/85] chore: formating fix --- preprocessing/mask_generator.py | 51 ++++++++++--- preprocessing/tiler.py | 73 ++++++++++++++----- stain_normalization/callbacks/__init__.py | 3 +- .../callbacks/analysis_export.py | 15 ++-- stain_normalization/callbacks/save_wsi.py | 13 ++-- stain_normalization/callbacks/tiles_export.py | 24 +++--- stain_normalization/data/data_module.py | 10 +-- .../data/datasets/predict_dataset.py | 28 +++---- .../data/datasets/test_dataset.py | 23 +++--- .../data/datasets/train_dataset.py | 10 +-- .../data/modification/__init__.py | 8 +- .../modification/combiend_modification.py | 18 ++++- .../data/modification/exposure_adjustment.py | 11 ++- .../data/modification/hed_factor.py | 23 ++++-- .../data/modification/hvs_modification.py | 7 +- stain_normalization/modeling/__init__.py | 6 +- stain_normalization/modeling/l1ssim_loss.py | 67 ++++++++++------- stain_normalization/modeling/unet.py | 48 ++++++------ .../stain_normalization_model.py | 10 +-- stain_normalization/typing.py | 2 - 20 files changed, 280 insertions(+), 170 deletions(-) diff --git a/preprocessing/mask_generator.py b/preprocessing/mask_generator.py index 45cd285..0c0bc98 100644 --- a/preprocessing/mask_generator.py +++ b/preprocessing/mask_generator.py @@ -24,10 +24,8 @@ def process_slide(slide_path: Path) -> None: # extract proper spatial resulution based on desired level using OpenSlide with OpenSlide(slide_path) as slide: downsample = slide.level_downsamples[LEVEL] - xres = 1000 / \ - (float(slide.properties[PROPERTY_NAME_MPP_X]) * downsample) - yres = 1000 / \ - (float(slide.properties[PROPERTY_NAME_MPP_Y]) * downsample) + xres = 1000 / (float(slide.properties[PROPERTY_NAME_MPP_X]) * downsample) + yres = 1000 / (float(slide.properties[PROPERTY_NAME_MPP_Y]) * downsample) slide = pyvips.Image.new_from_file(slide_path, level=LEVEL) mask = tissue_mask(slide) @@ -37,10 +35,45 @@ def process_slide(slide_path: Path) -> None: BROKEN_SLIDES = { - 'P-2016_3829-04-1.mrxs', 'P-2016_3732-06-1.mrxs', 'P-2016_3732-03-1.mrxs', 'P-2016_3760-06-1.mrxs', 'P-2016_3629-13-1.mrxs', 'P-2016_3926-03-1.mrxs', - 'P-2016_3852-02-1.mrxs', 'P-2016_3988-07-1.mrxs', 'P-2016_3852-01-1.mrxs', - 'P-2016_3851-02-1.mrxs', 'P-2016_3629-12-1.mrxs', 'P-2016_3667-10-0.mrxs', 'P-2016_3606-04-1.mrxs', 'P-2016_3597-10-1.mrxs', 'P-2016_3988-10-1.mrxs', 'P-2016_3829-01-1.mrxs', 'P-2016_3926-02-1.mrxs', 'P-2019_3025-03-1.mrxs', - 'P-2016_3760-04-1.mrxs', 'P-2016_3732-04-1.mrxs', 'P-2016_3667-09-1.mrxs', 'P-2019_3292-06-1.mrxs', 'P-2016_3988-02-1.mrxs', 'P-2016_3667-11-0.mrxs', 'P-2016_3606-05-1.mrxs', 'P-2016_3851-09-1.mrxs', 'P-2016_3627-09-1.mrxs', 'P-2016_3606-03-1.mrxs', 'P-2016_3627-10-1.mrxs', 'P-2016_3852-03-1.mrxs', 'P-2016_3851-07-1.mrxs', 'P-2016_3829-03-1.mrxs', 'P-2016_3627-06-1.mrxs', 'P-2016_3597-09-1.mrxs', 'P-2016_3926-01-1.mrxs', 'P-2016_3629-11-1.mrxs', 'P-2016_3760-03-1.mrxs'} + "P-2016_3829-04-1.mrxs", + "P-2016_3732-06-1.mrxs", + "P-2016_3732-03-1.mrxs", + "P-2016_3760-06-1.mrxs", + "P-2016_3629-13-1.mrxs", + "P-2016_3926-03-1.mrxs", + "P-2016_3852-02-1.mrxs", + "P-2016_3988-07-1.mrxs", + "P-2016_3852-01-1.mrxs", + "P-2016_3851-02-1.mrxs", + "P-2016_3629-12-1.mrxs", + "P-2016_3667-10-0.mrxs", + "P-2016_3606-04-1.mrxs", + "P-2016_3597-10-1.mrxs", + "P-2016_3988-10-1.mrxs", + "P-2016_3829-01-1.mrxs", + "P-2016_3926-02-1.mrxs", + "P-2019_3025-03-1.mrxs", + "P-2016_3760-04-1.mrxs", + "P-2016_3732-04-1.mrxs", + "P-2016_3667-09-1.mrxs", + "P-2019_3292-06-1.mrxs", + "P-2016_3988-02-1.mrxs", + "P-2016_3667-11-0.mrxs", + "P-2016_3606-05-1.mrxs", + "P-2016_3851-09-1.mrxs", + "P-2016_3627-09-1.mrxs", + "P-2016_3606-03-1.mrxs", + "P-2016_3627-10-1.mrxs", + "P-2016_3852-03-1.mrxs", + "P-2016_3851-07-1.mrxs", + "P-2016_3829-03-1.mrxs", + "P-2016_3627-06-1.mrxs", + "P-2016_3597-09-1.mrxs", + "P-2016_3926-01-1.mrxs", + "P-2016_3629-11-1.mrxs", + "P-2016_3760-03-1.mrxs", +} + def main() -> None: folders = [ @@ -49,7 +82,7 @@ def main() -> None: # "Prospective negative cases", #DONE # "Prospective test cases", # DONE # "Prospective tumor cases" # DONE - ] + ] for folder in folders: slides = [] diff --git a/preprocessing/tiler.py b/preprocessing/tiler.py index 5ffb3a4..87f7062 100644 --- a/preprocessing/tiler.py +++ b/preprocessing/tiler.py @@ -56,20 +56,56 @@ def handler(slide_path: Path) -> TiledSlideMetadata: return slide, tiles + BROKEN_SLIDES = { - 'P-2016_3829-04-1.mrxs', 'P-2016_3732-06-1.mrxs', 'P-2016_3732-03-1.mrxs', 'P-2016_3760-06-1.mrxs', 'P-2016_3629-13-1.mrxs', 'P-2016_3926-03-1.mrxs', - 'P-2016_3852-02-1.mrxs', 'P-2016_3988-07-1.mrxs', 'P-2016_3852-01-1.mrxs', - 'P-2016_3851-02-1.mrxs', 'P-2016_3629-12-1.mrxs', 'P-2016_3667-10-0.mrxs', 'P-2016_3606-04-1.mrxs', 'P-2016_3597-10-1.mrxs', 'P-2016_3988-10-1.mrxs', 'P-2016_3829-01-1.mrxs', 'P-2016_3926-02-1.mrxs', 'P-2019_3025-03-1.mrxs', - 'P-2016_3760-04-1.mrxs', 'P-2016_3732-04-1.mrxs', 'P-2016_3667-09-1.mrxs', 'P-2019_3292-06-1.mrxs', 'P-2016_3988-02-1.mrxs', 'P-2016_3667-11-0.mrxs', 'P-2016_3606-05-1.mrxs', 'P-2016_3851-09-1.mrxs', 'P-2016_3627-09-1.mrxs', 'P-2016_3606-03-1.mrxs', 'P-2016_3627-10-1.mrxs', 'P-2016_3852-03-1.mrxs', 'P-2016_3851-07-1.mrxs', 'P-2016_3829-03-1.mrxs', 'P-2016_3627-06-1.mrxs', 'P-2016_3597-09-1.mrxs', 'P-2016_3926-01-1.mrxs', 'P-2016_3629-11-1.mrxs', 'P-2016_3760-03-1.mrxs'} + "P-2016_3829-04-1.mrxs", + "P-2016_3732-06-1.mrxs", + "P-2016_3732-03-1.mrxs", + "P-2016_3760-06-1.mrxs", + "P-2016_3629-13-1.mrxs", + "P-2016_3926-03-1.mrxs", + "P-2016_3852-02-1.mrxs", + "P-2016_3988-07-1.mrxs", + "P-2016_3852-01-1.mrxs", + "P-2016_3851-02-1.mrxs", + "P-2016_3629-12-1.mrxs", + "P-2016_3667-10-0.mrxs", + "P-2016_3606-04-1.mrxs", + "P-2016_3597-10-1.mrxs", + "P-2016_3988-10-1.mrxs", + "P-2016_3829-01-1.mrxs", + "P-2016_3926-02-1.mrxs", + "P-2019_3025-03-1.mrxs", + "P-2016_3760-04-1.mrxs", + "P-2016_3732-04-1.mrxs", + "P-2016_3667-09-1.mrxs", + "P-2019_3292-06-1.mrxs", + "P-2016_3988-02-1.mrxs", + "P-2016_3667-11-0.mrxs", + "P-2016_3606-05-1.mrxs", + "P-2016_3851-09-1.mrxs", + "P-2016_3627-09-1.mrxs", + "P-2016_3606-03-1.mrxs", + "P-2016_3627-10-1.mrxs", + "P-2016_3852-03-1.mrxs", + "P-2016_3851-07-1.mrxs", + "P-2016_3829-03-1.mrxs", + "P-2016_3627-06-1.mrxs", + "P-2016_3597-09-1.mrxs", + "P-2016_3926-01-1.mrxs", + "P-2016_3629-11-1.mrxs", + "P-2016_3760-03-1.mrxs", +} + def main() -> None: folders = [ "archive tumor cases", - "archive negative cases", - "Prospective negative cases", - "Prospective test cases", - "Prospective tumor cases" - ] + "archive negative cases", + "Prospective negative cases", + "Prospective test cases", + "Prospective tumor cases", + ] slides = [] for folder in folders: @@ -78,18 +114,13 @@ def main() -> None: continue slides.append(slide) - slides, test_slides = train_test_split( - slides, test_size=0.2 - ) + slides, test_slides = train_test_split(slides, test_size=0.2) train_slides, val_slides = train_test_split(slides, test_size=0.1) - train_slides_df, train_tiles_df = tiling( - slides=train_slides, handler=handler) - val_slides_df, val_tiles_df = tiling( - slides=list(val_slides), handler=handler) - test_slides_df, test_tiles_df = tiling( - slides=list(test_slides), handler=handler) - + train_slides_df, train_tiles_df = tiling(slides=train_slides, handler=handler) + val_slides_df, val_tiles_df = tiling(slides=list(val_slides), handler=handler) + test_slides_df, test_tiles_df = tiling(slides=list(test_slides), handler=handler) + train_slides_df.to_csv("./data/datasets/train_slides.csv", index=False) train_tiles_df.to_csv("./data/datasets/train_tiles.csv", index=False) @@ -107,7 +138,9 @@ def main() -> None: dataset_name="Stain Normalization - train", ) save_mlflow_dataset( - slides=val_slides_df, tiles=val_tiles_df, dataset_name="Stain Normalization - val" + slides=val_slides_df, + tiles=val_tiles_df, + dataset_name="Stain Normalization - val", ) save_mlflow_dataset( slides=test_slides_df, diff --git a/stain_normalization/callbacks/__init__.py b/stain_normalization/callbacks/__init__.py index 344b24c..86a8ce0 100644 --- a/stain_normalization/callbacks/__init__.py +++ b/stain_normalization/callbacks/__init__.py @@ -3,5 +3,4 @@ from stain_normalization.callbacks.tiles_export import TilesExport -__all__ = ["AnalysisExport", "SaveWSI", "TileExport"] - +__all__ = ["AnalysisExport", "SaveWSI", "TilesExport"] diff --git a/stain_normalization/callbacks/analysis_export.py b/stain_normalization/callbacks/analysis_export.py index bd48d21..3bfbf01 100644 --- a/stain_normalization/callbacks/analysis_export.py +++ b/stain_normalization/callbacks/analysis_export.py @@ -40,13 +40,14 @@ def _compare_vectors(self, vec1: np.ndarray, vec2: np.ndarray) -> float: def _compute_ssim(self, img1: np.ndarray, img2: np.ndarray) -> float: return float(ssim(img1, img2, channel_axis=-1, data_range=255)) - def _compute_nmi(self, img: np.ndarray): # normalized median intensity + def _compute_nmi(self, img: np.ndarray): # normalized median intensity avg_rgb = img.mean(axis=2) median_val = np.median(avg_rgb) p95_val = np.percentile(avg_rgb, 95) nmi = median_val / p95_val if p95_val != 0 else 0 return nmi + def on_test_batch_end( self, trainer: Trainer, @@ -73,7 +74,6 @@ def on_test_batch_end( ssim_mod = self._compute_ssim(original_np, modified_np) ssim_pred = self._compute_ssim(original_np, predicted_np) - nmi_original = self.compute_nmi(original_np) nmi_modified = self.compute_nmi(modified_np) nmi_predicted = self.compute_nmi(predicted_np) @@ -92,15 +92,20 @@ def on_test_batch_end( for name, vec, nmi in zip( ["original", "modified", "predicted"], [vec_original, vec_modified, vec_predicted], - [nmi_original, nmi_modified, nmi_predicted], strict=False + [nmi_original, nmi_modified, nmi_predicted], + strict=False, ): vectors = vec.flatten() for j, val in enumerate(vectors): raw_row[f"{name}_vec_{j}"] = val raw_row[f"{name}_nmi"] = nmi - self.df_diff = pd.concat([self.df_diff, pd.DataFrame([diff_row])], ignore_index=True) - self.df_raw = pd.concat([self.df_raw, pd.DataFrame([raw_row])], ignore_index=True) + self.df_diff = pd.concat( + [self.df_diff, pd.DataFrame([diff_row])], ignore_index=True + ) + self.df_raw = pd.concat( + [self.df_raw, pd.DataFrame([raw_row])], ignore_index=True + ) def on_predict_end( self, diff --git a/stain_normalization/callbacks/save_wsi.py b/stain_normalization/callbacks/save_wsi.py index 7bec981..b74ba83 100644 --- a/stain_normalization/callbacks/save_wsi.py +++ b/stain_normalization/callbacks/save_wsi.py @@ -7,7 +7,9 @@ class SaveWSI(Callback): - def __init__(self, output_dir: str | Path, normalization_config: DictConfig) -> None: + def __init__( + self, output_dir: str | Path, normalization_config: DictConfig + ) -> None: super().__init__() self.output_root = Path(output_dir) self.mean = torch.tensor(normalization_config.mean).view(3, 1, 1) @@ -28,15 +30,14 @@ def on_predict_batch_end( batch_idx: int, dataloader_idx: int = 0, ) -> None: - for b in range(len(outputs)): predicted_image = outputs[b] metadata = batch[1][b] - + slide_name = metadata["slide_name"] level = metadata["level"] original_image = Image.fromarray(metadata["original_image"].astype("uint8")) - filename = f'{metadata["xy"]}.png' + filename = f"{metadata['xy']}.png" base_folder = self.output_root / str(slide_name) / str(level) predicted_folder = base_folder / "predicted" @@ -49,5 +50,7 @@ def on_predict_batch_end( predicted_image = self.denormalize(predicted_image).clamp(0, 1) predicted_image = (predicted_image * 255).byte() - predicted_image = Image.fromarray(predicted_image.permute(1, 2, 0).cpu().numpy()) + predicted_image = Image.fromarray( + predicted_image.permute(1, 2, 0).cpu().numpy() + ) predicted_image.save(predicted_folder / filename) diff --git a/stain_normalization/callbacks/tiles_export.py b/stain_normalization/callbacks/tiles_export.py index 724d92e..0c44f07 100644 --- a/stain_normalization/callbacks/tiles_export.py +++ b/stain_normalization/callbacks/tiles_export.py @@ -7,7 +7,9 @@ class TilesExport(Callback): - def __init__(self, output_dir: str | Path, normalization_config: DictConfig) -> None: + def __init__( + self, output_dir: str | Path, normalization_config: DictConfig + ) -> None: super().__init__() self.output_dir = Path(output_dir) self.output_dir.mkdir(parents=True, exist_ok=True) @@ -15,19 +17,19 @@ def __init__(self, output_dir: str | Path, normalization_config: DictConfig) -> normalization = normalization_config self.mean = torch.tensor(normalization.mean).view(3, 1, 1) self.std = torch.tensor(normalization.std).view(3, 1, 1) - + def denormalize(self, tensor: torch.Tensor) -> torch.Tensor: device = tensor.device std = self.std.to(device) mean = self.mean.to(device) return (tensor * std) + mean - + def tensor_to_image(self, tensor: torch.Tensor) -> Image.Image: - tensor = self.denormalize(tensor) - tensor = tensor.clamp(0, 1) + tensor = self.denormalize(tensor) + tensor = tensor.clamp(0, 1) tensor = (tensor * 255).byte() - return Image.fromarray(tensor.permute(1, 2, 0).cpu().numpy()) - + return Image.fromarray(tensor.permute(1, 2, 0).cpu().numpy()) + def on_test_batch_end( self, trainer: Trainer, @@ -62,9 +64,13 @@ def _save_images(self, outputs, batch, is_predict: bool) -> None: predicted_image.save(self.output_dir / f"{name}.png") if not is_predict: - original_image = Image.fromarray(data[b]["original_image"].astype("uint8")) + original_image = Image.fromarray( + data[b]["original_image"].astype("uint8") + ) original_image.save(self.output_dir / f"{name}_original.png") if "modified_image" in data[b]: - modified_image = Image.fromarray((data[b]["modified_image"] * 255).astype("uint8")) + modified_image = Image.fromarray( + (data[b]["modified_image"] * 255).astype("uint8") + ) modified_image.save(self.output_dir / f"{name}_modified.png") diff --git a/stain_normalization/data/data_module.py b/stain_normalization/data/data_module.py index 5fb8521..eb1f7ae 100644 --- a/stain_normalization/data/data_module.py +++ b/stain_normalization/data/data_module.py @@ -52,16 +52,16 @@ def val_dataloader(self) -> Iterable[Input]: def test_dataloader(self) -> Iterable[Input]: return DataLoader( - self.test, - batch_size=self.batch_size, + self.test, + batch_size=self.batch_size, num_workers=self.num_workers, collate_fn=collate_fn, ) def predict_dataloader(self) -> Iterable[tuple[Tensor, list[dict[str, Any]]]]: return DataLoader( - self.predict, - batch_size=self.batch_size, + self.predict, + batch_size=self.batch_size, num_workers=self.num_workers, collate_fn=collate_fn, - ) \ No newline at end of file + ) diff --git a/stain_normalization/data/datasets/predict_dataset.py b/stain_normalization/data/datasets/predict_dataset.py index 1871f94..ebd85fb 100644 --- a/stain_normalization/data/datasets/predict_dataset.py +++ b/stain_normalization/data/datasets/predict_dataset.py @@ -13,7 +13,7 @@ class PredictDataset(MetaTiledSlides[PredictSample]): def __init__( self, uris: Iterable[str], - normalize: Transform3D | None = None, + normalize: Transform3D | None = None, ) -> None: self.normalize = normalize super().__init__(uris=uris) @@ -34,7 +34,7 @@ def __init__( self, slide_metadata: pd.Series, tiles: pd.DataFrame, - normalize: Transform3D | None = None, + normalize: Transform3D | None = None, ) -> None: super().__init__() self.slide_tiles = OpenSlideTilesDataset( @@ -44,7 +44,7 @@ def __init__( tile_extent_y=slide_metadata.tile_extent_y, tiles=tiles, ) - + self.normalize = normalize self.to_tensor = ToTensorV2() @@ -58,19 +58,19 @@ def __getitem__(self, idx: int) -> PredictSample: x = self.slide_tiles.tiles.iloc[idx]["x"] y = self.slide_tiles.tiles.iloc[idx]["y"] - input_image = input_image_255 / 255.0 + input_image = input_image_255 / 255.0 if self.normalize: input_image = self.normalize(image=input_image)["image"] - - input_image = self.to_tensor(image=input_image)["image"] - - return input_image, {"original_image": input_image_255, - "modified_image": None, - "slide_name": slide_name, - "level":level , - "index": idx, - "xy":f"{x}_{y}", - "name":f"{slide_name}-{x}-{y}"} + input_image = self.to_tensor(image=input_image)["image"] + return input_image, { + "original_image": input_image_255, + "modified_image": None, + "slide_name": slide_name, + "level": level, + "index": idx, + "xy": f"{x}_{y}", + "name": f"{slide_name}-{x}-{y}", + } diff --git a/stain_normalization/data/datasets/test_dataset.py b/stain_normalization/data/datasets/test_dataset.py index 4779d06..151b373 100644 --- a/stain_normalization/data/datasets/test_dataset.py +++ b/stain_normalization/data/datasets/test_dataset.py @@ -14,7 +14,7 @@ def __init__( self, uris: Iterable[str], modify: Transform3D, - normalize: Transform3D | None = None, + normalize: Transform3D | None = None, ) -> None: self.modify = modify self.normalize = normalize @@ -38,7 +38,7 @@ def __init__( slide_metadata: pd.Series, tiles: pd.DataFrame, modify: Transform3D, - normalize: Transform3D | None = None, + normalize: Transform3D | None = None, ) -> None: super().__init__() self.slide_tiles = OpenSlideTilesDataset( @@ -61,7 +61,7 @@ def __getitem__(self, idx: int) -> PredictSample: # Create "wrong" image to use as input. Outputs image in float 0-1 modified_image_raw = self.modify(image=original_image_255)["image"] modified_image = modified_image_raw - original_image = original_image_255 / 255.0 + original_image = original_image_255 / 255.0 if self.normalize: original_image = self.normalize(image=original_image)["image"] @@ -69,12 +69,13 @@ def __getitem__(self, idx: int) -> PredictSample: original_image = self.to_tensor(image=original_image)["image"] modified_image = self.to_tensor(image=modified_image)["image"] - - - return (modified_image, - {"original_image_tensor":original_image, - "original_image": original_image_255, - "modified_image": modified_image_raw, - "index": idx} - ) + return ( + modified_image, + { + "original_image_tensor": original_image, + "original_image": original_image_255, + "modified_image": modified_image_raw, + "index": idx, + }, + ) diff --git a/stain_normalization/data/datasets/train_dataset.py b/stain_normalization/data/datasets/train_dataset.py index d64e26f..2a0fab7 100644 --- a/stain_normalization/data/datasets/train_dataset.py +++ b/stain_normalization/data/datasets/train_dataset.py @@ -14,7 +14,7 @@ def __init__( self, uris: Iterable[str], modify: Transform3D, - normalize: Transform3D | None = None, + normalize: Transform3D | None = None, ) -> None: self.modify = modify self.normalize = normalize @@ -38,7 +38,7 @@ def __init__( slide_metadata: pd.Series, tiles: pd.DataFrame, modify: Transform3D, - normalize: Transform3D | None = None, + normalize: Transform3D | None = None, ) -> None: super().__init__() self.slide_tiles = OpenSlideTilesDataset( @@ -60,7 +60,7 @@ def __getitem__(self, idx: int) -> Sample: # Create "wrong" image to use as input. Outputs image in float 0-1 modified_image = self.modify(image=original_image)["image"] - original_image = original_image / 255.0 + original_image = original_image / 255.0 if self.normalize: original_image = self.normalize(image=original_image)["image"] @@ -68,7 +68,5 @@ def __getitem__(self, idx: int) -> Sample: original_image = self.to_tensor(image=original_image)["image"] modified_image = self.to_tensor(image=modified_image)["image"] - - return modified_image, original_image - + return modified_image, original_image diff --git a/stain_normalization/data/modification/__init__.py b/stain_normalization/data/modification/__init__.py index a43111a..1d8962b 100644 --- a/stain_normalization/data/modification/__init__.py +++ b/stain_normalization/data/modification/__init__.py @@ -8,5 +8,9 @@ from stain_normalization.data.transformation.hvs_modification import HVSModification -__all__ = ["CombinedModifications", "ExposureAdjustment", "HEDFactor", "HVSModification"] - +__all__ = [ + "CombinedModifications", + "ExposureAdjustment", + "HEDFactor", + "HVSModification", +] diff --git a/stain_normalization/data/modification/combiend_modification.py b/stain_normalization/data/modification/combiend_modification.py index 773d804..18dbaf3 100644 --- a/stain_normalization/data/modification/combiend_modification.py +++ b/stain_normalization/data/modification/combiend_modification.py @@ -5,7 +5,13 @@ class CombinedModifications(ImageOnlyTransform): - def __init__(self, intensity_range=(0.4, 1.5), brightness_range=(-0.4, 0.4), always_apply=True, p=1.0): + def __init__( + self, + intensity_range=(0.4, 1.5), + brightness_range=(-0.4, 0.4), + always_apply=True, + p=1.0, + ): super().__init__(always_apply, p) self.intensity_range = intensity_range self.brightness_range = brightness_range @@ -19,9 +25,13 @@ def modify_channel(channel, intensity_range, brightness_range): return np.clip(channel, 0, 1) hed_image = separate_stains(hed_img, hed_from_rgb) - h = modify_channel(hed_image[:, :, 0], self.intensity_range, self.brightness_range) - e = modify_channel(hed_image[:, :, 1], self.intensity_range, self.brightness_range) + h = modify_channel( + hed_image[:, :, 0], self.intensity_range, self.brightness_range + ) + e = modify_channel( + hed_image[:, :, 1], self.intensity_range, self.brightness_range + ) d = hed_image[:, :, 2] # Skip modification for D channel modified_rgb = combine_stains(np.stack((h, e, d), axis=-1), rgb_from_hed) - return modified_rgb \ No newline at end of file + return modified_rgb diff --git a/stain_normalization/data/modification/exposure_adjustment.py b/stain_normalization/data/modification/exposure_adjustment.py index bd0cc1a..6e0899e 100644 --- a/stain_normalization/data/modification/exposure_adjustment.py +++ b/stain_normalization/data/modification/exposure_adjustment.py @@ -3,13 +3,18 @@ class ExposureAdjustment(ImageOnlyTransform): - def __init__(self, brightness_range: tuple[float, float] = (0.8, 1.2), always_apply: bool = True, p: float = 1): + def __init__( + self, + brightness_range: tuple[float, float] = (0.8, 1.2), + always_apply: bool = True, + p: float = 1, + ): super().__init__(always_apply, p) self.brightness_range = brightness_range def apply(self, img, **params): brightness_factor = np.random.uniform(*self.brightness_range) img = img.astype(np.float32) - img = np.clip(img*brightness_factor, 0.0, 1.0) - + img = np.clip(img * brightness_factor, 0.0, 1.0) + return img diff --git a/stain_normalization/data/modification/hed_factor.py b/stain_normalization/data/modification/hed_factor.py index 6fe8c40..c8e90b2 100644 --- a/stain_normalization/data/modification/hed_factor.py +++ b/stain_normalization/data/modification/hed_factor.py @@ -4,25 +4,32 @@ class HEDFactor(ImageOnlyTransform): - def __init__(self, - h_range: tuple[float, float] = (0.8, 1.2), - e_range: tuple[float, float] = (0.8, 1.2), - always_apply: bool = True, - p: float = 1.0): + def __init__( + self, + h_range: tuple[float, float] = (0.8, 1.2), + e_range: tuple[float, float] = (0.8, 1.2), + always_apply: bool = True, + p: float = 1.0, + ): super().__init__(always_apply, p) self.h_range = h_range self.e_range = e_range - def apply(self, img, **params): + def apply(self, img, **params): h_factor = np.random.uniform(*self.h_range) e_factor = np.random.uniform(*self.e_range) return self.multiply_channels(img, e_factor, h_factor) - def multiply_channels(self, image, e_factor=1.1, h_factor=1.1,): + def multiply_channels( + self, + image, + e_factor=1.1, + h_factor=1.1, + ): hed_image = separate_stains(image, hed_from_rgb) h = hed_image[:, :, 0] * h_factor e = hed_image[:, :, 1] * e_factor - d = hed_image[:, :, 2] + d = hed_image[:, :, 2] modified_rgb = combine_stains(np.stack((h, e, d), axis=-1), rgb_from_hed) return modified_rgb diff --git a/stain_normalization/data/modification/hvs_modification.py b/stain_normalization/data/modification/hvs_modification.py index 71f2138..8c47003 100644 --- a/stain_normalization/data/modification/hvs_modification.py +++ b/stain_normalization/data/modification/hvs_modification.py @@ -1,5 +1,3 @@ - - import numpy as np from albumentations import ImageOnlyTransform from skimage.color import hsv2rgb, rgb2hsv @@ -12,9 +10,8 @@ def __init__( saturation_range: tuple[float, float] = (0.8, 1.5), value_range: tuple[float, float] = (0.8, 1.3), always_apply: bool = True, - p: float = 1.0 + p: float = 1.0, ): - super().__init__(always_apply, p) self.hue_shift_range = hue_shift_range self.saturation_range = saturation_range @@ -32,4 +29,4 @@ def apply(self, img, **params): modified_rgb = hsv2rgb(hsv_image) - return modified_rgb \ No newline at end of file + return modified_rgb diff --git a/stain_normalization/modeling/__init__.py b/stain_normalization/modeling/__init__.py index e199cc5..7a8746c 100644 --- a/stain_normalization/modeling/__init__.py +++ b/stain_normalization/modeling/__init__.py @@ -1,5 +1,5 @@ -from .l1ssim_loss import L1SSIMLoss -from .unet import UNet +from stain_normalization.modeling.l1ssim_loss import L1SSIMLoss +from stain_normalization.modeling.unet import UNet -__all__ = ["L1SSIMLoss", "UNet"] \ No newline at end of file +__all__ = ["L1SSIMLoss", "UNet"] diff --git a/stain_normalization/modeling/l1ssim_loss.py b/stain_normalization/modeling/l1ssim_loss.py index 263ffaf..9cad241 100644 --- a/stain_normalization/modeling/l1ssim_loss.py +++ b/stain_normalization/modeling/l1ssim_loss.py @@ -6,22 +6,24 @@ from torch.autograd import Variable -class L1SSIMLoss(nn.Module): +class L1SSIMLoss(nn.Module): def __init__(self, lambda_dssim: float = 0.6, lambda_gdl: float = 0.2): super().__init__() self.lambda_dssim = lambda_dssim self.lambda_gdl = lambda_gdl def forward(self, image: torch.Tensor, target_image: torch.Tensor) -> torch.Tensor: - Ll1 = F.l1_loss(image, target_image, reduction='mean') - ssim_loss = 1.0 - ssim(image, target_image) + l1 = F.l1_loss(image, target_image, reduction="mean") + ssim_loss = 1.0 - ssim(image, target_image) gdl_loss = gradient_loss(image, target_image) # GDL for sharp edges - total_loss = (1.0 - self.lambda_dssim) * Ll1 + \ - self.lambda_dssim * ssim_loss + \ - self.lambda_gdl * gdl_loss - + total_loss = ( + (1.0 - self.lambda_dssim) * l1 + + self.lambda_dssim * ssim_loss + + self.lambda_gdl * gdl_loss + ) + return total_loss @@ -34,24 +36,28 @@ def gradient(x): image_dx, image_dy = gradient(image) target_dx, target_dy = gradient(target_image) - loss_x = F.l1_loss(image_dx, target_dx, reduction='mean') - loss_y = F.l1_loss(image_dy, target_dy, reduction='mean') + loss_x = F.l1_loss(image_dx, target_dx, reduction="mean") + loss_y = F.l1_loss(image_dy, target_dy, reduction="mean") return loss_x + loss_y def gaussian(window_size, sigma): - gauss = torch.Tensor([exp(-(x - window_size // 2) ** 2 / - float(2 * sigma ** 2)) for x in range(window_size)]) + gauss = torch.Tensor( + [ + exp(-((x - window_size // 2) ** 2) / float(2 * sigma**2)) + for x in range(window_size) + ] + ) return gauss / gauss.sum() def create_window(window_size, channel): - _1D_window = gaussian(window_size, 1.5).unsqueeze(1) - _2D_window = _1D_window.mm( - _1D_window.t()).float().unsqueeze(0).unsqueeze(0) - window = Variable(_2D_window.expand( - channel, 1, window_size, window_size).contiguous()) + _1d_window = gaussian(window_size, 1.5).unsqueeze(1) + _2d_window = _1d_window.mm(_1d_window.t()).float().unsqueeze(0).unsqueeze(0) + window = Variable( + _2d_window.expand(channel, 1, window_size, window_size).contiguous() + ) return window @@ -74,18 +80,23 @@ def _ssim(img1, img2, window, window_size, channel, size_average=True): mu2_sq = mu2.pow(2) mu1_mu2 = mu1 * mu2 - sigma1_sq = F.conv2d(img1 * img1, window, - padding=window_size // 2, groups=channel) - mu1_sq - sigma2_sq = F.conv2d(img2 * img2, window, - padding=window_size // 2, groups=channel) - mu2_sq - sigma12 = F.conv2d(img1 * img2, window, - padding=window_size // 2, groups=channel) - mu1_mu2 - - C1 = 0.01 ** 2 - C2 = 0.03 ** 2 - - ssim_map = ((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) / \ - ((mu1_sq + mu2_sq + C1) * (sigma1_sq + sigma2_sq + C2)) + sigma1_sq = ( + F.conv2d(img1 * img1, window, padding=window_size // 2, groups=channel) - mu1_sq + ) + sigma2_sq = ( + F.conv2d(img2 * img2, window, padding=window_size // 2, groups=channel) - mu2_sq + ) + sigma12 = ( + F.conv2d(img1 * img2, window, padding=window_size // 2, groups=channel) + - mu1_mu2 + ) + + c1 = 0.01**2 + c2 = 0.03**2 + + ssim_map = ((2 * mu1_mu2 + c1) * (2 * sigma12 + c2)) / ( + (mu1_sq + mu2_sq + c1) * (sigma1_sq + sigma2_sq + c2) + ) if size_average: return ssim_map.mean() diff --git a/stain_normalization/modeling/unet.py b/stain_normalization/modeling/unet.py index f83735d..a5f5d2b 100644 --- a/stain_normalization/modeling/unet.py +++ b/stain_normalization/modeling/unet.py @@ -1,9 +1,9 @@ -"""Model created using segmentation_models_pytorch library: -# Adapted U-Net implementation based on https://github.com/milesial/Pytorch-UNet +"""Model created using segmentation_models_pytorch library. -Ronneberger, O., Fischer, P., & Brox, T. (2015). -U-Net: Convolutional Networks for Biomedical Image Segmentation. -arXiv:1505.04597 [cs.CV]. +Adapted U-Net implementation based on https://github.com/milesial/Pytorch-UNet +Ronneberger, O., Fischer, P., & Brox, T. (2015). +U-Net: Convolutional Networks for Biomedical Image Segmentation. +arXiv:1505.04597 [cs.CV]. Retrieved from https://arxiv.org/abs/1505.04597. """ @@ -13,7 +13,7 @@ class DoubleConv(nn.Module): - """(convolution => [BN] => ReLU) * 2""" + """(convolution => [BN] => ReLU) * 2.""" def __init__(self, in_channels, out_channels, mid_channels=None): super().__init__() @@ -25,7 +25,7 @@ def __init__(self, in_channels, out_channels, mid_channels=None): nn.ReLU(inplace=True), nn.Conv2d(mid_channels, out_channels, kernel_size=3, padding=1, bias=False), nn.BatchNorm2d(out_channels), - nn.ReLU(inplace=True) + nn.ReLU(inplace=True), ) def forward(self, x): @@ -33,13 +33,12 @@ def forward(self, x): class Down(nn.Module): - """Downscaling with maxpool then double conv""" + """Downscaling with maxpool then double conv.""" def __init__(self, in_channels, out_channels): super().__init__() self.maxpool_conv = nn.Sequential( - nn.MaxPool2d(2), - DoubleConv(in_channels, out_channels) + nn.MaxPool2d(2), DoubleConv(in_channels, out_channels) ) def forward(self, x): @@ -47,27 +46,28 @@ def forward(self, x): class Up(nn.Module): - """Upscaling then double conv""" + """Upscaling then double conv.""" def __init__(self, in_channels, out_channels, bilinear=True): super().__init__() # if bilinear, use the normal convolutions to reduce the number of channels if bilinear: - self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True) + self.up = nn.Upsample(scale_factor=2, mode="bilinear", align_corners=True) self.conv = DoubleConv(in_channels, out_channels, in_channels // 2) else: - self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=2, stride=2) + self.up = nn.ConvTranspose2d( + in_channels, in_channels // 2, kernel_size=2, stride=2 + ) self.conv = DoubleConv(in_channels, out_channels) def forward(self, x1, x2): x1 = self.up(x1) # input is CHW - diffY = x2.size()[2] - x1.size()[2] - diffX = x2.size()[3] - x1.size()[3] + diffy = x2.size()[2] - x1.size()[2] + diffx = x2.size()[3] - x1.size()[3] - x1 = F.pad(x1, [diffX // 2, diffX - diffX // 2, - diffY // 2, diffY - diffY // 2]) + x1 = F.pad(x1, [diffx // 2, diffx - diffx // 2, diffy // 2, diffy - diffy // 2]) # if you have padding issues, see # https://github.com/HaiyongJiang/U-Net-Pytorch-Unstructured-Buggy/commit/0e854509c2cea854e247a9c615f175f76fbb2e3a # https://github.com/xiaopeng-liao/Pytorch-UNet/commit/8ebac70e633bac59fc22bb5195e513d5832fb3bd @@ -77,7 +77,7 @@ def forward(self, x1, x2): class OutConv(nn.Module): def __init__(self, in_channels, out_channels): - super(OutConv, self).__init__() + super().__init__() self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1) def forward(self, x): @@ -86,18 +86,18 @@ def forward(self, x): class UNet(nn.Module): def __init__(self, in_channels=3, out_channels=3, bilinear=True): - super(UNet, self).__init__() + super().__init__() self.in_conv = DoubleConv(in_channels, 64) self.down1 = Down(64, 128) self.down2 = Down(128, 256) self.down3 = Down(256, 512) factor = 2 if bilinear else 1 - self.down4 = (Down(512, 1024 // factor)) - self.up1 = (Up(1024, 512 // factor, bilinear)) - self.up2 = (Up(512, 256 // factor, bilinear)) - self.up3 = (Up(256, 128 // factor, bilinear)) - self.up4 = (Up(128, 64, bilinear)) + self.down4 = Down(512, 1024 // factor) + self.up1 = Up(1024, 512 // factor, bilinear) + self.up2 = Up(512, 256 // factor, bilinear) + self.up3 = Up(256, 128 // factor, bilinear) + self.up4 = Up(128, 64, bilinear) self.out_conv = OutConv(64, out_channels) def forward(self, x): diff --git a/stain_normalization/stain_normalization_model.py b/stain_normalization/stain_normalization_model.py index f4a0b05..b84372c 100644 --- a/stain_normalization/stain_normalization_model.py +++ b/stain_normalization/stain_normalization_model.py @@ -14,14 +14,14 @@ class StainNormalizationModel(LightningModule): def __init__(self) -> None: super().__init__() - self.unet = UNet(in_channels=3,out_channels=3) - self.criterion = L1SSIMLoss() + self.unet = UNet(in_channels=3, out_channels=3) + self.criterion = L1SSIMLoss() self.val_metrics = MetricCollection( { "ssim": StructuralSimilarityIndexMeasure(), } - ) + ) self.test_metrics = self.val_metrics.clone(prefix="test/") self.val_metrics.prefix = "validation/" @@ -53,13 +53,13 @@ def validation_step(self, batch: Input) -> None: def test_step(self, batch: Input) -> Outputs: inputs, data = batch outputs = self(inputs) - targets = stack([item['original_image_tensor'] for item in data]) + targets = stack([item["original_image_tensor"] for item in data]) self.test_metrics.update(outputs, targets) self.log_dict( self.test_metrics, batch_size=len(inputs), on_epoch=True, - ) + ) return outputs def predict_step(self, batch: tuple[Tensor, Any], batch_idx: int) -> Outputs: diff --git a/stain_normalization/typing.py b/stain_normalization/typing.py index c507266..a379a3c 100644 --- a/stain_normalization/typing.py +++ b/stain_normalization/typing.py @@ -9,5 +9,3 @@ Input: TypeAlias = Sample Outputs: TypeAlias = Tensor - - From 137fa370d5be88c917623fd7bf4b320edd85c110 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 16 May 2025 14:07:47 +0000 Subject: [PATCH 56/85] chore: formating fix --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f8be213..a758313 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "Stain Normalization" version = "0.1.0" -authors = ["Adam Lopatka"] +authors = [{name = "Adam Lopatka"}] requires-python = ">=3.11" readme = "README.md" license = { file = "LICENSE" } From 7d6aee234fdcd7a878923aafb5f760ccfbf3cc84 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Sun, 18 May 2025 01:58:48 +0000 Subject: [PATCH 57/85] chore: remove kubejobs --- configs/default.yaml | 43 +++++++++++++--------- pdm.lock | 86 +------------------------------------------- 2 files changed, 28 insertions(+), 101 deletions(-) diff --git a/configs/default.yaml b/configs/default.yaml index 0076175..9a6b082 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -11,30 +11,41 @@ seed: ${random_seed:} mode: predict checkpoint: mlflow-artifacts:/79/f75808f6e14344bb814e5c0659321054/artifacts/checkpoints/epoch=5-step=86273/checkpoint.ckpt + +callbacks: + model_checkpoint: + _target_: lightning.pytorch.callbacks.ModelCheckpoint + save_top_k: 1 + monitor: validation/loss + mode: min + + tiles_export: + _target_: stain_normalization.callbacks.TilesExport + output_dir: ${output_dir} + normalization_config: ${data.test.normalize} + + analysis_export: + _target_: stain_normalization.callbacks.AnalysisExport + output_dir: ${output_dir} + normalization_config: ${data.test.normalize} + + save_wsi: + _target_: stain_normalization.callbacks.SaveWSI + output_dir: ${output_dir} + normalization_config: ${data.predict.normalize} + trainer: enable_checkpointing: True max_epochs: 30 log_every_n_steps: 5 callbacks: - - _target_: lightning.pytorch.callbacks.ModelCheckpoint - save_top_k: 1 - monitor: validation/loss - mode: min - - _target_: stain_normalization.callbacks.TilesExport - output_dir: ${output_dir} - normalization_config: ${data.test.normalize} - - - _target_: stain_normalization.callbacks.AnalysisExport - output_dir: ${output_dir} - normalization_config: ${data.test.normalize} - - - _target_: stain_normalization.callbacks.SaveWSI - output_dir: ${output_dir} - normalization_config: ${data.predict.normalize} + - ${callbacks.model_checkpoint} + - ${callbacks.tiles_export} + data: batch_size: 64 - num_workers: 32 + num_workers: 48 metadata: user: xlopatka diff --git a/pdm.lock b/pdm.lock index 267b14f..9434e64 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:2f62924ed5f088a39fa44fe5eaa436688edca68488cf39f4b0f4da79ca23d64f" +content_hash = "sha256:3b136c97eed3fb433ad9cd3f25569a59cb6da6d85820f1a20158ce14eb886796" [[metadata.targets]] requires_python = ">=3.11" @@ -493,16 +493,6 @@ files = [ {file = "docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c"}, ] -[[package]] -name = "durationpy" -version = "0.9" -summary = "Module for converting between datetime.timedelta and Go's Duration strings." -groups = ["default"] -files = [ - {file = "durationpy-0.9-py3-none-any.whl", hash = "sha256:e65359a7af5cedad07fb77a2dd3f390f8eb0b74cb845589fa6c057086834dd38"}, - {file = "durationpy-0.9.tar.gz", hash = "sha256:fd3feb0a69a0057d582ef643c355c40d2fa1c942191f914d12203b1a01ac722a"}, -] - [[package]] name = "filelock" version = "3.17.0" @@ -1007,30 +997,6 @@ files = [ {file = "kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e"}, ] -[[package]] -name = "kubernetes" -version = "32.0.1" -requires_python = ">=3.6" -summary = "Kubernetes python client" -groups = ["default"] -dependencies = [ - "certifi>=14.05.14", - "durationpy>=0.7", - "google-auth>=1.0.1", - "oauthlib>=3.2.2", - "python-dateutil>=2.5.3", - "pyyaml>=5.4.1", - "requests", - "requests-oauthlib", - "six>=1.9.0", - "urllib3>=1.24.2", - "websocket-client!=0.40.0,!=0.41.*,!=0.42.*,>=0.32.0", -] -files = [ - {file = "kubernetes-32.0.1-py2.py3-none-any.whl", hash = "sha256:35282ab8493b938b08ab5526c7ce66588232df00ef5e1dbe88a419107dc10998"}, - {file = "kubernetes-32.0.1.tar.gz", hash = "sha256:42f43d49abd437ada79a79a16bd48a604d3471a117a8347e87db693f2ba0ba28"}, -] - [[package]] name = "lazy-loader" version = "0.4" @@ -1675,17 +1641,6 @@ files = [ {file = "nvidia_nvtx_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:641dccaaa1139f3ffb0d3164b4b84f9d253397e38246a4f2f36728b48566d485"}, ] -[[package]] -name = "oauthlib" -version = "3.2.2" -requires_python = ">=3.6" -summary = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" -groups = ["default"] -files = [ - {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, - {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, -] - [[package]] name = "omegaconf" version = "2.3.0" @@ -2301,19 +2256,6 @@ files = [ {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] -[[package]] -name = "rationai-kube-jobs" -version = "0.1.0" -requires_python = ">=3.11" -git = "ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/kube-jobs.git" -revision = "7ce565cc35bfd1abe30db2e1e4e606e65dda8c84" -summary = "A simple CLI tool for managing Kubernetes jobs." -groups = ["default"] -dependencies = [ - "click>=8.1.7", - "kubernetes>=31.0.0", -] - [[package]] name = "rationai-masks" version = "1.1.0" @@ -2448,21 +2390,6 @@ files = [ {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] -[[package]] -name = "requests-oauthlib" -version = "2.0.0" -requires_python = ">=3.4" -summary = "OAuthlib authentication support for Requests." -groups = ["default"] -dependencies = [ - "oauthlib>=3.0.0", - "requests>=2.0.0", -] -files = [ - {file = "requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9"}, - {file = "requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36"}, -] - [[package]] name = "rpds-py" version = "0.23.1" @@ -3105,17 +3032,6 @@ files = [ {file = "waitress-3.0.2.tar.gz", hash = "sha256:682aaaf2af0c44ada4abfb70ded36393f0e307f4ab9456a215ce0020baefc31f"}, ] -[[package]] -name = "websocket-client" -version = "1.8.0" -requires_python = ">=3.8" -summary = "WebSocket client for Python with low level API options" -groups = ["default"] -files = [ - {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, - {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"}, -] - [[package]] name = "werkzeug" version = "3.1.3" From 054114269af32a9d515b3c45765e9905f0d79c24 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Mon, 19 May 2025 02:54:17 +0000 Subject: [PATCH 58/85] chore: naming fix --- configs/data/modify/test.yaml | 8 +- configs/data/modify/train.yaml | 8 +- configs/default.yaml | 8 +- .../callbacks/analysis_export.py | 156 ++++++++++-------- .../data/modification/__init__.py | 8 +- 5 files changed, 107 insertions(+), 81 deletions(-) diff --git a/configs/data/modify/test.yaml b/configs/data/modify/test.yaml index 8024799..9491339 100644 --- a/configs/data/modify/test.yaml +++ b/configs/data/modify/test.yaml @@ -1,15 +1,15 @@ _target_: albumentations.OneOf p: 1.0 transforms: - - _target_: stain_normalization.data.transformation.HEDFactor + - _target_: stain_normalization.data.modification.HEDFactor h_range: [0.8, 1.2] e_range: [0.8, 1.2] - - _target_: stain_normalization.data.transformation.ExposureAdjustment + - _target_: stain_normalization.data.modification.ExposureAdjustment brightness_range: [0.8, 1.2] - - _target_: stain_normalization.data.transformation.HVSModification + - _target_: stain_normalization.data.modification.HVSModification hue_shift_range: [-0.4, 0.4] saturation_range: [0.8, 1.5] value_range: [0.8, 1.3] - - _target_: stain_normalization.data.transformation.CombinedModifications + - _target_: stain_normalization.data.modification.CombinedModifications intensity_range: [0.65, 1.35] brightness_range: [-0.4, 0.4] \ No newline at end of file diff --git a/configs/data/modify/train.yaml b/configs/data/modify/train.yaml index 8024799..9491339 100644 --- a/configs/data/modify/train.yaml +++ b/configs/data/modify/train.yaml @@ -1,15 +1,15 @@ _target_: albumentations.OneOf p: 1.0 transforms: - - _target_: stain_normalization.data.transformation.HEDFactor + - _target_: stain_normalization.data.modification.HEDFactor h_range: [0.8, 1.2] e_range: [0.8, 1.2] - - _target_: stain_normalization.data.transformation.ExposureAdjustment + - _target_: stain_normalization.data.modification.ExposureAdjustment brightness_range: [0.8, 1.2] - - _target_: stain_normalization.data.transformation.HVSModification + - _target_: stain_normalization.data.modification.HVSModification hue_shift_range: [-0.4, 0.4] saturation_range: [0.8, 1.5] value_range: [0.8, 1.3] - - _target_: stain_normalization.data.transformation.CombinedModifications + - _target_: stain_normalization.data.modification.CombinedModifications intensity_range: [0.65, 1.35] brightness_range: [-0.4, 0.4] \ No newline at end of file diff --git a/configs/default.yaml b/configs/default.yaml index 9a6b082..2542909 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -8,7 +8,7 @@ defaults: - _self_ seed: ${random_seed:} -mode: predict +mode: test checkpoint: mlflow-artifacts:/79/f75808f6e14344bb814e5c0659321054/artifacts/checkpoints/epoch=5-step=86273/checkpoint.ckpt @@ -40,12 +40,12 @@ trainer: log_every_n_steps: 5 callbacks: - ${callbacks.model_checkpoint} - - ${callbacks.tiles_export} + - ${callbacks.analysis_export} data: - batch_size: 64 - num_workers: 48 + batch_size: 80 + num_workers: 8 metadata: user: xlopatka diff --git a/stain_normalization/callbacks/analysis_export.py b/stain_normalization/callbacks/analysis_export.py index 3bfbf01..1328fe1 100644 --- a/stain_normalization/callbacks/analysis_export.py +++ b/stain_normalization/callbacks/analysis_export.py @@ -10,6 +10,8 @@ class AnalysisExport(Callback): + """Callback for exporting analysis metrics during testing.""" + def __init__(self, output_dir: str | Path, normalization_config) -> None: super().__init__() self.output_dir = Path(output_dir) @@ -32,87 +34,111 @@ def tensor_to_image(self, tensor: torch.Tensor) -> np.ndarray: return (tensor * 255).byte().permute(1, 2, 0).cpu().numpy() def _estimate_vectors(self, img: np.ndarray) -> np.ndarray: + """Estimate stain vectors for a given image.""" return estimate_stain_vectors(img, i0=240, alpha=1, beta=0.15) def _compare_vectors(self, vec1: np.ndarray, vec2: np.ndarray) -> float: + """Compute Euclidean distance between two stain vector estimates.""" return float(np.linalg.norm(vec1 - vec2)) def _compute_ssim(self, img1: np.ndarray, img2: np.ndarray) -> float: + """Compute Structural Similarity Index (SSIM) between two images.""" return float(ssim(img1, img2, channel_axis=-1, data_range=255)) - def _compute_nmi(self, img: np.ndarray): # normalized median intensity + def _compute_nmi(self, img: np.ndarray) -> float: + """Compute Normalized Median Intensity (NMI) of an image.""" avg_rgb = img.mean(axis=2) median_val = np.median(avg_rgb) p95_val = np.percentile(avg_rgb, 95) - nmi = median_val / p95_val if p95_val != 0 else 0 - return nmi - - -def on_test_batch_end( - self, - trainer: Trainer, - pl_module: LightningModule, - outputs: list[torch.Tensor], - batch: tuple[torch.Tensor, list], - batch_idx: int, - dataloader_idx: int = 0, -) -> None: - for b in range(len(outputs)): - predicted_tensor = outputs[b] - original_np = batch[1][b]["original_image"].astype("uint8") - modified_np = (batch[1][b]["modified_image"] * 255).astype("uint8") - index = batch[1][b]["index"] - predicted_np = self.tensor_to_image(predicted_tensor) - - vec_original = self._estimate_vectors(original_np) - vec_modified = self._estimate_vectors(modified_np) - vec_predicted = self._estimate_vectors(predicted_np) - - vec_diff_mod = self._compare_vectors(vec_original, vec_modified) - vec_diff_pred = self._compare_vectors(vec_original, vec_predicted) - - ssim_mod = self._compute_ssim(original_np, modified_np) - ssim_pred = self._compute_ssim(original_np, predicted_np) - - nmi_original = self.compute_nmi(original_np) - nmi_modified = self.compute_nmi(modified_np) - nmi_predicted = self.compute_nmi(predicted_np) - - diff_row = { - "index": index, - "vector_diff_modified_vs_original": vec_diff_mod, - "vector_diff_predicted_vs_original": vec_diff_pred, - "ssim_modified_vs_original": ssim_mod, - "ssim_predicted_vs_original": ssim_pred, - "nmi_diff_modified_vs_original": nmi_modified - nmi_original, - "nmi_diff_predicted_vs_original": nmi_predicted - nmi_original, - } - - raw_row = {"index": index} - for name, vec, nmi in zip( - ["original", "modified", "predicted"], - [vec_original, vec_modified, vec_predicted], - [nmi_original, nmi_modified, nmi_predicted], - strict=False, - ): - vectors = vec.flatten() - for j, val in enumerate(vectors): - raw_row[f"{name}_vec_{j}"] = val - raw_row[f"{name}_nmi"] = nmi - - self.df_diff = pd.concat( - [self.df_diff, pd.DataFrame([diff_row])], ignore_index=True - ) - self.df_raw = pd.concat( - [self.df_raw, pd.DataFrame([raw_row])], ignore_index=True - ) - - def on_predict_end( + return median_val / p95_val if p95_val != 0 else 0.0 + + def _compute_pcc(self, img1: np.ndarray, img2: np.ndarray) -> float: + """Compute Pearson Correlation Coefficient (PCC) between two images.""" + img1_flat = img1.flatten().astype(np.float64) + img2_flat = img2.flatten().astype(np.float64) + if img1_flat.size == 0 or img2_flat.size == 0: + return 0.0 + + return float(np.corrcoef(img1_flat, img2_flat)[0, 1]) + + def on_test_batch_end( + self, + trainer: Trainer, + pl_module: LightningModule, + outputs: list[torch.Tensor], + batch: tuple[torch.Tensor, list[dict]], + batch_idx: int, + dataloader_idx: int = 0, + ) -> None: + """Called at the end of each test batch. + + Computes metrics for each sample and accumulates results. + """ + for b in range(len(outputs)): + predicted_tensor = outputs[b] + original_img = batch[1][b]["original_image"].astype("uint8") + modified_img = (batch[1][b]["modified_image"] * 255).astype("uint8") + index = batch[1][b]["index"] + predicted_img = self.tensor_to_image(predicted_tensor) + + vec_original = self._estimate_vectors(original_img) + vec_modified = self._estimate_vectors(modified_img) + vec_predicted = self._estimate_vectors(predicted_img) + + vec_diff_mod = self._compare_vectors(vec_original, vec_modified) + vec_diff_pred = self._compare_vectors(vec_original, vec_predicted) + + ssim_mod = self._compute_ssim(original_img, modified_img) + ssim_pred = self._compute_ssim(original_img, predicted_img) + + nmi_original = self._compute_nmi(original_img) + nmi_modified = self._compute_nmi(modified_img) + nmi_predicted = self._compute_nmi(predicted_img) + + pcc_mod = self._compute_pcc(original_img, modified_img) + pcc_pred = self._compute_pcc(original_img, predicted_img) + + diff_row = { + "index": index, + "vector_diff_modified_vs_original": vec_diff_mod, + "vector_diff_predicted_vs_original": vec_diff_pred, + "ssim_modified_vs_original": ssim_mod, + "ssim_predicted_vs_original": ssim_pred, + "nmi_diff_modified_vs_original": nmi_modified - nmi_original, + "nmi_diff_predicted_vs_original": nmi_predicted - nmi_original, + "pcc_modified_vs_original": pcc_mod, + "pcc_predicted_vs_original": pcc_pred, + } + + raw_row = {"index": index} + for name, vec, nmi in zip( + ["original", "modified", "predicted"], + [vec_original, vec_modified, vec_predicted], + [nmi_original, nmi_modified, nmi_predicted], + strict=False, + ): + vectors = vec.flatten() + for j, val in enumerate(vectors): + raw_row[f"{name}_vec_{j}"] = val + raw_row[f"{name}_nmi"] = nmi + + self.df_diff = pd.concat( + [self.df_diff, pd.DataFrame([diff_row])], ignore_index=True + ) + self.df_raw = pd.concat( + [self.df_raw, pd.DataFrame([raw_row])], ignore_index=True + ) + + def on_test_end( self, trainer: Trainer, pl_module: LightningModule, outputs: list, ) -> None: + """Called at the end of testing. + + Saves the collected metrics as CSV files and logs them as mlflow artifacts. + """ diff_path = self.output_dir / "analysis_differences.csv" raw_path = self.output_dir / "analysis_raw_vectors.csv" diff --git a/stain_normalization/data/modification/__init__.py b/stain_normalization/data/modification/__init__.py index 1d8962b..0e01429 100644 --- a/stain_normalization/data/modification/__init__.py +++ b/stain_normalization/data/modification/__init__.py @@ -1,11 +1,11 @@ -from stain_normalization.data.transformation.combiend_modification import ( +from stain_normalization.data.modification.combiend_modification import ( CombinedModifications, ) -from stain_normalization.data.transformation.exposure_adjustment import ( +from stain_normalization.data.modification.exposure_adjustment import ( ExposureAdjustment, ) -from stain_normalization.data.transformation.hed_factor import HEDFactor -from stain_normalization.data.transformation.hvs_modification import HVSModification +from stain_normalization.data.modification.hed_factor import HEDFactor +from stain_normalization.data.modification.hvs_modification import HVSModification __all__ = [ From b3cc69885f0802abaf06689100820d954ccbf8f6 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni.cz> Date: Mon, 19 May 2025 08:30:43 +0200 Subject: [PATCH 59/85] chore: add comments and fix formating --- preprocessing/tiler.py | 1 - .../modification/combiend_modification.py | 50 +++++++++++++------ .../data/modification/exposure_adjustment.py | 23 ++++++++- .../data/modification/hed_factor.py | 33 ++++++++---- .../data/modification/hvs_modification.py | 25 +++++++++- stain_normalization/modeling/l1ssim_loss.py | 5 ++ stain_normalization/modeling/unet.py | 11 ++-- 7 files changed, 113 insertions(+), 35 deletions(-) diff --git a/preprocessing/tiler.py b/preprocessing/tiler.py index 87f7062..55c5cda 100644 --- a/preprocessing/tiler.py +++ b/preprocessing/tiler.py @@ -150,5 +150,4 @@ def main() -> None: if __name__ == "__main__": - # pass main() diff --git a/stain_normalization/data/modification/combiend_modification.py b/stain_normalization/data/modification/combiend_modification.py index 18dbaf3..7188798 100644 --- a/stain_normalization/data/modification/combiend_modification.py +++ b/stain_normalization/data/modification/combiend_modification.py @@ -1,37 +1,57 @@ import numpy as np from albumentations import ImageOnlyTransform +from numpy.typing import NDArray from skimage import exposure from skimage.color import combine_stains, hed_from_rgb, rgb_from_hed, separate_stains class CombinedModifications(ImageOnlyTransform): + """Apply combined modifications to the H&E channels in HED color space. + + Attributes: + intensity_range: Range of multiplicative factors to scale stain channel intensities. + brightness_range: Range for gamma correction to simulate brightness shift. + always_apply: Whether the transformation should always be applied. + p: Probability of applying the transformation. + """ + def __init__( self, - intensity_range=(0.4, 1.5), - brightness_range=(-0.4, 0.4), - always_apply=True, - p=1.0, + intensity_range: tuple[float, float] = (0.4, 1.5), + brightness_range: tuple[float, float] = (-0.4, 0.4), + always_apply: bool = True, + p: float = 1.0, ): super().__init__(always_apply, p) self.intensity_range = intensity_range self.brightness_range = brightness_range - def apply(self, hed_img, **params): - def modify_channel(channel, intensity_range, brightness_range): - intensity_scale = np.random.uniform(*intensity_range) - channel = channel * intensity_scale - brightness_shift = np.random.uniform(*brightness_range) - channel = exposure.adjust_gamma(channel, gamma=1 + brightness_shift) - return np.clip(channel, 0, 1) + def apply(self, img: NDArray, **params) -> NDArray: + """Apply intensity and brightness adjustments to H and E channels. + + Args: + img: Image to which the transformation will be applied. + params: Additional parameters (unused). - hed_image = separate_stains(hed_img, hed_from_rgb) - h = modify_channel( + Returns: + Modified RGB image as a float32 NumPy array with values in [0.0, 1.0]. + """ + hed_image = separate_stains(img, hed_from_rgb) + h = self.modify_channel( hed_image[:, :, 0], self.intensity_range, self.brightness_range ) - e = modify_channel( + e = self.modify_channel( hed_image[:, :, 1], self.intensity_range, self.brightness_range ) - d = hed_image[:, :, 2] # Skip modification for D channel + d = hed_image[:, :, 2] modified_rgb = combine_stains(np.stack((h, e, d), axis=-1), rgb_from_hed) + return modified_rgb + + def modify_channel(self, channel: NDArray[np.float32]) -> NDArray[np.float32]: + intensity_scale = np.random.uniform(*self.intensity_range) + channel = channel * intensity_scale + brightness_shift = np.random.uniform(*self.brightness_range) + channel = exposure.adjust_gamma(channel, gamma=1 + brightness_shift) + return np.clip(channel, 0, 1) diff --git a/stain_normalization/data/modification/exposure_adjustment.py b/stain_normalization/data/modification/exposure_adjustment.py index 6e0899e..eb4215a 100644 --- a/stain_normalization/data/modification/exposure_adjustment.py +++ b/stain_normalization/data/modification/exposure_adjustment.py @@ -1,8 +1,19 @@ import numpy as np from albumentations import ImageOnlyTransform +from numpy.typing import NDArray class ExposureAdjustment(ImageOnlyTransform): + """Adjust the exposure of an image by scaling its brightness. + + Attributes: + brightness_range: Range specifying the lower and upper bounds for the + random brightness scaling factor. Values less than 1.0 darken the image, + while values greater than 1.0 brighten it. + always_apply: Whether this transformation should always be applied. + p: Probability of applying the transformation. + """ + def __init__( self, brightness_range: tuple[float, float] = (0.8, 1.2), @@ -12,7 +23,17 @@ def __init__( super().__init__(always_apply, p) self.brightness_range = brightness_range - def apply(self, img, **params): + def apply(self, img: NDArray, **params) -> NDArray: + """Apply brightness scaling to the image. + + Args: + img: Input image whose brightness will be adjusted. + params: Additional parameters. + + Returns: + RGB image with adjusted brightness as a float32 + NumPy array with values in [0.0, 1.]. + """ brightness_factor = np.random.uniform(*self.brightness_range) img = img.astype(np.float32) img = np.clip(img * brightness_factor, 0.0, 1.0) diff --git a/stain_normalization/data/modification/hed_factor.py b/stain_normalization/data/modification/hed_factor.py index c8e90b2..6617a85 100644 --- a/stain_normalization/data/modification/hed_factor.py +++ b/stain_normalization/data/modification/hed_factor.py @@ -1,9 +1,19 @@ import numpy as np from albumentations import ImageOnlyTransform +from numpy.typing import NDArray from skimage.color import combine_stains, hed_from_rgb, rgb_from_hed, separate_stains class HEDFactor(ImageOnlyTransform): + """Adjust the intensity of Hematoxylin and Eosin stains in HED color space. + + Attributes: + h_intensity_range: Range for the random intensity adjustment factor for the Hematoxylin channel. + e_intensity_range: Range for the random intensity adjustment factor for the Eosin channel. + always_apply: Whether this transformation should always be applied. + p: Probability of applying the transformation. + """ + def __init__( self, h_range: tuple[float, float] = (0.8, 1.2), @@ -15,21 +25,24 @@ def __init__( self.h_range = h_range self.e_range = e_range - def apply(self, img, **params): + def apply(self, img: NDArray, **params) -> NDArray: + """Apply the modification to the image. + + Args: + img: Image to which the transformation will be applied. + params: Additional parameters. + + Returns: + RGB image with modified Hematoxylin and Eosin channels + as a float32 NumPy array with values in [0.0, 1.]. + """ h_factor = np.random.uniform(*self.h_range) e_factor = np.random.uniform(*self.e_range) - return self.multiply_channels(img, e_factor, h_factor) - - def multiply_channels( - self, - image, - e_factor=1.1, - h_factor=1.1, - ): - hed_image = separate_stains(image, hed_from_rgb) + hed_image = separate_stains(img, hed_from_rgb) h = hed_image[:, :, 0] * h_factor e = hed_image[:, :, 1] * e_factor d = hed_image[:, :, 2] modified_rgb = combine_stains(np.stack((h, e, d), axis=-1), rgb_from_hed) + return modified_rgb diff --git a/stain_normalization/data/modification/hvs_modification.py b/stain_normalization/data/modification/hvs_modification.py index 8c47003..0ae293c 100644 --- a/stain_normalization/data/modification/hvs_modification.py +++ b/stain_normalization/data/modification/hvs_modification.py @@ -1,9 +1,22 @@ import numpy as np from albumentations import ImageOnlyTransform +from numpy.typing import NDArray from skimage.color import hsv2rgb, rgb2hsv class HVSModification(ImageOnlyTransform): + """Randomly modify hue, saturation, and value (brightness) of an image in HSV color space. + + Attributes: + hue_shift_range: Range of values to randomly shift the hue channel. + Values are wrapped around the [0, 1) interval (modulo 1.0). + saturation_range: Range for randomly scaling the saturation channel. + Values >1.0 increase saturation, <1.0 decrease it. + value_range: Range for randomly scaling the value (brightness) channel. + always_apply: Whether the transformation is always applied. + p: Probability of applying the transformation. + """ + def __init__( self, hue_shift_range: tuple[float, float] = (-0.2, 0.2), @@ -17,7 +30,17 @@ def __init__( self.saturation_range = saturation_range self.value_range = value_range - def apply(self, img, **params): + def apply(self, img: NDArray, **params) -> NDArray: + """Apply the modifications to an image. + + Args: + img: Image to which the transformation will be applied. + params: Additional parameters. + + Returns: + RGB image with HVS modifiedications as a float32 + NumPy array with values in [0.0, 1.]. + """ hue_shift = np.random.uniform(*self.hue_shift_range) saturation_scale = np.random.uniform(*self.saturation_range) value_scale = np.random.uniform(*self.value_range) diff --git a/stain_normalization/modeling/l1ssim_loss.py b/stain_normalization/modeling/l1ssim_loss.py index 9cad241..620e51f 100644 --- a/stain_normalization/modeling/l1ssim_loss.py +++ b/stain_normalization/modeling/l1ssim_loss.py @@ -1,3 +1,8 @@ +"""Original SSIM code based on pytorch-ssim by Evan Su (MIT License). + +https://github.com/Po-Hsun-Su/pytorch-ssim . +""" + from math import exp import torch diff --git a/stain_normalization/modeling/unet.py b/stain_normalization/modeling/unet.py index a5f5d2b..77f9b32 100644 --- a/stain_normalization/modeling/unet.py +++ b/stain_normalization/modeling/unet.py @@ -1,10 +1,11 @@ -"""Model created using segmentation_models_pytorch library. +"""Adapted U-Net implementation based on the GitHub repository. -Adapted U-Net implementation based on https://github.com/milesial/Pytorch-UNet +https://github.com/milesial/Pytorch-UNet . +Original U-Net architecture proposed in the paper. Ronneberger, O., Fischer, P., & Brox, T. (2015). U-Net: Convolutional Networks for Biomedical Image Segmentation. arXiv:1505.04597 [cs.CV]. -Retrieved from https://arxiv.org/abs/1505.04597. +Retrieved from https://arxiv.org/abs/1505.04597 . """ import torch @@ -63,14 +64,10 @@ def __init__(self, in_channels, out_channels, bilinear=True): def forward(self, x1, x2): x1 = self.up(x1) - # input is CHW diffy = x2.size()[2] - x1.size()[2] diffx = x2.size()[3] - x1.size()[3] x1 = F.pad(x1, [diffx // 2, diffx - diffx // 2, diffy // 2, diffy - diffy // 2]) - # if you have padding issues, see - # https://github.com/HaiyongJiang/U-Net-Pytorch-Unstructured-Buggy/commit/0e854509c2cea854e247a9c615f175f76fbb2e3a - # https://github.com/xiaopeng-liao/Pytorch-UNet/commit/8ebac70e633bac59fc22bb5195e513d5832fb3bd x = torch.cat([x2, x1], dim=1) return self.conv(x) From 1bbce8082e72fe9ad1e45793a0705265571c840a Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni.cz> Date: Mon, 19 May 2025 08:31:06 +0200 Subject: [PATCH 60/85] feat: added demo --- README.md | 42 +++++++---------- demo.py | 122 +++++++++++++++++++++++++++++++++++++++++++++++++ pdm.lock | 117 +++++++---------------------------------------- pyproject.toml | 2 +- 4 files changed, 156 insertions(+), 127 deletions(-) create mode 100644 demo.py diff --git a/README.md b/README.md index 71036c8..a6d034d 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,34 @@ # Stain Normalization -export MLFLOW_TRACKING_USERNAME= +Tento repozitár slúži ako doplnkový materiál k bakalárskej práci **"Normalizácia farbenia histopatologických snímkov pomocou neuronových sietí"**. +Celý kód nie je možné spustiť samostatne, pretože vyžaduje prístup k citlivým dátam a k platforme na správu strojového učenia MLflow. Avšak je možné spustiť demo, ku ktorému je pripravených pár vzoriek na demonštráciu. -[![PyTorch Lightning](https://img.shields.io/badge/pytorch-lightning-blue.svg?logo=PyTorch%20Lightning)](https://github.com/Lightning-AI/lightning) -[![License](https://img.shields.io/badge/License-MIT-red.svg)](https://gitlab.ics.muni.cz/rationai/digital-pathology/pathology/patch-camelyon/-/blob/master/LICENSE) +## Demo +Demo skript umožňuje načítať jeden obrázok alebo celý priečinok, normalizovať ich pomocou predtrénovaného modelu a uložiť výsledné obrázky do určeného priečinka. -The main goal ... +## Priložené dáta +- **Originálne obrázky** (v priečinku `./demo_data/original`) – referenčné vzorky. +- **Upravené obrázky** (v priečinku `./demo_data/modified`) – referenčné obrázky s modifikovaným sfarbením, ktoré sa následne modelom znormalizujú späť k originálnemu vzhľadu. +- **Obrázky z iného datasetu** (v priečinku `./demo_data/to_predict`) – ukazujú, ako model dokáže prispôsobiť farby vstupov tak, aby zodpovedali farebnému štýlu trénovacích dát. +- **Model checkpoint** -## Getting Started +## Spustenie dema -### Installation +Projekt je spravovaný pomocou nástroja **pdm**, ktorý umožňuje jednoduchú správu závislostí a prostredia. Stačí nainštalovať závislosti pomocou: -Install [PDM](https://pdm.fming.dev/) package manager and install all the dependencies using the following command: ```bash pdm install ``` - -### Preprocessing - -```bash -export MLFLOW_TRACKING_USERNAME= -pdm run preprocessing/generate_tissue_mask.py -pdm run preprocessing/tiling.py -``` - -### Training +a potom spustiť demo skript: ```bash -export MLFLOW_TRACKING_USERNAME= -pdm fit model/backbone=(vgg16|resnet18) +pdm run python demo.py --input ./demo_data/modified ``` -### Testing - -```bash -export MLFLOW_TRACKING_USERNAME= -pdm test model/backbone=(vgg16|resnet18) 'checkpoint=""' -``` +## Dostupné arguemnty: +- **input**: cesta k obrázku alebo priečinku s obrázkami na normalizáciu (default ./demo_data/modified) +- **output**: priečinok, kam sa uložia normalizované obrázky (default ./demo_data) +- **use_cpu**: defaultne nadstavené na použitie GPU ak je dostupná, avšak ak by nastali problémy odporúčam použivať iba CPU \ No newline at end of file diff --git a/demo.py b/demo.py new file mode 100644 index 0000000..69ca771 --- /dev/null +++ b/demo.py @@ -0,0 +1,122 @@ +import argparse +from pathlib import Path + +import albumentations as A +import numpy as np +import torch +from albumentations.pytorch import ToTensorV2 +from PIL import Image + +from stain_normalization.stain_normalization_model import StainNormalizationModel + + +class StainNormalizerDemo: + CHECKPOINT_PATH = "./demo_data/checkpoint.ckpt" + MEAN = (0.780361961, 0.614529804, 0.725567843) + STD = (0.144428627, 0.183275686, 0.140768627) + + NORMALIZE_TRANSFORM = A.Normalize(mean=MEAN, std=STD, max_pixel_value=1) + TO_TENSOR = ToTensorV2() + + def __init__(self, use_cpu=True): + self.device = torch.device( + "cpu" if use_cpu else ("cuda" if torch.cuda.is_available() else "cpu") + ) + print(f"Using device: {self.device}") + + self.model = StainNormalizationModel() + checkpoint = torch.load(self.CHECKPOINT_PATH, map_location=self.device) + if "state_dict" in checkpoint: + self.model.load_state_dict(checkpoint["state_dict"]) + else: + self.model.load_state_dict(checkpoint) + self.model.to(self.device) + self.model.eval() + + def load_image(self, path: Path) -> torch.Tensor: + img = Image.open(path).convert("RGB") + img_np = np.array(img).astype(np.float32) / 255.0 + normalized = self.NORMALIZE_TRANSFORM(image=img_np)["image"] + tensor = self.TO_TENSOR(image=normalized)["image"].to(self.device).unsqueeze(0) + return tensor + + def denormalize(self, tensor: torch.Tensor) -> torch.Tensor: + mean = torch.tensor(self.MEAN).view(3, 1, 1).to(tensor.device) + std = torch.tensor(self.STD).view(3, 1, 1).to(tensor.device) + return tensor * std + mean + + def tensor_to_image(self, tensor: torch.Tensor) -> Image.Image: + tensor = self.denormalize(tensor) + tensor = tensor.clamp(0, 1) + tensor = (tensor * 255).byte() + return Image.fromarray(tensor.permute(1, 2, 0).cpu().numpy()) + + def save_image(self, tensor: torch.Tensor, path: Path): + img = self.tensor_to_image(tensor.squeeze(0)) + img.save(path) + + def predict_image(self, input_path: Path, output_path: Path): + with torch.no_grad(): + input_tensor = self.load_image(input_path) + output = self.model(input_tensor) + self.save_image(output, output_path) + print(f"Saved normalized image to {output_path}") + + +def main(): + parser = argparse.ArgumentParser(description="Stain Normalization Demo") + parser.add_argument( + "--input", + type=str, + default="./demo_data/modified", + help="Input image or folder path", + ) + parser.add_argument( + "--output", + type=str, + default="./demo_data", + help="Output folder path", + ) + parser.add_argument( + "--use_cpu", + action="store_true", + help="Force CPU even if GPU available", + ) + args = parser.parse_args() + + normalizer = StainNormalizerDemo(use_cpu=args.use_cpu) + + input_path = Path(args.input) + output_path = Path(args.output) + + if not output_path.exists(): + output_path.mkdir(parents=True) + + if input_path.is_file(): + out_filename = input_path.stem + "_normalized" + input_path.suffix + out_path = output_path / out_filename + normalizer.predict_image(input_path, out_path) + + elif input_path.is_dir(): + norm_folder = output_path / "normalized" + norm_folder.mkdir(exist_ok=True) + + input_files = sorted( + f + for f in input_path.iterdir() + if f.is_file() and f.suffix.lower() in {".png", ".jpg", ".jpeg"} + ) + if not input_files: + print(f"No image files found in {input_path}.") + return + for in_path in input_files: + out_filename = in_path.stem + "_normalized" + in_path.suffix + out_path = norm_folder / out_filename + normalizer.predict_image(in_path, out_path) + + else: + raise ValueError(f"Input path {input_path} does not exist.") + + +if __name__ == "__main__": + main() diff --git a/pdm.lock b/pdm.lock index 9434e64..c53931a 100644 --- a/pdm.lock +++ b/pdm.lock @@ -4,11 +4,8 @@ [metadata] groups = ["default", "dev"] strategy = ["inherit_metadata"] -lock_version = "4.5.0" -content_hash = "sha256:3b136c97eed3fb433ad9cd3f25569a59cb6da6d85820f1a20158ce14eb886796" - -[[metadata.targets]] -requires_python = ">=3.11" +lock_version = "4.4.2" +content_hash = "sha256:d9fbd23622ea9172dda4f468af65fdfdb8d70bea316aaed2d1898878e365ad57" [[package]] name = "aiohappyeyeballs" @@ -30,7 +27,6 @@ groups = ["default"] dependencies = [ "aiohappyeyeballs>=2.3.0", "aiosignal>=1.1.2", - "async-timeout<6.0,>=4.0; python_version < \"3.11\"", "attrs>=17.3.0", "frozenlist>=1.1.1", "multidict<7.0,>=4.5", @@ -114,7 +110,6 @@ dependencies = [ "opencv-python-headless>=4.9.0.80", "simsimd>=5.9.2", "stringzilla>=3.10.4", - "typing-extensions>=4.9.0; python_version < \"3.10\"", ] files = [ {file = "albucore-0.0.23-py3-none-any.whl", hash = "sha256:99274ac0c15a1a7d9a726df9d54d5ab70d9d0c189e2a935399dba3d4bafad415"}, @@ -130,12 +125,10 @@ groups = ["default"] dependencies = [ "PyYAML", "albucore==0.0.23", - "eval-type-backport; python_version < \"3.10\"", "numpy>=1.24.4", "opencv-python-headless>=4.9.0.80", "pydantic>=2.9.2", "scipy>=1.10.0", - "typing-extensions>=4.9.0; python_version < \"3.10\"", ] files = [ {file = "albumentations-2.0.5-py3-none-any.whl", hash = "sha256:1fc253942d34dd7c07652bf6511049c8bb7d522baec7f1fe355df16293c3c7b6"}, @@ -164,9 +157,6 @@ version = "0.7.0" requires_python = ">=3.8" summary = "Reusable constraint types to use with typing.Annotated" groups = ["default"] -dependencies = [ - "typing-extensions>=4.0.0; python_version < \"3.9\"", -] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -177,9 +167,6 @@ name = "antlr4-python3-runtime" version = "4.9.3" summary = "ANTLR 4.9.3 runtime for Python 3.7" groups = ["default"] -dependencies = [ - "typing; python_version < \"3.5\"", -] files = [ {file = "antlr4-python3-runtime-4.9.3.tar.gz", hash = "sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b"}, ] @@ -342,10 +329,6 @@ version = "8.1.8" requires_python = ">=3.7" summary = "Composable command line interface toolkit" groups = ["default"] -dependencies = [ - "colorama; platform_system == \"Windows\"", - "importlib-metadata; python_version < \"3.8\"", -] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -362,18 +345,6 @@ files = [ {file = "cloudpickle-3.1.1.tar.gz", hash = "sha256:b216fa8ae4019d5482a8ac3c95d8f6346115d8835911fd4aefd1a445e4242c64"}, ] -[[package]] -name = "colorama" -version = "0.4.6" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -summary = "Cross-platform colored terminal text." -groups = ["default"] -marker = "platform_system == \"Windows\"" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - [[package]] name = "contourpy" version = "1.3.1" @@ -484,7 +455,6 @@ requires_python = ">=3.8" summary = "A Python library for the Docker Engine API." groups = ["default"] dependencies = [ - "pywin32>=304; sys_platform == \"win32\"", "requests>=2.26.0", "urllib3>=1.26.0", ] @@ -515,7 +485,6 @@ dependencies = [ "Werkzeug>=3.1", "blinker>=1.9", "click>=8.1.3", - "importlib-metadata>=3.6; python_version < \"3.10\"", "itsdangerous>=2.2", ] files = [ @@ -663,7 +632,6 @@ summary = "GitPython is a Python library used to interact with Git repositories" groups = ["default"] dependencies = [ "gitdb<5,>=4.0.1", - "typing-extensions>=3.7.4.3; python_version < \"3.8\"", ] files = [ {file = "GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110"}, @@ -708,9 +676,6 @@ version = "3.2.6" requires_python = "<4,>=3.6" summary = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL." groups = ["default"] -dependencies = [ - "typing-extensions<5,>=4; python_version < \"3.10\"", -] files = [ {file = "graphql_core-3.2.6-py3-none-any.whl", hash = "sha256:78b016718c161a6fb20a7d97bbf107f331cd1afe53e45566c59f776ed7f0b45f"}, {file = "graphql_core-3.2.6.tar.gz", hash = "sha256:c08eec22f9e40f0bd61d805907e3b3b1b9a320bc606e23dc145eebca07c8fbab"}, @@ -724,7 +689,6 @@ summary = "Relay library for graphql-core" groups = ["default"] dependencies = [ "graphql-core<3.3,>=3.2", - "typing-extensions<5,>=4.1; python_version < \"3.8\"", ] files = [ {file = "graphql-relay-3.2.0.tar.gz", hash = "sha256:1ff1c51298356e481a0be009ccdff249832ce53f30559c1338f22a0e0d17250c"}, @@ -784,7 +748,6 @@ summary = "WSGI HTTP Server for UNIX" groups = ["default"] marker = "platform_system != \"Windows\"" dependencies = [ - "importlib-metadata; python_version < \"3.8\"", "packaging", ] files = [ @@ -799,7 +762,6 @@ summary = "A framework for elegantly configuring complex applications" groups = ["default"] dependencies = [ "antlr4-python3-runtime==4.9.*", - "importlib-resources; python_version < \"3.9\"", "omegaconf<2.4,>=2.2", "packaging", ] @@ -852,7 +814,6 @@ requires_python = ">=3.8" summary = "Read metadata from Python packages" groups = ["default"] dependencies = [ - "typing-extensions>=3.6.4; python_version < \"3.8\"", "zipp>=3.20", ] files = [ @@ -904,9 +865,7 @@ summary = "An implementation of JSON Schema validation for Python" groups = ["default"] dependencies = [ "attrs>=22.2.0", - "importlib-resources>=1.4.0; python_version < \"3.9\"", "jsonschema-specifications>=2023.03.6", - "pkgutil-resolve-name>=1.3.10; python_version < \"3.9\"", "referencing>=0.28.4", "rpds-py>=0.7.1", ] @@ -1004,7 +963,6 @@ requires_python = ">=3.7" summary = "Makes it easy to load subpackages and functions on demand." groups = ["default"] dependencies = [ - "importlib-metadata; python_version < \"3.8\"", "packaging", ] files = [ @@ -1041,7 +999,6 @@ requires_python = ">=3.9" summary = "Lightning toolbox for across the our ecosystem." groups = ["default"] dependencies = [ - "importlib-metadata>=4.0.0; python_version < \"3.8\"", "packaging>=17.1", "setuptools", "typing-extensions", @@ -1071,9 +1028,6 @@ version = "3.7" requires_python = ">=3.8" summary = "Python implementation of John Gruber's Markdown." groups = ["default"] -dependencies = [ - "importlib-metadata>=4.4; python_version < \"3.10\"", -] files = [ {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, @@ -1183,7 +1137,6 @@ groups = ["default"] dependencies = [ "Flask<4", "Jinja2<4,>=2.11; platform_system != \"Windows\"", - "Jinja2<4,>=3.0; platform_system == \"Windows\"", "alembic!=1.10.0,<2", "docker<8,>=4.0.0", "graphene<4", @@ -1197,7 +1150,6 @@ dependencies = [ "scikit-learn<2", "scipy<2", "sqlalchemy<3,>=1.4.0", - "waitress<4; platform_system == \"Windows\"", ] files = [ {file = "mlflow-2.20.3-py3-none-any.whl", hash = "sha256:efafe5d4d17b53be1ae02c7d8708a5e4bbde4bd3aecd2bd68b64a3c4175e9dc6"}, @@ -1292,9 +1244,6 @@ version = "6.1.0" requires_python = ">=3.8" summary = "multidict implementation" groups = ["default"] -dependencies = [ - "typing-extensions>=4.1.0; python_version < \"3.11\"", -] files = [ {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, @@ -1353,7 +1302,6 @@ summary = "Optional static typing for Python" groups = ["dev"] dependencies = [ "mypy-extensions>=1.0.0", - "tomli>=1.1.0; python_version < \"3.11\"", "typing-extensions>=4.6.0", ] files = [ @@ -1536,9 +1484,6 @@ requires_python = ">=3" summary = "CUFFT native runtime libraries" groups = ["default"] marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" -dependencies = [ - "nvidia-nvjitlink-cu12", -] files = [ {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5dad8008fc7f92f5ddfa2101430917ce2ffacd86824914c82e28990ad7f00399"}, {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f083fc24912aa410be21fa16d157fed2055dab1cc4b6934a0e03cba69eb242b9"}, @@ -1650,7 +1595,6 @@ groups = ["default"] dependencies = [ "PyYAML>=5.1.0", "antlr4-python3-runtime==4.9.*", - "dataclasses; python_version == \"3.6\"", ] files = [ {file = "omegaconf-2.3.0-py3-none-any.whl", hash = "sha256:7b4df175cdb08ba400f45cae3bdcae7ba8365db4d165fc65fd04b050ab63b46b"}, @@ -1664,14 +1608,10 @@ requires_python = ">=3.6" summary = "Wrapper package for OpenCV python bindings." groups = ["default"] dependencies = [ - "numpy>=1.13.3; python_version < \"3.7\"", "numpy>=1.17.0; python_version >= \"3.7\"", "numpy>=1.17.3; python_version >= \"3.8\"", - "numpy>=1.19.3; python_version >= \"3.6\" and platform_system == \"Linux\" and platform_machine == \"aarch64\"", "numpy>=1.19.3; python_version >= \"3.9\"", - "numpy>=1.21.0; python_version <= \"3.9\" and platform_system == \"Darwin\" and platform_machine == \"arm64\"", "numpy>=1.21.2; python_version >= \"3.10\"", - "numpy>=1.21.4; python_version >= \"3.10\" and platform_system == \"Darwin\"", "numpy>=1.23.5; python_version >= \"3.11\"", "numpy>=1.26.0; python_version >= \"3.12\"", ] @@ -1780,8 +1720,6 @@ requires_python = ">=3.9" summary = "Powerful data structures for data analysis, time series, and statistics" groups = ["default"] dependencies = [ - "numpy>=1.22.4; python_version < \"3.11\"", - "numpy>=1.23.2; python_version == \"3.11\"", "numpy>=1.26.0; python_version >= \"3.12\"", "python-dateutil>=2.8.2", "pytz>=2020.1", @@ -1869,6 +1807,17 @@ files = [ {file = "pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20"}, ] +[[package]] +name = "pkgconfig" +version = "1.5.5" +requires_python = ">=3.3,<4.0" +summary = "Interface Python with pkg-config" +groups = ["default"] +files = [ + {file = "pkgconfig-1.5.5-py3-none-any.whl", hash = "sha256:d20023bbeb42ee6d428a0fac6e0904631f545985a10cdd71a20aa58bc47a4209"}, + {file = "pkgconfig-1.5.5.tar.gz", hash = "sha256:deb4163ef11f75b520d822d9505c1f462761b4309b1bb713d08689759ea8b899"}, +] + [[package]] name = "platformdirs" version = "4.3.6" @@ -2192,33 +2141,16 @@ files = [ [[package]] name = "pyvips" version = "2.2.3" -summary = "binding for the libvips image processing library, ABI mode" +summary = "binding for the libvips image processing library, API mode" groups = ["default"] dependencies = [ "cffi>=1.0.0", + "pkgconfig", ] files = [ {file = "pyvips-2.2.3.tar.gz", hash = "sha256:43bceced0db492654c93008246a58a508e0373ae1621116b87b322f2ac72212f"}, ] -[[package]] -name = "pywin32" -version = "308" -summary = "Python for Window Extensions" -groups = ["default"] -marker = "sys_platform == \"win32\"" -files = [ - {file = "pywin32-308-cp311-cp311-win32.whl", hash = "sha256:5d8c8015b24a7d6855b1550d8e660d8daa09983c80e5daf89a273e5c6fb5095a"}, - {file = "pywin32-308-cp311-cp311-win_amd64.whl", hash = "sha256:575621b90f0dc2695fec346b2d6302faebd4f0f45c05ea29404cefe35d89442b"}, - {file = "pywin32-308-cp311-cp311-win_arm64.whl", hash = "sha256:100a5442b7332070983c4cd03f2e906a5648a5104b8a7f50175f7906efd16bb6"}, - {file = "pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897"}, - {file = "pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47"}, - {file = "pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091"}, - {file = "pywin32-308-cp313-cp313-win32.whl", hash = "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed"}, - {file = "pywin32-308-cp313-cp313-win_amd64.whl", hash = "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4"}, - {file = "pywin32-308-cp313-cp313-win_arm64.whl", hash = "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd"}, -] - [[package]] name = "pyyaml" version = "6.0.2" @@ -2297,7 +2229,7 @@ dependencies = [ name = "rationai-staining" version = "0.1.0" requires_python = ">=3.11" -git = "ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git" +git = "https://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git" revision = "bd1d704d86468a42d2321e9041c746215255c099" summary = "" groups = ["default"] @@ -2714,7 +2646,6 @@ summary = "Database Abstraction Library" groups = ["default"] dependencies = [ "greenlet!=0.4.17; (platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\") and python_version < \"3.14\"", - "importlib-metadata; python_version < \"3.8\"", "typing-extensions>=4.6.0", ] files = [ @@ -2950,9 +2881,6 @@ version = "4.67.1" requires_python = ">=3.7" summary = "Fast, Extensible Progress Meter" groups = ["default"] -dependencies = [ - "colorama; platform_system == \"Windows\"", -] files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -3012,7 +2940,6 @@ groups = ["dev"] dependencies = [ "distlib<1,>=0.3.7", "filelock<4,>=3.12.2", - "importlib-metadata>=6.6; python_version < \"3.8\"", "platformdirs<5,>=3.9.1", ] files = [ @@ -3020,18 +2947,6 @@ files = [ {file = "virtualenv-20.29.3.tar.gz", hash = "sha256:95e39403fcf3940ac45bc717597dba16110b74506131845d9b687d5e73d947ac"}, ] -[[package]] -name = "waitress" -version = "3.0.2" -requires_python = ">=3.9.0" -summary = "Waitress WSGI server" -groups = ["default"] -marker = "platform_system == \"Windows\"" -files = [ - {file = "waitress-3.0.2-py3-none-any.whl", hash = "sha256:c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e"}, - {file = "waitress-3.0.2.tar.gz", hash = "sha256:682aaaf2af0c44ada4abfb70ded36393f0e307f4ab9456a215ce0020baefc31f"}, -] - [[package]] name = "werkzeug" version = "3.1.3" diff --git a/pyproject.toml b/pyproject.toml index a758313..d2acf8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ dependencies = [ "rationai-tiling @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git", "scikit-image>=0.25.2", "openslide-bin>=4.0.0.6", - "rationai-staining @ git+ssh://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git" + "rationai-staining @ git+https://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git", ] [tool.pdm.dev-dependencies] From a36d502fb15f93c00ade4373528eee8ed61f015b Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni.cz> Date: Mon, 19 May 2025 17:10:17 +0200 Subject: [PATCH 61/85] fix: formating --- .../data/modification/combiend_modification.py | 8 ++------ .../data/modification/exposure_adjustment.py | 2 +- stain_normalization/data/modification/hed_factor.py | 2 +- stain_normalization/data/modification/hvs_modification.py | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/stain_normalization/data/modification/combiend_modification.py b/stain_normalization/data/modification/combiend_modification.py index 7188798..6eebbc3 100644 --- a/stain_normalization/data/modification/combiend_modification.py +++ b/stain_normalization/data/modification/combiend_modification.py @@ -37,12 +37,8 @@ def apply(self, img: NDArray, **params) -> NDArray: Modified RGB image as a float32 NumPy array with values in [0.0, 1.0]. """ hed_image = separate_stains(img, hed_from_rgb) - h = self.modify_channel( - hed_image[:, :, 0], self.intensity_range, self.brightness_range - ) - e = self.modify_channel( - hed_image[:, :, 1], self.intensity_range, self.brightness_range - ) + h = self.modify_channel(hed_image[:, :, 0]) + e = self.modify_channel(hed_image[:, :, 1]) d = hed_image[:, :, 2] modified_rgb = combine_stains(np.stack((h, e, d), axis=-1), rgb_from_hed) diff --git a/stain_normalization/data/modification/exposure_adjustment.py b/stain_normalization/data/modification/exposure_adjustment.py index eb4215a..69e9e5c 100644 --- a/stain_normalization/data/modification/exposure_adjustment.py +++ b/stain_normalization/data/modification/exposure_adjustment.py @@ -31,7 +31,7 @@ def apply(self, img: NDArray, **params) -> NDArray: params: Additional parameters. Returns: - RGB image with adjusted brightness as a float32 + RGB image with adjusted brightness as a float32 NumPy array with values in [0.0, 1.]. """ brightness_factor = np.random.uniform(*self.brightness_range) diff --git a/stain_normalization/data/modification/hed_factor.py b/stain_normalization/data/modification/hed_factor.py index 6617a85..790982a 100644 --- a/stain_normalization/data/modification/hed_factor.py +++ b/stain_normalization/data/modification/hed_factor.py @@ -33,7 +33,7 @@ def apply(self, img: NDArray, **params) -> NDArray: params: Additional parameters. Returns: - RGB image with modified Hematoxylin and Eosin channels + RGB image with modified Hematoxylin and Eosin channels as a float32 NumPy array with values in [0.0, 1.]. """ h_factor = np.random.uniform(*self.h_range) diff --git a/stain_normalization/data/modification/hvs_modification.py b/stain_normalization/data/modification/hvs_modification.py index 0ae293c..53e4697 100644 --- a/stain_normalization/data/modification/hvs_modification.py +++ b/stain_normalization/data/modification/hvs_modification.py @@ -38,7 +38,7 @@ def apply(self, img: NDArray, **params) -> NDArray: params: Additional parameters. Returns: - RGB image with HVS modifiedications as a float32 + RGB image with HVS modifiedications as a float32 NumPy array with values in [0.0, 1.]. """ hue_shift = np.random.uniform(*self.hue_shift_range) From cdd39a6f5a4157b3d2cae6477b5254ff7551a153 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Mon, 19 May 2025 23:09:38 +0000 Subject: [PATCH 62/85] fix: arguments --- stain_normalization/callbacks/analysis_export.py | 1 - 1 file changed, 1 deletion(-) diff --git a/stain_normalization/callbacks/analysis_export.py b/stain_normalization/callbacks/analysis_export.py index 1328fe1..59e34de 100644 --- a/stain_normalization/callbacks/analysis_export.py +++ b/stain_normalization/callbacks/analysis_export.py @@ -133,7 +133,6 @@ def on_test_end( self, trainer: Trainer, pl_module: LightningModule, - outputs: list, ) -> None: """Called at the end of testing. From b8a928c53f1c60982dd65e9c2eb771da273cdcce Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Sat, 29 Nov 2025 03:06:50 +0000 Subject: [PATCH 63/85] feat: add brightness loss and analysis --- configs/default.yaml | 13 +- .../callbacks/analysis_export.py | 18 +++ .../data/modification/hed_factor.py | 6 +- stain_normalization/modeling/l1ssim_loss.py | 141 ++++++++++-------- .../stain_normalization_model.py | 2 + 5 files changed, 108 insertions(+), 72 deletions(-) diff --git a/configs/default.yaml b/configs/default.yaml index 2542909..7ca7078 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -8,14 +8,14 @@ defaults: - _self_ seed: ${random_seed:} -mode: test -checkpoint: mlflow-artifacts:/79/f75808f6e14344bb814e5c0659321054/artifacts/checkpoints/epoch=5-step=86273/checkpoint.ckpt +mode: fit +checkpoint: null callbacks: model_checkpoint: _target_: lightning.pytorch.callbacks.ModelCheckpoint - save_top_k: 1 + save_top_k: 2 monitor: validation/loss mode: min @@ -38,19 +38,20 @@ trainer: enable_checkpointing: True max_epochs: 30 log_every_n_steps: 5 + val_check_interval: 0.5 + callbacks: - ${callbacks.model_checkpoint} - ${callbacks.analysis_export} - data: - batch_size: 80 + batch_size: 16 num_workers: 8 metadata: user: xlopatka experiment_name: Stain-Normalization - run_name: Debuging + run_name: fit bright description: Stain Normalization hyperparams: {} diff --git a/stain_normalization/callbacks/analysis_export.py b/stain_normalization/callbacks/analysis_export.py index 59e34de..f1e307d 100644 --- a/stain_normalization/callbacks/analysis_export.py +++ b/stain_normalization/callbacks/analysis_export.py @@ -7,6 +7,8 @@ from lightning import Callback, LightningModule, Trainer from rationai.staining import estimate_stain_vectors from skimage.metrics import structural_similarity as ssim +from skimage.color import rgb2lab +from skimage.metrics import peak_signal_noise_ratio class AnalysisExport(Callback): @@ -60,6 +62,17 @@ def _compute_pcc(self, img1: np.ndarray, img2: np.ndarray) -> float: return 0.0 return float(np.corrcoef(img1_flat, img2_flat)[0, 1]) + + def _compute_lab_brightness_psnr(self, img1: np.ndarray, img2: np.ndarray) -> float: + """Compute PSNR on the L (lightness) channel in Lab color space. + """ + lab1 = rgb2lab(img1.astype(np.float32) / 255.0) + lab2 = rgb2lab(img2.astype(np.float32) / 255.0) + + L1 = lab1[:, :, 0] + L2 = lab2[:, :, 0] + + return float(peak_signal_noise_ratio(L1, L2, data_range=100.0)) def on_test_batch_end( self, @@ -97,6 +110,9 @@ def on_test_batch_end( pcc_mod = self._compute_pcc(original_img, modified_img) pcc_pred = self._compute_pcc(original_img, predicted_img) + + lab_psnr_mod = self._compute_lab_brightness_psnr(original_img, modified_img) + lab_psnr_pred = self._compute_lab_brightness_psnr(original_img, predicted_img) diff_row = { "index": index, @@ -108,6 +124,8 @@ def on_test_batch_end( "nmi_diff_predicted_vs_original": nmi_predicted - nmi_original, "pcc_modified_vs_original": pcc_mod, "pcc_predicted_vs_original": pcc_pred, + "lab_brightness_psnr_mod_vs_orig": lab_psnr_mod, + "lab_brightness_psnr_pred_vs_orig": lab_psnr_pred, } raw_row = {"index": index} diff --git a/stain_normalization/data/modification/hed_factor.py b/stain_normalization/data/modification/hed_factor.py index 790982a..a5031c9 100644 --- a/stain_normalization/data/modification/hed_factor.py +++ b/stain_normalization/data/modification/hed_factor.py @@ -40,9 +40,9 @@ def apply(self, img: NDArray, **params) -> NDArray: e_factor = np.random.uniform(*self.e_range) hed_image = separate_stains(img, hed_from_rgb) - h = hed_image[:, :, 0] * h_factor - e = hed_image[:, :, 1] * e_factor - d = hed_image[:, :, 2] + h = np.clip(hed_image[:, :, 0] * h_factor, 0, 1) + e = np.clip(hed_image[:, :, 1] * e_factor, 0, 1) + d = hed_image[:, :, 2] # DAB channel unchanged modified_rgb = combine_stains(np.stack((h, e, d), axis=-1), rgb_from_hed) return modified_rgb diff --git a/stain_normalization/modeling/l1ssim_loss.py b/stain_normalization/modeling/l1ssim_loss.py index 620e51f..ccaf08d 100644 --- a/stain_normalization/modeling/l1ssim_loss.py +++ b/stain_normalization/modeling/l1ssim_loss.py @@ -3,8 +3,14 @@ https://github.com/Po-Hsun-Su/pytorch-ssim . """ +""" +The SSIM is based on implementation from gaussian-splatting and slightly simplified (pre-computed windows and removal of unused arguments) +https://github.com/graphdeco-inria/gaussian-splatting/blob/472689c0dc70417448fb451bf529ae532d32c095/utils/loss_utils.py +""" + from math import exp + import torch import torch.nn as nn import torch.nn.functional as F @@ -12,39 +18,67 @@ class L1SSIMLoss(nn.Module): - def __init__(self, lambda_dssim: float = 0.6, lambda_gdl: float = 0.2): + def __init__(self, lambda_dssim: float = 0.6, lambda_l1: float = 0.35, lambda_lum: float = 0.35, lambda_gdl: float = 0.15): super().__init__() - self.lambda_dssim = lambda_dssim - self.lambda_gdl = lambda_gdl + self.lambda_dssim = lambda_dssim + self.lambda_l1 = lambda_l1 + self.lambda_lum = lambda_lum + self.lambda_gdl = lambda_gdl + + # precompute SSIM windows to avoid repetation + self.window_size = 11 + self.channel = 3 + self._1d_window = gaussian(self.window_size, 1.5).unsqueeze(1) + self._2d_window = self._1d_window.mm(self._1d_window.t()).float().unsqueeze(0).unsqueeze(0) + self.window = self._2d_window.expand(self.channel, 1, self.window_size, self.window_size).contiguous() def forward(self, image: torch.Tensor, target_image: torch.Tensor) -> torch.Tensor: - l1 = F.l1_loss(image, target_image, reduction="mean") - ssim_loss = 1.0 - ssim(image, target_image) - - gdl_loss = gradient_loss(image, target_image) # GDL for sharp edges - + if self.window.device != image.device: + self.window = self.window.to(image.device) + # L1 color loss + l1_loss = F.l1_loss(image, target_image, reduction="mean") + + # SSIM structural loss + ssim_loss = 1.0 - self._ssim(image, target_image, self.window) + + # Gradient loss for edges + gdl_loss = gradient_loss(image, target_image) + + # Luminance / brightness loss + lum_loss = luminance_loss(image, target_image) + + # total weighted loss total_loss = ( - (1.0 - self.lambda_dssim) * l1 + self.lambda_l1 * l1_loss + self.lambda_dssim * ssim_loss + self.lambda_gdl * gdl_loss + + self.lambda_lum * lum_loss ) - + return total_loss - -def gradient_loss(image, target_image): - def gradient(x): - dx = torch.abs(x[:, :, :-1, :] - x[:, :, 1:, :]) # Horizontal gradient - dy = torch.abs(x[:, :, :, :-1] - x[:, :, :, 1:]) # Vertical gradient - return dx, dy - - image_dx, image_dy = gradient(image) - target_dx, target_dy = gradient(target_image) - - loss_x = F.l1_loss(image_dx, target_dx, reduction="mean") - loss_y = F.l1_loss(image_dy, target_dy, reduction="mean") - - return loss_x + loss_y + @torch.compile + def _ssim(self, img1, img2, window): + # Modified _ssim that uses pre-computed window + mu1 = F.conv2d(img1, window, padding=self.window_size // 2, groups=self.channel) + mu2 = F.conv2d(img2, window, padding=self.window_size // 2, groups=self.channel) + + mu1_sq = mu1.pow(2) + mu2_sq = mu2.pow(2) + mu1_mu2 = mu1 * mu2 + + sigma1_sq = F.conv2d(img1 * img1, window, padding=self.window_size // 2, groups=self.channel) - mu1_sq + sigma2_sq = F.conv2d(img2 * img2, window, padding=self.window_size // 2, groups=self.channel) - mu2_sq + sigma12 = F.conv2d(img1 * img2, window, padding=self.window_size // 2, groups=self.channel) - mu1_mu2 + + c1 = 0.01**2 + c2 = 0.03**2 + + ssim_map = ((2 * mu1_mu2 + c1) * (2 * sigma12 + c2)) / ( + (mu1_sq + mu2_sq + c1) * (sigma1_sq + sigma2_sq + c2) + ) + + return ssim_map.mean() def gaussian(window_size, sigma): @@ -57,53 +91,34 @@ def gaussian(window_size, sigma): return gauss / gauss.sum() -def create_window(window_size, channel): - _1d_window = gaussian(window_size, 1.5).unsqueeze(1) - _2d_window = _1d_window.mm(_1d_window.t()).float().unsqueeze(0).unsqueeze(0) - window = Variable( - _2d_window.expand(channel, 1, window_size, window_size).contiguous() - ) - return window +def luminance_loss( + pred: torch.Tensor, + target: torch.Tensor, + he_weights=[0.4, 0.1, 0.5]) -> torch.Tensor: + device = pred.device + weights = torch.tensor(he_weights, device=device).view(1, 3, 1, 1) + lum_pred = (pred * weights).sum(dim=1, keepdim=True) + lum_target = (target * weights).sum(dim=1, keepdim=True) -def ssim(img1, img2, window_size=11, size_average=True): - channel = img1.size(-3) - window = create_window(window_size, channel) + lum_loss = F.l1_loss(lum_pred, lum_target) + return lum_loss - if img1.is_cuda: - window = window.cuda(img1.get_device()) - window = window.type_as(img1) +def gradient_loss(image, target_image): + def gradient(x): + dx = torch.abs(x[:, :, :-1, :] - x[:, :, 1:, :]) # Horizontal gradient + dy = torch.abs(x[:, :, :, :-1] - x[:, :, :, 1:]) # Vertical gradient + return dx, dy - return _ssim(img1, img2, window, window_size, channel, size_average) + image_dx, image_dy = gradient(image) + target_dx, target_dy = gradient(target_image) + loss_x = F.l1_loss(image_dx, target_dx, reduction="mean") + loss_y = F.l1_loss(image_dy, target_dy, reduction="mean") -def _ssim(img1, img2, window, window_size, channel, size_average=True): - mu1 = F.conv2d(img1, window, padding=window_size // 2, groups=channel) - mu2 = F.conv2d(img2, window, padding=window_size // 2, groups=channel) + return loss_x + loss_y - mu1_sq = mu1.pow(2) - mu2_sq = mu2.pow(2) - mu1_mu2 = mu1 * mu2 - sigma1_sq = ( - F.conv2d(img1 * img1, window, padding=window_size // 2, groups=channel) - mu1_sq - ) - sigma2_sq = ( - F.conv2d(img2 * img2, window, padding=window_size // 2, groups=channel) - mu2_sq - ) - sigma12 = ( - F.conv2d(img1 * img2, window, padding=window_size // 2, groups=channel) - - mu1_mu2 - ) - c1 = 0.01**2 - c2 = 0.03**2 - ssim_map = ((2 * mu1_mu2 + c1) * (2 * sigma12 + c2)) / ( - (mu1_sq + mu2_sq + c1) * (sigma1_sq + sigma2_sq + c2) - ) - if size_average: - return ssim_map.mean() - else: - return ssim_map.mean(1).mean(1).mean(1) diff --git a/stain_normalization/stain_normalization_model.py b/stain_normalization/stain_normalization_model.py index b84372c..bce89f0 100644 --- a/stain_normalization/stain_normalization_model.py +++ b/stain_normalization/stain_normalization_model.py @@ -20,6 +20,8 @@ def __init__(self) -> None: self.val_metrics = MetricCollection( { "ssim": StructuralSimilarityIndexMeasure(), + "l1": MeanAbsoluteError() + } ) self.test_metrics = self.val_metrics.clone(prefix="test/") From 8142cc7aedbfe9b149ad6547e27a5e737e00de2a Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Sat, 29 Nov 2025 03:07:33 +0000 Subject: [PATCH 64/85] feat: update dataset slide paths --- configs/data/datasets/stain_normalization/test.yaml | 2 +- configs/data/datasets/stain_normalization/train.yaml | 2 +- configs/data/datasets/stain_normalization/val.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/data/datasets/stain_normalization/test.yaml b/configs/data/datasets/stain_normalization/test.yaml index c55810c..9de1949 100644 --- a/configs/data/datasets/stain_normalization/test.yaml +++ b/configs/data/datasets/stain_normalization/test.yaml @@ -3,4 +3,4 @@ defaults: - /data/normalize@normalize: default _target_: stain_normalization.data.datasets.TestDataset -uris: ["mlflow-artifacts:/79/bc7dfc2258164cd292091c3056609277/artifacts/Stain Normalization - test"] +uris: ["mlflow-artifacts:/79/5102491687c0413092f194280b9b4287/artifacts/Stain Normalization - test"] diff --git a/configs/data/datasets/stain_normalization/train.yaml b/configs/data/datasets/stain_normalization/train.yaml index e0f2b43..013d820 100644 --- a/configs/data/datasets/stain_normalization/train.yaml +++ b/configs/data/datasets/stain_normalization/train.yaml @@ -3,4 +3,4 @@ defaults: - /data/normalize@normalize: default _target_: stain_normalization.data.datasets.TrainDataset -uris: ["mlflow-artifacts:/79/bc7dfc2258164cd292091c3056609277/artifacts/Stain Normalization - train"] +uris: ["mlflow-artifacts:/79/5102491687c0413092f194280b9b4287/artifacts/Stain Normalization - train"] diff --git a/configs/data/datasets/stain_normalization/val.yaml b/configs/data/datasets/stain_normalization/val.yaml index b39e255..70842b5 100644 --- a/configs/data/datasets/stain_normalization/val.yaml +++ b/configs/data/datasets/stain_normalization/val.yaml @@ -3,5 +3,5 @@ defaults: - /data/normalize@normalize: default _target_: stain_normalization.data.datasets.TrainDataset -uris: ["mlflow-artifacts:/79/bc7dfc2258164cd292091c3056609277/artifacts/Stain Normalization - val"] +uris: ["mlflow-artifacts:/79/5102491687c0413092f194280b9b4287/artifacts/Stain Normalization - val"] From b6341fe9d95b312be74cbc802311524de00350e2 Mon Sep 17 00:00:00 2001 From: Adam L <55331774+LAdam-ix@users.noreply.github.com> Date: Thu, 4 Dec 2025 02:12:55 +0100 Subject: [PATCH 65/85] fix: update dataset path --- configs/data/datasets/stain_normalization/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/data/datasets/stain_normalization/test.yaml b/configs/data/datasets/stain_normalization/test.yaml index 9de1949..be01300 100644 --- a/configs/data/datasets/stain_normalization/test.yaml +++ b/configs/data/datasets/stain_normalization/test.yaml @@ -3,4 +3,4 @@ defaults: - /data/normalize@normalize: default _target_: stain_normalization.data.datasets.TestDataset -uris: ["mlflow-artifacts:/79/5102491687c0413092f194280b9b4287/artifacts/Stain Normalization - test"] +uris: ["mlflow-artifacts:/79/b987e3e7329342fe96906eb3b17bfcc4/artifacts/Stain Normalization - test (updated)"] From de4e59b674c5aa35892e5777f6bf8477318ac8e1 Mon Sep 17 00:00:00 2001 From: Adam L <55331774+LAdam-ix@users.noreply.github.com> Date: Thu, 4 Dec 2025 02:13:27 +0100 Subject: [PATCH 66/85] fix: update dataset path --- configs/data/datasets/stain_normalization/train.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/data/datasets/stain_normalization/train.yaml b/configs/data/datasets/stain_normalization/train.yaml index 013d820..6754a96 100644 --- a/configs/data/datasets/stain_normalization/train.yaml +++ b/configs/data/datasets/stain_normalization/train.yaml @@ -3,4 +3,4 @@ defaults: - /data/normalize@normalize: default _target_: stain_normalization.data.datasets.TrainDataset -uris: ["mlflow-artifacts:/79/5102491687c0413092f194280b9b4287/artifacts/Stain Normalization - train"] +uris: ["mlflow-artifacts:/79/b987e3e7329342fe96906eb3b17bfcc4/artifacts/Stain Normalization - train (updated)"] From 3584636116573913755d48c1743027117c7bb9b3 Mon Sep 17 00:00:00 2001 From: Adam L <55331774+LAdam-ix@users.noreply.github.com> Date: Thu, 4 Dec 2025 02:13:49 +0100 Subject: [PATCH 67/85] fix: update dataset path --- configs/data/datasets/stain_normalization/val.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/data/datasets/stain_normalization/val.yaml b/configs/data/datasets/stain_normalization/val.yaml index 70842b5..4c4c13c 100644 --- a/configs/data/datasets/stain_normalization/val.yaml +++ b/configs/data/datasets/stain_normalization/val.yaml @@ -3,5 +3,5 @@ defaults: - /data/normalize@normalize: default _target_: stain_normalization.data.datasets.TrainDataset -uris: ["mlflow-artifacts:/79/5102491687c0413092f194280b9b4287/artifacts/Stain Normalization - val"] +uris: ["mlflow-artifacts:/79/b987e3e7329342fe96906eb3b17bfcc4/artifacts/Stain Normalization - val (updated)"] From b986f3c68f47d8c5a5aeede5f8e21bd0592260b0 Mon Sep 17 00:00:00 2001 From: Adam L <55331774+LAdam-ix@users.noreply.github.com> Date: Thu, 4 Dec 2025 02:19:15 +0100 Subject: [PATCH 68/85] chore: change batch size --- configs/default.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/default.yaml b/configs/default.yaml index 7ca7078..73fa375 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -45,9 +45,9 @@ trainer: - ${callbacks.analysis_export} data: - batch_size: 16 - num_workers: 8 - + batch_size: 64 + num_workers: 10 + metadata: user: xlopatka experiment_name: Stain-Normalization From 87dca95a6dcb5a4313bfe44c6d8ca241fdcba4a3 Mon Sep 17 00:00:00 2001 From: Adam L <55331774+LAdam-ix@users.noreply.github.com> Date: Thu, 4 Dec 2025 03:07:59 +0100 Subject: [PATCH 69/85] fix: correct python version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d2acf8d..8cba123 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "Stain Normalization" version = "0.1.0" authors = [{name = "Adam Lopatka"}] -requires-python = ">=3.11" +requires-python = ">=3.11,<3.13" readme = "README.md" license = { file = "LICENSE" } dependencies = [ From aaa8d3bf613712697257a1f2affae234d889fe9d Mon Sep 17 00:00:00 2001 From: Adam L <55331774+LAdam-ix@users.noreply.github.com> Date: Thu, 4 Dec 2025 03:12:35 +0100 Subject: [PATCH 70/85] fix: add missing import --- stain_normalization/stain_normalization_model.py | 1 + 1 file changed, 1 insertion(+) diff --git a/stain_normalization/stain_normalization_model.py b/stain_normalization/stain_normalization_model.py index bce89f0..c1f9da1 100644 --- a/stain_normalization/stain_normalization_model.py +++ b/stain_normalization/stain_normalization_model.py @@ -6,6 +6,7 @@ from torch.optim.optimizer import Optimizer from torchmetrics import MetricCollection from torchmetrics.image import StructuralSimilarityIndexMeasure +from torchmetrics.regression import MeanAbsoluteError from stain_normalization.modeling import L1SSIMLoss, UNet from stain_normalization.typing import Input, Outputs From d500e9150dd4b65d7165b41ce193a032a485e791 Mon Sep 17 00:00:00 2001 From: Adam L <55331774+LAdam-ix@users.noreply.github.com> Date: Thu, 4 Dec 2025 03:19:24 +0100 Subject: [PATCH 71/85] chore: change batch size --- configs/default.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/default.yaml b/configs/default.yaml index 73fa375..f177f56 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -45,7 +45,7 @@ trainer: - ${callbacks.analysis_export} data: - batch_size: 64 + batch_size: 32 num_workers: 10 metadata: From 04366653792ac3170436be5a9831225f22044ab1 Mon Sep 17 00:00:00 2001 From: Adam L <55331774+LAdam-ix@users.noreply.github.com> Date: Thu, 4 Dec 2025 03:26:17 +0100 Subject: [PATCH 72/85] chore: change batch size --- configs/default.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/default.yaml b/configs/default.yaml index f177f56..dee8d7a 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -45,7 +45,7 @@ trainer: - ${callbacks.analysis_export} data: - batch_size: 32 + batch_size: 24 num_workers: 10 metadata: From 18962690899d70ea4acceb3232705a638e27f03b Mon Sep 17 00:00:00 2001 From: Adam L <55331774+LAdam-ix@users.noreply.github.com> Date: Fri, 5 Dec 2025 04:08:52 +0100 Subject: [PATCH 73/85] fix: adjust hyperparameters --- stain_normalization/modeling/l1ssim_loss.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stain_normalization/modeling/l1ssim_loss.py b/stain_normalization/modeling/l1ssim_loss.py index ccaf08d..03faacc 100644 --- a/stain_normalization/modeling/l1ssim_loss.py +++ b/stain_normalization/modeling/l1ssim_loss.py @@ -18,7 +18,7 @@ class L1SSIMLoss(nn.Module): - def __init__(self, lambda_dssim: float = 0.6, lambda_l1: float = 0.35, lambda_lum: float = 0.35, lambda_gdl: float = 0.15): + def __init__(self, lambda_dssim: float = 0.6, lambda_l1: float = 0.2, lambda_lum: float = 0.2, lambda_gdl: float = 0.1): super().__init__() self.lambda_dssim = lambda_dssim self.lambda_l1 = lambda_l1 @@ -94,7 +94,7 @@ def gaussian(window_size, sigma): def luminance_loss( pred: torch.Tensor, target: torch.Tensor, - he_weights=[0.4, 0.1, 0.5]) -> torch.Tensor: + he_weights=[0.33, 0.33, 0.33]) -> torch.Tensor: device = pred.device weights = torch.tensor(he_weights, device=device).view(1, 3, 1, 1) From 9c31f5a27bbea3b001d831d9a34f376edd51e228 Mon Sep 17 00:00:00 2001 From: Adam L <55331774+LAdam-ix@users.noreply.github.com> Date: Sun, 7 Dec 2025 19:51:27 +0100 Subject: [PATCH 74/85] chore: add checkpoint path --- configs/default.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/default.yaml b/configs/default.yaml index dee8d7a..49d76f4 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -9,7 +9,7 @@ defaults: seed: ${random_seed:} mode: fit -checkpoint: null +checkpoint: mlflow-artifacts:/79/f75808f6e14344bb814e5c0659321054/artifacts/checkpoints/epoch=5-step=86273/checkpoint.ckpt callbacks: From 03791ffcfc48268b33cf6c8fcc37ec7853ffd060 Mon Sep 17 00:00:00 2001 From: Adam L <55331774+LAdam-ix@users.noreply.github.com> Date: Mon, 8 Dec 2025 02:41:30 +0100 Subject: [PATCH 75/85] feat: add brightness los Replaced luminance_loss with brightness_loss and added new brightness_loss function. --- stain_normalization/modeling/l1ssim_loss.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/stain_normalization/modeling/l1ssim_loss.py b/stain_normalization/modeling/l1ssim_loss.py index 03faacc..5a94a1f 100644 --- a/stain_normalization/modeling/l1ssim_loss.py +++ b/stain_normalization/modeling/l1ssim_loss.py @@ -45,7 +45,7 @@ def forward(self, image: torch.Tensor, target_image: torch.Tensor) -> torch.Tens gdl_loss = gradient_loss(image, target_image) # Luminance / brightness loss - lum_loss = luminance_loss(image, target_image) + lum_loss = brightness_loss(image, target_image) # total weighted loss total_loss = ( @@ -104,6 +104,14 @@ def luminance_loss( lum_loss = F.l1_loss(lum_pred, lum_target) return lum_loss +def brightness_loss(pred, target, he_weights=[0.33, 0.33, 0.33]): + weights = torch.tensor(he_weights, device=device).view(1, 3, 1) + + pred_mean = (pred * weights).mean(dim=[2, 3]) # [B, 3] + target_mean = (target * weights).mean(dim=[2, 3]) # [B, 3] + + return F.l1_loss(pred_mean, target_mean) + def gradient_loss(image, target_image): def gradient(x): dx = torch.abs(x[:, :, :-1, :] - x[:, :, 1:, :]) # Horizontal gradient From e6c19cb286083c0e73a4c510365315e419018706 Mon Sep 17 00:00:00 2001 From: Adam L <55331774+LAdam-ix@users.noreply.github.com> Date: Mon, 8 Dec 2025 04:25:45 +0100 Subject: [PATCH 76/85] fix: add missed line Added device handling for brightness loss calculation. --- stain_normalization/modeling/l1ssim_loss.py | 1 + 1 file changed, 1 insertion(+) diff --git a/stain_normalization/modeling/l1ssim_loss.py b/stain_normalization/modeling/l1ssim_loss.py index 5a94a1f..aefec05 100644 --- a/stain_normalization/modeling/l1ssim_loss.py +++ b/stain_normalization/modeling/l1ssim_loss.py @@ -105,6 +105,7 @@ def luminance_loss( return lum_loss def brightness_loss(pred, target, he_weights=[0.33, 0.33, 0.33]): + device = pred.device weights = torch.tensor(he_weights, device=device).view(1, 3, 1) pred_mean = (pred * weights).mean(dim=[2, 3]) # [B, 3] From a6e54ee11bd9fe43bf0a643d0ded059b54e3dbe1 Mon Sep 17 00:00:00 2001 From: Adam L <55331774+LAdam-ix@users.noreply.github.com> Date: Mon, 8 Dec 2025 04:39:27 +0100 Subject: [PATCH 77/85] fix: dimension in brightness loss Updated brightness_loss function to maintain dimensions. --- stain_normalization/modeling/l1ssim_loss.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stain_normalization/modeling/l1ssim_loss.py b/stain_normalization/modeling/l1ssim_loss.py index aefec05..474fe14 100644 --- a/stain_normalization/modeling/l1ssim_loss.py +++ b/stain_normalization/modeling/l1ssim_loss.py @@ -106,10 +106,10 @@ def luminance_loss( def brightness_loss(pred, target, he_weights=[0.33, 0.33, 0.33]): device = pred.device - weights = torch.tensor(he_weights, device=device).view(1, 3, 1) + weights = torch.tensor(he_weights, device=device).view(1, 3, 1, 1) - pred_mean = (pred * weights).mean(dim=[2, 3]) # [B, 3] - target_mean = (target * weights).mean(dim=[2, 3]) # [B, 3] + pred_mean = (pred * weights).mean(dim=[2, 3], keepdim=True) + target_mean = (target * weights).mean(dim=[2, 3], keepdim=True) return F.l1_loss(pred_mean, target_mean) From 4bd95d461b010be593884688518dc645957041d1 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni,cz> Date: Fri, 12 Dec 2025 00:37:13 +0000 Subject: [PATCH 78/85] chore: update checkpoint --- configs/default.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/default.yaml b/configs/default.yaml index 49d76f4..03ba209 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -9,7 +9,7 @@ defaults: seed: ${random_seed:} mode: fit -checkpoint: mlflow-artifacts:/79/f75808f6e14344bb814e5c0659321054/artifacts/checkpoints/epoch=5-step=86273/checkpoint.ckpt +checkpoint: mlflow-artifacts:/79/978f5d5e54844be3b42509cce76793d7/artifacts/checkpoints/epoch=7-step=152375/checkpoint.ckpt callbacks: From 9f5ab8ab0907a0a035815a9a947f63dc28332ece Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni.cz> Date: Wed, 4 Mar 2026 05:45:56 +0100 Subject: [PATCH 79/85] feat: update analysis methods and WSI assembly --- .gitignore | 1 + analyze_dataset.py | 139 +++++++++ configs/default.yaml | 4 +- pdm.lock | 275 ++++++++++++++++-- preprocessing/mask_generator.py | 72 +---- preprocessing/tiler.py | 80 ++--- pyproject.toml | 2 +- stain_normalization/analysis/__init__.py | 20 ++ stain_normalization/analysis/analyzer.py | 189 ++++++++++++ stain_normalization/callbacks/__init__.py | 5 +- stain_normalization/callbacks/_base.py | 25 ++ .../callbacks/analysis_export.py | 157 ++-------- stain_normalization/callbacks/save_wsi.py | 56 ---- stain_normalization/callbacks/tiles_export.py | 72 +++-- .../callbacks/wsi_assembler.py | 232 +++++++++++++++ stain_normalization/data/data_module.py | 12 +- .../data/datasets/predict_dataset.py | 8 +- .../data/datasets/test_dataset.py | 8 +- .../data/datasets/train_dataset.py | 2 +- .../data/modification/exposure_adjustment.py | 2 + stain_normalization/metrics/__init__.py | 16 + stain_normalization/metrics/image_metrics.py | 56 ++++ stain_normalization/metrics/vector_metrics.py | 75 +++++ stain_normalization/modeling/l1ssim_loss.py | 26 +- .../stain_normalization_model.py | 15 +- stain_normalization/type_aliases.py | 13 + stain_normalization/typing.py | 11 - 27 files changed, 1138 insertions(+), 435 deletions(-) create mode 100644 analyze_dataset.py create mode 100644 stain_normalization/analysis/__init__.py create mode 100644 stain_normalization/analysis/analyzer.py create mode 100644 stain_normalization/callbacks/_base.py delete mode 100644 stain_normalization/callbacks/save_wsi.py create mode 100644 stain_normalization/callbacks/wsi_assembler.py create mode 100644 stain_normalization/metrics/__init__.py create mode 100644 stain_normalization/metrics/image_metrics.py create mode 100644 stain_normalization/metrics/vector_metrics.py create mode 100644 stain_normalization/type_aliases.py delete mode 100644 stain_normalization/typing.py diff --git a/.gitignore b/.gitignore index 1c12553..f1e9425 100644 --- a/.gitignore +++ b/.gitignore @@ -180,3 +180,4 @@ lightning_logs/ # Project /data +/demo_* \ No newline at end of file diff --git a/analyze_dataset.py b/analyze_dataset.py new file mode 100644 index 0000000..d10064f --- /dev/null +++ b/analyze_dataset.py @@ -0,0 +1,139 @@ +"""Compare a dataset against a reference image or between two datasets. + +Usage: + # Compare dataset against a reference image + python analyze_dataset.py --reference ref.png --uri "mlflow-artifacts:/79/..." + + # Compare two datasets (e.g. original vs normalized) + # Assume that both datasets have the same slides and tiles in the same order for paired comparison + python analyze_dataset.py --original "mlflow-artifacts:/79/...original" \ + --compared "mlflow-artifacts:/79/...normalized" + + # Subsample for faster run + python analyze_dataset.py --reference ref.png --uri "mlflow-artifacts:/79/..." --max-tiles 5000 +""" + +import argparse +from pathlib import Path + +import numpy as np +from PIL import Image +from rationai.mlkit.data.datasets import MetaTiledSlides, OpenSlideTilesDataset +from tqdm import tqdm + +from stain_normalization.analysis import StainAnalyzer +from stain_normalization.analysis.report import REPORT_METRICS + + +def load_image(path: str | Path) -> np.ndarray: + return np.array(Image.open(path).convert("RGB")) + + +def iterate_tiles(slides, tiles): + """Yield (slide_name, tile_uint8, image_id) for each tile.""" + for _, slide in slides.iterrows(): + slide_name = Path(slide.path).stem + slide_tiles = tiles[tiles["slide_id"] == slide["id"]] + + if slide_tiles.empty: + continue + + dataset = OpenSlideTilesDataset( + slide_path=slide.path, + level=slide.level, + tile_extent_x=slide.tile_extent_x, + tile_extent_y=slide.tile_extent_y, + tiles=slide_tiles, + ) + + for i in range(len(dataset)): + image_id = f"{slide_name}_{slide_tiles.iloc[i]['x']}_{slide_tiles.iloc[i]['y']}" + yield slide_name, dataset[i], image_id + + +def run_reference_mode(args): + """Compare all tiles in a dataset against a single reference image.""" + ref_img = load_image(args.reference) + slides, tiles = MetaTiledSlides.load_slides_and_tiles(paths=[], uris=args.uri) + print(f"Dataset: {len(slides)} slides, {len(tiles)} tiles") + print(f"Reference: {args.reference}") + + if args.max_tiles and len(tiles) > args.max_tiles: + tiles = tiles.sample(n=args.max_tiles, random_state=42) + print(f"Subsampled to {args.max_tiles} tiles") + + analyzer = StainAnalyzer(reference=ref_img) + for _, tile, image_id in tqdm(iterate_tiles(slides, tiles), total=len(tiles)): + analyzer.compare(tile, image_id=image_id) + + return analyzer, len(analyzer.results) + + +def run_paired_mode(args): + """Compare matching tiles between two datasets (original vs compared).""" + orig_slides, orig_tiles = MetaTiledSlides.load_slides_and_tiles(paths=[], uris=args.original) + comp_slides, comp_tiles = MetaTiledSlides.load_slides_and_tiles(paths=[], uris=args.compared) + print(f"Original: {len(orig_slides)} slides, {len(orig_tiles)} tiles") + print(f"Compared: {len(comp_slides)} slides, {len(comp_tiles)} tiles") + + if args.max_tiles and len(orig_tiles) > args.max_tiles: + orig_tiles = orig_tiles.sample(n=args.max_tiles, random_state=42) + comp_tiles = comp_tiles.loc[orig_tiles.index] + print(f"Subsampled to {args.max_tiles} tile pairs") + + analyzer = StainAnalyzer() + orig_iter = iterate_tiles(orig_slides, orig_tiles) + comp_iter = iterate_tiles(comp_slides, comp_tiles) + + for (_, orig_tile, image_id), (_, comp_tile, _) in tqdm( + zip(orig_iter, comp_iter), total=len(orig_tiles) + ): + analyzer.compare(comp_tile, image_id=image_id, reference=orig_tile) + + return analyzer, len(analyzer.results) + + +def main(): + parser = argparse.ArgumentParser( + description="Dataset stain analysis", + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument("--output", default="./analysis_output", + help="Output directory (default: ./analysis_output)") + parser.add_argument("--max-tiles", type=int, default=None, + help="Limit number of tiles to analyze") + + # Mode 1: reference image + parser.add_argument("--reference", help="Path to reference image") + parser.add_argument("--uri", nargs="+", help="MLflow dataset URI(s) to analyze") + + # Mode 2: two datasets + parser.add_argument("--original", nargs="+", help="MLflow URI(s) for original dataset") + parser.add_argument("--compared", nargs="+", help="MLflow URI(s) for compared dataset") + + args = parser.parse_args() + + if args.reference and args.uri: + analyzer, count = run_reference_mode(args) + elif args.original and args.compared: + analyzer, count = run_paired_mode(args) + else: + parser.error("Use either (--reference + --uri) or (--original + --compared)") + + if analyzer is None: + return + + print(f"\nAnalyzed {count} tiles") + + analyzer.save_csv(args.output) + print(f"Results saved to: {args.output}/") + + stats = analyzer.get_statistics() + print("\nStatistics:") + for m in REPORT_METRICS: + if m in stats.columns: + print(f" {m:25s}: mean={stats[m]['mean']:.4f} std={stats[m]['std']:.4f}") + + +if __name__ == "__main__": + main() diff --git a/configs/default.yaml b/configs/default.yaml index 03ba209..1ccce2a 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -29,8 +29,8 @@ callbacks: output_dir: ${output_dir} normalization_config: ${data.test.normalize} - save_wsi: - _target_: stain_normalization.callbacks.SaveWSI + wsi_assembler: + _target_: stain_normalization.callbacks.WSIAssembler output_dir: ${output_dir} normalization_config: ${data.predict.normalize} diff --git a/pdm.lock b/pdm.lock index c53931a..80abec9 100644 --- a/pdm.lock +++ b/pdm.lock @@ -4,8 +4,14 @@ [metadata] groups = ["default", "dev"] strategy = ["inherit_metadata"] -lock_version = "4.4.2" -content_hash = "sha256:d9fbd23622ea9172dda4f468af65fdfdb8d70bea316aaed2d1898878e365ad57" +lock_version = "4.5.0" +content_hash = "sha256:474958a455ca9402a3ee96e32c7d3dacdbb307e643b91b2e8a4a80002a33d2b7" + +[[metadata.targets]] +requires_python = "==3.12.5" +platform = "manylinux_2_35_x86_64" +implementation = "cpython" +gil_disabled = false [[package]] name = "aiohappyeyeballs" @@ -13,6 +19,7 @@ version = "2.5.0" requires_python = ">=3.9" summary = "Happy Eyeballs for asyncio" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "aiohappyeyeballs-2.5.0-py3-none-any.whl", hash = "sha256:0850b580748c7071db98bffff6d4c94028d0d3035acc20fd721a0ce7e8cac35d"}, {file = "aiohappyeyeballs-2.5.0.tar.gz", hash = "sha256:18fde6204a76deeabc97c48bdd01d5801cfda5d6b9c8bbeb1aaaee9d648ca191"}, @@ -24,6 +31,7 @@ version = "3.11.13" requires_python = ">=3.9" summary = "Async http client/server framework (asyncio)" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "aiohappyeyeballs>=2.3.0", "aiosignal>=1.1.2", @@ -91,6 +99,7 @@ version = "1.3.2" requires_python = ">=3.9" summary = "aiosignal: a list of registered asynchronous callbacks" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "frozenlist>=1.1.0", ] @@ -105,6 +114,7 @@ version = "0.0.23" requires_python = ">=3.9" summary = "High-performance image processing functions for deep learning and computer vision." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "numpy>=1.24.4", "opencv-python-headless>=4.9.0.80", @@ -122,6 +132,7 @@ version = "2.0.5" requires_python = ">=3.9" summary = "Fast, flexible, and advanced augmentation library for deep learning, computer vision, and medical imaging. Albumentations offers a wide range of transformations for both 2D (images, masks, bboxes, keypoints) and 3D (volumes, volumetric masks, keypoints) data, with optimized performance and seamless integration into ML workflows." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "PyYAML", "albucore==0.0.23", @@ -141,6 +152,7 @@ version = "1.15.1" requires_python = ">=3.9" summary = "A database migration tool for SQLAlchemy." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "Mako", "SQLAlchemy>=1.4.0", @@ -157,6 +169,7 @@ version = "0.7.0" requires_python = ">=3.8" summary = "Reusable constraint types to use with typing.Annotated" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -167,6 +180,7 @@ name = "antlr4-python3-runtime" version = "4.9.3" summary = "ANTLR 4.9.3 runtime for Python 3.7" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "antlr4-python3-runtime-4.9.3.tar.gz", hash = "sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b"}, ] @@ -177,6 +191,7 @@ version = "25.1.0" requires_python = ">=3.8" summary = "Classes Without Boilerplate" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"}, {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"}, @@ -188,6 +203,7 @@ version = "1.9.0" requires_python = ">=3.9" summary = "Fast, simple object-to-object and broadcast signaling" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"}, {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, @@ -199,6 +215,7 @@ version = "5.5.2" requires_python = ">=3.7" summary = "Extensible memoizing collections and decorators" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a"}, {file = "cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4"}, @@ -210,6 +227,7 @@ version = "2025.1.31" requires_python = ">=3.6" summary = "Python package for providing Mozilla's CA Bundle." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, @@ -221,6 +239,7 @@ version = "1.17.1" requires_python = ">=3.8" summary = "Foreign Function Interface for Python calling C code." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "pycparser", ] @@ -268,6 +287,7 @@ version = "3.4.0" requires_python = ">=3.8" summary = "Validate configuration and produce human readable error messages." groups = ["dev"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -279,6 +299,7 @@ version = "3.4.1" requires_python = ">=3.7" summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, @@ -329,6 +350,7 @@ version = "8.1.8" requires_python = ">=3.7" summary = "Composable command line interface toolkit" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -340,6 +362,7 @@ version = "3.1.1" requires_python = ">=3.8" summary = "Pickler class to extend the standard pickle.Pickler functionality" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "cloudpickle-3.1.1-py3-none-any.whl", hash = "sha256:c8c5a44295039331ee9dad40ba100a9c7297b6f988e50e87ccdf3765a668350e"}, {file = "cloudpickle-3.1.1.tar.gz", hash = "sha256:b216fa8ae4019d5482a8ac3c95d8f6346115d8835911fd4aefd1a445e4242c64"}, @@ -351,6 +374,7 @@ version = "1.3.1" requires_python = ">=3.10" summary = "Python library for calculating contours of 2D quadrilateral grids" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "numpy>=1.23", ] @@ -404,6 +428,7 @@ version = "0.12.1" requires_python = ">=3.8" summary = "Composable style cycles" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, @@ -415,6 +440,7 @@ version = "0.44.1" requires_python = ">=3.7" summary = "Databricks SDK for Python (Beta)" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "google-auth~=2.0", "requests<3,>=2.28.1", @@ -430,6 +456,7 @@ version = "1.2.18" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" summary = "Python @deprecated decorator to deprecate old python classes, functions or methods." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "wrapt<2,>=1.10", ] @@ -443,6 +470,7 @@ name = "distlib" version = "0.3.9" summary = "Distribution utilities" groups = ["dev"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, @@ -454,6 +482,7 @@ version = "7.1.0" requires_python = ">=3.8" summary = "A Python library for the Docker Engine API." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "requests>=2.26.0", "urllib3>=1.26.0", @@ -463,12 +492,24 @@ files = [ {file = "docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c"}, ] +[[package]] +name = "durationpy" +version = "0.10" +summary = "Module for converting between datetime.timedelta and Go's Duration strings." +groups = ["default"] +marker = "python_full_version == \"3.12.5\"" +files = [ + {file = "durationpy-0.10-py3-none-any.whl", hash = "sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286"}, + {file = "durationpy-0.10.tar.gz", hash = "sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba"}, +] + [[package]] name = "filelock" version = "3.17.0" requires_python = ">=3.9" summary = "A platform independent file lock." groups = ["default", "dev"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338"}, {file = "filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e"}, @@ -480,6 +521,7 @@ version = "3.1.0" requires_python = ">=3.9" summary = "A simple framework for building complex web applications." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "Jinja2>=3.1.2", "Werkzeug>=3.1", @@ -498,6 +540,7 @@ version = "4.56.0" requires_python = ">=3.8" summary = "Tools to manipulate font files" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "fonttools-4.56.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ef04bc7827adb7532be3d14462390dd71287644516af3f1e67f1e6ff9c6d6df"}, {file = "fonttools-4.56.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ffda9b8cd9cb8b301cae2602ec62375b59e2e2108a117746f12215145e3f786c"}, @@ -533,6 +576,7 @@ version = "1.5.0" requires_python = ">=3.8" summary = "A list-like structure which implements collections.abc.MutableSequence" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30"}, {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5"}, @@ -589,6 +633,7 @@ version = "2025.2.0" requires_python = ">=3.8" summary = "File-system specification" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "fsspec-2025.2.0-py3-none-any.whl", hash = "sha256:9de2ad9ce1f85e1931858535bc882543171d197001a0a5eb2ddc04f1781ab95b"}, {file = "fsspec-2025.2.0.tar.gz", hash = "sha256:1c24b16eaa0a1798afa0337aa0db9b256718ab2a89c425371f5628d22c3b6afd"}, @@ -601,6 +646,7 @@ extras = ["http"] requires_python = ">=3.8" summary = "File-system specification" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "aiohttp!=4.0.0a0,!=4.0.0a1", "fsspec==2025.2.0", @@ -616,6 +662,7 @@ version = "4.0.12" requires_python = ">=3.7" summary = "Git Object Database" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "smmap<6,>=3.0.1", ] @@ -630,6 +677,7 @@ version = "3.1.44" requires_python = ">=3.7" summary = "GitPython is a Python library used to interact with Git repositories" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "gitdb<5,>=4.0.1", ] @@ -644,6 +692,7 @@ version = "2.38.0" requires_python = ">=3.7" summary = "Google Authentication Library" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "cachetools<6.0,>=2.0.0", "pyasn1-modules>=0.2.1", @@ -659,6 +708,7 @@ name = "graphene" version = "3.4.3" summary = "GraphQL Framework for Python" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "graphql-core<3.3,>=3.1", "graphql-relay<3.3,>=3.1", @@ -676,6 +726,7 @@ version = "3.2.6" requires_python = "<4,>=3.6" summary = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "graphql_core-3.2.6-py3-none-any.whl", hash = "sha256:78b016718c161a6fb20a7d97bbf107f331cd1afe53e45566c59f776ed7f0b45f"}, {file = "graphql_core-3.2.6.tar.gz", hash = "sha256:c08eec22f9e40f0bd61d805907e3b3b1b9a320bc606e23dc145eebca07c8fbab"}, @@ -687,6 +738,7 @@ version = "3.2.0" requires_python = ">=3.6,<4" summary = "Relay library for graphql-core" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "graphql-core<3.3,>=3.2", ] @@ -701,7 +753,7 @@ version = "3.1.1" requires_python = ">=3.7" summary = "Lightweight in-process concurrent programming" groups = ["default"] -marker = "(platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\") and python_version < \"3.14\"" +marker = "(platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\") and python_full_version == \"3.12.5\"" files = [ {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, @@ -746,7 +798,7 @@ version = "23.0.0" requires_python = ">=3.7" summary = "WSGI HTTP Server for UNIX" groups = ["default"] -marker = "platform_system != \"Windows\"" +marker = "platform_system != \"Windows\" and python_full_version == \"3.12.5\"" dependencies = [ "packaging", ] @@ -760,6 +812,7 @@ name = "hydra-core" version = "1.3.2" summary = "A framework for elegantly configuring complex applications" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "antlr4-python3-runtime==4.9.*", "omegaconf<2.4,>=2.2", @@ -776,6 +829,7 @@ version = "2.6.8" requires_python = ">=3.9" summary = "File identification library for Python" groups = ["dev"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "identify-2.6.8-py2.py3-none-any.whl", hash = "sha256:83657f0f766a3c8d0eaea16d4ef42494b39b34629a4b3192a9d020d349b3e255"}, {file = "identify-2.6.8.tar.gz", hash = "sha256:61491417ea2c0c5c670484fd8abbb34de34cdae1e5f39a73ee65e48e4bb663fc"}, @@ -787,6 +841,7 @@ version = "3.10" requires_python = ">=3.6" summary = "Internationalized Domain Names in Applications (IDNA)" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -798,6 +853,7 @@ version = "2.37.0" requires_python = ">=3.9" summary = "Library for reading and writing a wide range of image, video, scientific, and volumetric data formats." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "numpy", "pillow>=8.3.2", @@ -813,6 +869,7 @@ version = "8.5.0" requires_python = ">=3.8" summary = "Read metadata from Python packages" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "zipp>=3.20", ] @@ -827,6 +884,7 @@ version = "2.2.0" requires_python = ">=3.8" summary = "Safely pass data to untrusted environments and back." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, @@ -838,6 +896,7 @@ version = "3.1.6" requires_python = ">=3.7" summary = "A very fast and expressive template engine." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "MarkupSafe>=2.0", ] @@ -852,6 +911,7 @@ version = "1.4.2" requires_python = ">=3.8" summary = "Lightweight pipelining with Python functions" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6"}, {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"}, @@ -863,6 +923,7 @@ version = "4.23.0" requires_python = ">=3.8" summary = "An implementation of JSON Schema validation for Python" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "attrs>=22.2.0", "jsonschema-specifications>=2023.03.6", @@ -880,6 +941,7 @@ version = "2024.10.1" requires_python = ">=3.9" summary = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "referencing>=0.31.0", ] @@ -894,6 +956,7 @@ version = "1.4.8" requires_python = ">=3.10" summary = "A fast implementation of the Cassowary constraint solver" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "kiwisolver-1.4.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84"}, {file = "kiwisolver-1.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561"}, @@ -956,12 +1019,37 @@ files = [ {file = "kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e"}, ] +[[package]] +name = "kubernetes" +version = "34.1.0" +requires_python = ">=3.6" +summary = "Kubernetes python client" +groups = ["default"] +marker = "python_full_version == \"3.12.5\"" +dependencies = [ + "certifi>=14.05.14", + "durationpy>=0.7", + "google-auth>=1.0.1", + "python-dateutil>=2.5.3", + "pyyaml>=5.4.1", + "requests", + "requests-oauthlib", + "six>=1.9.0", + "urllib3<2.4.0,>=1.24.2", + "websocket-client!=0.40.0,!=0.41.*,!=0.42.*,>=0.32.0", +] +files = [ + {file = "kubernetes-34.1.0-py2.py3-none-any.whl", hash = "sha256:bffba2272534e224e6a7a74d582deb0b545b7c9879d2cd9e4aae9481d1f2cc2a"}, + {file = "kubernetes-34.1.0.tar.gz", hash = "sha256:8fe8edb0b5d290a2f3ac06596b23f87c658977d46b5f8df9d0f4ea83d0003912"}, +] + [[package]] name = "lazy-loader" version = "0.4" requires_python = ">=3.7" summary = "Makes it easy to load subpackages and functions on demand." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "packaging", ] @@ -976,6 +1064,7 @@ version = "2.5.0.post0" requires_python = ">=3.9" summary = "The Deep Learning framework to train, deploy, and ship AI products Lightning fast." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "PyYAML<8.0,>=5.4", "fsspec[http]<2026.0,>=2022.5.0", @@ -998,6 +1087,7 @@ version = "0.13.1" requires_python = ">=3.9" summary = "Lightning toolbox for across the our ecosystem." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "packaging>=17.1", "setuptools", @@ -1014,6 +1104,7 @@ version = "1.3.9" requires_python = ">=3.8" summary = "A super-fast templating language that borrows the best ideas from the existing templating languages." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "MarkupSafe>=0.9.2", ] @@ -1028,6 +1119,7 @@ version = "3.7" requires_python = ">=3.8" summary = "Python implementation of John Gruber's Markdown." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, @@ -1039,6 +1131,7 @@ version = "3.0.2" requires_python = ">=3.9" summary = "Safely add untrusted strings to HTML/XML markup." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, @@ -1089,6 +1182,7 @@ version = "3.10.1" requires_python = ">=3.10" summary = "Python plotting package" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "contourpy>=1.0.1", "cycler>=0.10", @@ -1134,6 +1228,7 @@ version = "2.20.3" requires_python = ">=3.9" summary = "MLflow is an open source platform for the complete machine learning lifecycle" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "Flask<4", "Jinja2<4,>=2.11; platform_system != \"Windows\"", @@ -1162,6 +1257,7 @@ version = "2.20.3" requires_python = ">=3.9" summary = "MLflow is an open source platform for the complete machine learning lifecycle" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "cachetools<6,>=5.0.0", "click<9,>=7.0", @@ -1189,7 +1285,7 @@ name = "mpmath" version = "1.3.0" summary = "Python library for arbitrary-precision floating-point arithmetic" groups = ["default"] -marker = "python_version >= \"3.9\"" +marker = "python_full_version == \"3.12.5\"" files = [ {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, @@ -1201,6 +1297,7 @@ version = "1.1.0" requires_python = ">=3.8" summary = "MessagePack serializer" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7"}, {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa"}, @@ -1244,6 +1341,7 @@ version = "6.1.0" requires_python = ">=3.8" summary = "multidict implementation" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, @@ -1300,6 +1398,7 @@ version = "1.15.0" requires_python = ">=3.9" summary = "Optional static typing for Python" groups = ["dev"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "mypy-extensions>=1.0.0", "typing-extensions>=4.6.0", @@ -1333,6 +1432,7 @@ version = "1.0.0" requires_python = ">=3.5" summary = "Type system extensions for programs checked with the mypy type checker." groups = ["dev"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, @@ -1344,6 +1444,7 @@ version = "3.4.2" requires_python = ">=3.10" summary = "Python package for creating and manipulating graphs and networks" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"}, @@ -1355,6 +1456,7 @@ version = "1.9.1" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" summary = "Node.js virtual environment builder" groups = ["dev"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -1366,6 +1468,7 @@ version = "2.2.3" requires_python = ">=3.10" summary = "Fundamental package for array computing in Python" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "numpy-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:16372619ee728ed67a2a606a614f56d3eabc5b86f8b615c79d01957062826ca8"}, {file = "numpy-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5521a06a3148686d9269c53b09f7d399a5725c47bbb5b35747e1cb76326b714b"}, @@ -1416,7 +1519,7 @@ version = "12.4.5.8" requires_python = ">=3" summary = "CUBLAS native runtime libraries" groups = ["default"] -marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_full_version == \"3.12.5\"" files = [ {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0f8aa1706812e00b9f19dfe0cdb3999b092ccb8ca168c0db5b8ea712456fd9b3"}, {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2fc8da60df463fdefa81e323eef2e36489e1c94335b5358bcb38360adf75ac9b"}, @@ -1429,7 +1532,7 @@ version = "12.4.127" requires_python = ">=3" summary = "CUDA profiling tools runtime libs." groups = ["default"] -marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_full_version == \"3.12.5\"" files = [ {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:79279b35cf6f91da114182a5ce1864997fd52294a87a16179ce275773799458a"}, {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9dec60f5ac126f7bb551c055072b69d85392b13311fcc1bcda2202d172df30fb"}, @@ -1442,7 +1545,7 @@ version = "12.4.127" requires_python = ">=3" summary = "NVRTC native runtime libraries" groups = ["default"] -marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_full_version == \"3.12.5\"" files = [ {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0eedf14185e04b76aa05b1fea04133e59f465b6f960c0cbf4e37c3cb6b0ea198"}, {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a178759ebb095827bd30ef56598ec182b85547f1508941a3d560eb7ea1fbf338"}, @@ -1455,7 +1558,7 @@ version = "12.4.127" requires_python = ">=3" summary = "CUDA Runtime native Libraries" groups = ["default"] -marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_full_version == \"3.12.5\"" files = [ {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:961fe0e2e716a2a1d967aab7caee97512f71767f852f67432d572e36cb3a11f3"}, {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:64403288fa2136ee8e467cdc9c9427e0434110899d07c779f25b5c068934faa5"}, @@ -1468,7 +1571,7 @@ version = "9.1.0.70" requires_python = ">=3" summary = "cuDNN runtime libraries" groups = ["default"] -marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_full_version == \"3.12.5\"" dependencies = [ "nvidia-cublas-cu12", ] @@ -1483,7 +1586,7 @@ version = "11.2.1.3" requires_python = ">=3" summary = "CUFFT native runtime libraries" groups = ["default"] -marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_full_version == \"3.12.5\"" files = [ {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5dad8008fc7f92f5ddfa2101430917ce2ffacd86824914c82e28990ad7f00399"}, {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f083fc24912aa410be21fa16d157fed2055dab1cc4b6934a0e03cba69eb242b9"}, @@ -1496,7 +1599,7 @@ version = "10.3.5.147" requires_python = ">=3" summary = "CURAND native runtime libraries" groups = ["default"] -marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_full_version == \"3.12.5\"" files = [ {file = "nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1f173f09e3e3c76ab084aba0de819c49e56614feae5c12f69883f4ae9bb5fad9"}, {file = "nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a88f583d4e0bb643c49743469964103aa59f7f708d862c3ddb0fc07f851e3b8b"}, @@ -1509,7 +1612,7 @@ version = "11.6.1.9" requires_python = ">=3" summary = "CUDA solver native runtime libraries" groups = ["default"] -marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_full_version == \"3.12.5\"" dependencies = [ "nvidia-cublas-cu12", "nvidia-cusparse-cu12", @@ -1527,7 +1630,7 @@ version = "12.3.1.170" requires_python = ">=3" summary = "CUSPARSE native runtime libraries" groups = ["default"] -marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_full_version == \"3.12.5\"" dependencies = [ "nvidia-nvjitlink-cu12", ] @@ -1542,7 +1645,7 @@ name = "nvidia-cusparselt-cu12" version = "0.6.2" summary = "NVIDIA cuSPARSELt" groups = ["default"] -marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_full_version == \"3.12.5\"" files = [ {file = "nvidia_cusparselt_cu12-0.6.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:067a7f6d03ea0d4841c85f0c6f1991c5dda98211f6302cb83a4ab234ee95bef8"}, {file = "nvidia_cusparselt_cu12-0.6.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:df2c24502fd76ebafe7457dbc4716b2fec071aabaed4fb7691a201cde03704d9"}, @@ -1555,7 +1658,7 @@ version = "2.21.5" requires_python = ">=3" summary = "NVIDIA Collective Communication Library (NCCL) Runtime" groups = ["default"] -marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_full_version == \"3.12.5\"" files = [ {file = "nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8579076d30a8c24988834445f8d633c697d42397e92ffc3f63fa26766d25e0a0"}, ] @@ -1566,7 +1669,7 @@ version = "12.4.127" requires_python = ">=3" summary = "Nvidia JIT LTO Library" groups = ["default"] -marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_full_version == \"3.12.5\"" files = [ {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4abe7fef64914ccfa909bc2ba39739670ecc9e820c83ccc7a6ed414122599b83"}, {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57"}, @@ -1579,19 +1682,32 @@ version = "12.4.127" requires_python = ">=3" summary = "NVIDIA Tools Extension" groups = ["default"] -marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_full_version == \"3.12.5\"" files = [ {file = "nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7959ad635db13edf4fc65c06a6e9f9e55fc2f92596db928d169c0bb031e88ef3"}, {file = "nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a"}, {file = "nvidia_nvtx_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:641dccaaa1139f3ffb0d3164b4b84f9d253397e38246a4f2f36728b48566d485"}, ] +[[package]] +name = "oauthlib" +version = "3.3.1" +requires_python = ">=3.8" +summary = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +groups = ["default"] +marker = "python_full_version == \"3.12.5\"" +files = [ + {file = "oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1"}, + {file = "oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9"}, +] + [[package]] name = "omegaconf" version = "2.3.0" requires_python = ">=3.6" summary = "A flexible configuration library" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "PyYAML>=5.1.0", "antlr4-python3-runtime==4.9.*", @@ -1607,6 +1723,7 @@ version = "4.11.0.86" requires_python = ">=3.6" summary = "Wrapper package for OpenCV python bindings." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "numpy>=1.17.0; python_version >= \"3.7\"", "numpy>=1.17.3; python_version >= \"3.8\"", @@ -1631,6 +1748,7 @@ version = "4.0.0.6" requires_python = ">=3.8" summary = "Binary build of OpenSlide" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "openslide-bin-4.0.0.6.tar.gz", hash = "sha256:baca4a590cb15c8685f1ee1905d496bb0a23468b969f7066a2126c24c9e9ef39"}, {file = "openslide_bin-4.0.0.6-py3-none-macosx_11_0_universal2.whl", hash = "sha256:f9c4ed0da275202dce531b163d92e1b860d215e779e7620fdd0dd92b7c40764b"}, @@ -1645,6 +1763,7 @@ version = "1.4.1" requires_python = ">=3.8" summary = "Python interface to OpenSlide" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "Pillow", ] @@ -1662,6 +1781,7 @@ version = "1.30.0" requires_python = ">=3.8" summary = "OpenTelemetry Python API" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "deprecated>=1.2.6", "importlib-metadata<=8.5.0,>=6.0", @@ -1677,6 +1797,7 @@ version = "1.30.0" requires_python = ">=3.8" summary = "OpenTelemetry Python SDK" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "opentelemetry-api==1.30.0", "opentelemetry-semantic-conventions==0.51b0", @@ -1693,6 +1814,7 @@ version = "0.51b0" requires_python = ">=3.8" summary = "OpenTelemetry Semantic Conventions" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "deprecated>=1.2.6", "opentelemetry-api==1.30.0", @@ -1708,6 +1830,7 @@ version = "24.2" requires_python = ">=3.8" summary = "Core utilities for Python packages" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -1719,6 +1842,7 @@ version = "2.2.3" requires_python = ">=3.9" summary = "Powerful data structures for data analysis, time series, and statistics" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "numpy>=1.26.0; python_version >= \"3.12\"", "python-dateutil>=2.8.2", @@ -1762,6 +1886,7 @@ version = "11.1.0" requires_python = ">=3.9" summary = "Python Imaging Library (Fork)" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "pillow-11.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e06695e0326d05b06833b40b7ef477e475d0b1ba3a6d27da1bb48c23209bf457"}, {file = "pillow-11.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96f82000e12f23e4f29346e42702b6ed9a2f2fea34a740dd5ffffcc8c539eb35"}, @@ -1813,6 +1938,7 @@ version = "1.5.5" requires_python = ">=3.3,<4.0" summary = "Interface Python with pkg-config" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "pkgconfig-1.5.5-py3-none-any.whl", hash = "sha256:d20023bbeb42ee6d428a0fac6e0904631f545985a10cdd71a20aa58bc47a4209"}, {file = "pkgconfig-1.5.5.tar.gz", hash = "sha256:deb4163ef11f75b520d822d9505c1f462761b4309b1bb713d08689759ea8b899"}, @@ -1824,6 +1950,7 @@ version = "4.3.6" requires_python = ">=3.8" summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." groups = ["dev"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -1835,6 +1962,7 @@ version = "4.1.0" requires_python = ">=3.9" summary = "A framework for managing and maintaining multi-language pre-commit hooks." groups = ["dev"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "cfgv>=2.0.0", "identify>=1.0.0", @@ -1853,6 +1981,7 @@ version = "0.3.0" requires_python = ">=3.9" summary = "Accelerated property cache" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "propcache-0.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9ddd49258610499aab83b4f5b61b32e11fce873586282a0e972e5ab3bcadee51"}, {file = "propcache-0.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2578541776769b500bada3f8a4eeaf944530516b6e90c089aa368266ed70c49e"}, @@ -1928,6 +2057,7 @@ version = "5.29.3" requires_python = ">=3.8" summary = "" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "protobuf-5.29.3-cp310-abi3-win32.whl", hash = "sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888"}, {file = "protobuf-5.29.3-cp310-abi3-win_amd64.whl", hash = "sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a"}, @@ -1944,6 +2074,7 @@ version = "19.0.1" requires_python = ">=3.9" summary = "Python library for Apache Arrow" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "pyarrow-19.0.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:cc55d71898ea30dc95900297d191377caba257612f384207fe9f8293b5850f90"}, {file = "pyarrow-19.0.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:7a544ec12de66769612b2d6988c36adc96fb9767ecc8ee0a4d270b10b1c51e00"}, @@ -1981,6 +2112,7 @@ version = "0.6.1" requires_python = ">=3.8" summary = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, @@ -1992,6 +2124,7 @@ version = "0.4.1" requires_python = ">=3.8" summary = "A collection of ASN.1-based protocols modules" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "pyasn1<0.7.0,>=0.4.6", ] @@ -2006,6 +2139,7 @@ version = "2.22" requires_python = ">=3.8" summary = "C parser in Python" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -2017,6 +2151,7 @@ version = "2.10.6" requires_python = ">=3.8" summary = "Data validation using Python type hints" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "annotated-types>=0.6.0", "pydantic-core==2.27.2", @@ -2033,6 +2168,7 @@ version = "2.27.2" requires_python = ">=3.8" summary = "Core functionality for Pydantic validation and serialization" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "typing-extensions!=4.7.0,>=4.6.0", ] @@ -2088,6 +2224,7 @@ version = "3.2.1" requires_python = ">=3.9" summary = "pyparsing module - Classes and methods to define and execute parsing grammars" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1"}, {file = "pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a"}, @@ -2099,6 +2236,7 @@ version = "2.9.0.post0" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" summary = "Extensions to the standard Python datetime module" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "six>=1.5", ] @@ -2113,6 +2251,7 @@ version = "2.5.0.post0" requires_python = ">=3.9" summary = "PyTorch Lightning is the lightweight PyTorch wrapper for ML researchers. Scale your models. Write less boilerplate." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "PyYAML>=5.4", "fsspec[http]>=2022.5.0", @@ -2133,6 +2272,7 @@ name = "pytz" version = "2025.1" summary = "World timezone definitions, modern and historical" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"}, {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, @@ -2143,6 +2283,7 @@ name = "pyvips" version = "2.2.3" summary = "binding for the libvips image processing library, API mode" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "cffi>=1.0.0", "pkgconfig", @@ -2157,6 +2298,7 @@ version = "6.0.2" requires_python = ">=3.8" summary = "YAML parser and emitter for Python" groups = ["default", "dev"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, @@ -2188,6 +2330,20 @@ files = [ {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] +[[package]] +name = "rationai-kube-jobs" +version = "0.4.0" +requires_python = ">=3.12" +git = "ssh://git@gitlab.ics.muni.cz/rationai/infrastructure/kube-jobs" +revision = "d7a082700a1234f714c0ca3d3d2bf0b9f957c87d" +summary = "A simple CLI tool for managing Kubernetes jobs." +groups = ["default"] +marker = "python_full_version == \"3.12.5\"" +dependencies = [ + "click>=8.1.7", + "kubernetes>=31.0.0", +] + [[package]] name = "rationai-masks" version = "1.1.0" @@ -2196,6 +2352,7 @@ git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git revision = "91b2a2a2e288417e9bdd0fd83ddd4f04af449dec" summary = "" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "numpy>=1.9.0", "openslide-python>=1.3.1", @@ -2215,6 +2372,7 @@ git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/mlkit.git revision = "233ead782353b3f23eee75702576f5c004cf300e" summary = "" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "GitPython>=3.1.43", "hydra-core>=1.3.2", @@ -2233,6 +2391,7 @@ git = "https://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/stain revision = "bd1d704d86468a42d2321e9041c746215255c099" summary = "" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "numpy>=1.26.4", "pillow>=10.3.0", @@ -2247,6 +2406,7 @@ git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.gi revision = "9096ada81542c172908dff5b2766cf729786471b" summary = "" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "mlflow>=2.15.1", "numpy>=1.9.0", @@ -2264,6 +2424,7 @@ version = "2.43.0" requires_python = ">=3.9" summary = "Ray provides a simple, universal API for building distributed applications." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "aiosignal", "click>=7.0", @@ -2295,6 +2456,7 @@ version = "0.36.2" requires_python = ">=3.9" summary = "JSON Referencing + Python" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "attrs>=22.2.0", "rpds-py>=0.7.0", @@ -2311,6 +2473,7 @@ version = "2.32.3" requires_python = ">=3.8" summary = "Python HTTP for Humans." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "certifi>=2017.4.17", "charset-normalizer<4,>=2", @@ -2322,12 +2485,29 @@ files = [ {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] +[[package]] +name = "requests-oauthlib" +version = "2.0.0" +requires_python = ">=3.4" +summary = "OAuthlib authentication support for Requests." +groups = ["default"] +marker = "python_full_version == \"3.12.5\"" +dependencies = [ + "oauthlib>=3.0.0", + "requests>=2.0.0", +] +files = [ + {file = "requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9"}, + {file = "requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36"}, +] + [[package]] name = "rpds-py" version = "0.23.1" requires_python = ">=3.9" summary = "Python bindings to Rust's persistent data structures (rpds)" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "rpds_py-0.23.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b79f5ced71efd70414a9a80bbbfaa7160da307723166f09b69773153bf17c590"}, {file = "rpds_py-0.23.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c9e799dac1ffbe7b10c1fd42fe4cd51371a549c6e108249bde9cd1200e8f59b4"}, @@ -2390,6 +2570,7 @@ version = "4.9" requires_python = ">=3.6,<4" summary = "Pure-Python RSA implementation" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "pyasn1>=0.1.3", ] @@ -2404,6 +2585,7 @@ version = "0.9.9" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["dev"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "ruff-0.9.9-py3-none-linux_armv6l.whl", hash = "sha256:628abb5ea10345e53dff55b167595a159d3e174d6720bf19761f5e467e68d367"}, {file = "ruff-0.9.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6cd1428e834b35d7493354723543b28cc11dc14d1ce19b685f6e68e07c05ec7"}, @@ -2431,6 +2613,7 @@ version = "0.25.2" requires_python = ">=3.10" summary = "Image processing in Python" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "imageio!=2.35.0,>=2.33", "lazy-loader>=0.4", @@ -2467,6 +2650,7 @@ version = "1.6.1" requires_python = ">=3.9" summary = "A set of python modules for machine learning and data mining" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "joblib>=1.2.0", "numpy>=1.19.5", @@ -2502,6 +2686,7 @@ version = "1.15.2" requires_python = ">=3.10" summary = "Fundamental algorithms for scientific computing in Python" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "numpy<2.5,>=1.23.5", ] @@ -2545,12 +2730,30 @@ files = [ {file = "scipy-1.15.2.tar.gz", hash = "sha256:cd58a314d92838f7e6f755c8a2167ead4f27e1fd5c1251fd54289569ef3495ec"}, ] +[[package]] +name = "seaborn" +version = "0.13.2" +requires_python = ">=3.8" +summary = "Statistical data visualization" +groups = ["default"] +marker = "python_full_version == \"3.12.5\"" +dependencies = [ + "matplotlib!=3.6.1,>=3.4", + "numpy!=1.24.0,>=1.20", + "pandas>=1.2", +] +files = [ + {file = "seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987"}, + {file = "seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7"}, +] + [[package]] name = "setuptools" version = "75.8.2" requires_python = ">=3.9" summary = "Easily download, build, install, upgrade, and uninstall Python packages" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "setuptools-75.8.2-py3-none-any.whl", hash = "sha256:558e47c15f1811c1fa7adbd0096669bf76c1d3f433f58324df69f3f5ecac4e8f"}, {file = "setuptools-75.8.2.tar.gz", hash = "sha256:4880473a969e5f23f2a2be3646b2dfd84af9028716d398e46192f84bc36900d2"}, @@ -2561,6 +2764,7 @@ name = "simsimd" version = "6.2.1" summary = "Portable mixed-precision BLAS-like vector math library for x86 and ARM" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "simsimd-6.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e9614309af75be4d08a051dc61ed5cf41b5239b8303b37dc2f9c8a7223534392"}, {file = "simsimd-6.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ea4f0f68be5f85bbcf4322bfdd1b449176cf5fdd99960c546514457635632443"}, @@ -2622,6 +2826,7 @@ version = "1.17.0" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" summary = "Python 2 and 3 compatibility utilities" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -2633,6 +2838,7 @@ version = "5.0.2" requires_python = ">=3.7" summary = "A pure Python implementation of a sliding window memory map manager" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e"}, {file = "smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5"}, @@ -2644,6 +2850,7 @@ version = "2.0.38" requires_python = ">=3.7" summary = "Database Abstraction Library" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "greenlet!=0.4.17; (platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\") and python_version < \"3.14\"", "typing-extensions>=4.6.0", @@ -2683,6 +2890,7 @@ version = "0.5.3" requires_python = ">=3.8" summary = "A non-validating SQL parser." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca"}, {file = "sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272"}, @@ -2693,6 +2901,7 @@ name = "stringzilla" version = "3.12.2" summary = "SIMD-accelerated string search, sort, hashes, fingerprints, & edit distances" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "stringzilla-3.12.2-cp311-cp311-macosx_10_11_universal2.whl", hash = "sha256:78363cfa4ac9a232b728dd40f59b0d8b0ef31629708751fbdee9c0117d06500f"}, {file = "stringzilla-3.12.2-cp311-cp311-macosx_10_11_x86_64.whl", hash = "sha256:0ad4ae468411348532fd7cb35c7f0207b37f1a40fc7d7882f63e7b0cfc8ec117"}, @@ -2754,7 +2963,7 @@ version = "1.13.1" requires_python = ">=3.8" summary = "Computer algebra system (CAS) in Python" groups = ["default"] -marker = "python_version >= \"3.9\"" +marker = "python_full_version == \"3.12.5\"" dependencies = [ "mpmath<1.4,>=1.1.0", ] @@ -2769,6 +2978,7 @@ version = "3.5.0" requires_python = ">=3.8" summary = "threadpoolctl" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467"}, {file = "threadpoolctl-3.5.0.tar.gz", hash = "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107"}, @@ -2780,6 +2990,7 @@ version = "2025.2.18" requires_python = ">=3.10" summary = "Read and write TIFF files" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "numpy", ] @@ -2794,6 +3005,7 @@ version = "2.6.0" requires_python = ">=3.9.0" summary = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "filelock", "fsspec", @@ -2838,6 +3050,7 @@ version = "1.6.2" requires_python = ">=3.9" summary = "PyTorch native Metrics" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "lightning-utilities>=0.8.0", "numpy>1.20.0", @@ -2855,6 +3068,7 @@ version = "0.21.0" requires_python = ">=3.9" summary = "image and video datasets and models for torch deep learning" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "numpy", "pillow!=8.3.*,>=5.3.0", @@ -2881,6 +3095,7 @@ version = "4.67.1" requires_python = ">=3.7" summary = "Fast, Extensible Progress Meter" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -2891,7 +3106,7 @@ name = "triton" version = "3.2.0" summary = "A language and compiler for custom Deep Learning operations" groups = ["default"] -marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_full_version == \"3.12.5\"" files = [ {file = "triton-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8009a1fb093ee8546495e96731336a33fb8856a38e45bb4ab6affd6dbc3ba220"}, {file = "triton-3.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d9b215efc1c26fa7eefb9a157915c92d52e000d2bf83e5f69704047e63f125c"}, @@ -2904,6 +3119,7 @@ version = "4.12.2" requires_python = ">=3.8" summary = "Backported and Experimental Type Hints for Python 3.8+" groups = ["default", "dev"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -2915,6 +3131,7 @@ version = "2025.1" requires_python = ">=2" summary = "Provider of IANA time zone data" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639"}, {file = "tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694"}, @@ -2926,6 +3143,7 @@ version = "2.3.0" requires_python = ">=3.9" summary = "HTTP library with thread-safe connection pooling, file post, and more." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, @@ -2937,6 +3155,7 @@ version = "20.29.3" requires_python = ">=3.8" summary = "Virtual Python Environment builder" groups = ["dev"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "distlib<1,>=0.3.7", "filelock<4,>=3.12.2", @@ -2947,12 +3166,25 @@ files = [ {file = "virtualenv-20.29.3.tar.gz", hash = "sha256:95e39403fcf3940ac45bc717597dba16110b74506131845d9b687d5e73d947ac"}, ] +[[package]] +name = "websocket-client" +version = "1.9.0" +requires_python = ">=3.9" +summary = "WebSocket client for Python with low level API options" +groups = ["default"] +marker = "python_full_version == \"3.12.5\"" +files = [ + {file = "websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef"}, + {file = "websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98"}, +] + [[package]] name = "werkzeug" version = "3.1.3" requires_python = ">=3.9" summary = "The comprehensive WSGI web application library." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "MarkupSafe>=2.1.1", ] @@ -2967,6 +3199,7 @@ version = "1.17.2" requires_python = ">=3.8" summary = "Module for decorators, wrappers and monkey patching." groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58"}, {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda"}, @@ -3022,6 +3255,7 @@ version = "1.18.3" requires_python = ">=3.9" summary = "Yet another URL library" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" dependencies = [ "idna>=2.0", "multidict>=4.0", @@ -3086,6 +3320,7 @@ version = "3.21.0" requires_python = ">=3.9" summary = "Backport of pathlib-compatible object wrapper for zip files" groups = ["default"] +marker = "python_full_version == \"3.12.5\"" files = [ {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, diff --git a/preprocessing/mask_generator.py b/preprocessing/mask_generator.py index 0c0bc98..234f846 100644 --- a/preprocessing/mask_generator.py +++ b/preprocessing/mask_generator.py @@ -13,8 +13,8 @@ # Prospective test cases # Prospective tumor cases -SLIDES_PATH = "/mnt/data/scans/AI scans/Prostata/" -MASK_DEST = "./data/tissue_masks" +SLIDES_PATH = "/mnt/data/MOU/prostate/tile_level_annotations/" +MASK_DEST = "./mask/tissue_masks" LEVEL = 3 @@ -28,75 +28,17 @@ def process_slide(slide_path: Path) -> None: yres = 1000 / (float(slide.properties[PROPERTY_NAME_MPP_Y]) * downsample) slide = pyvips.Image.new_from_file(slide_path, level=LEVEL) - mask = tissue_mask(slide) + mask = tissue_mask(slide, xres) mask_path = Path(MASK_DEST, f"{Path(slide_path).stem}.tiff") mask_path.parent.mkdir(exist_ok=True, parents=True) - write_big_tiff(mask, path=mask_path, xres=xres, yres=yres) + write_big_tiff(mask, path=mask_path, mpp_x=xres, mpp_y=yres) -BROKEN_SLIDES = { - "P-2016_3829-04-1.mrxs", - "P-2016_3732-06-1.mrxs", - "P-2016_3732-03-1.mrxs", - "P-2016_3760-06-1.mrxs", - "P-2016_3629-13-1.mrxs", - "P-2016_3926-03-1.mrxs", - "P-2016_3852-02-1.mrxs", - "P-2016_3988-07-1.mrxs", - "P-2016_3852-01-1.mrxs", - "P-2016_3851-02-1.mrxs", - "P-2016_3629-12-1.mrxs", - "P-2016_3667-10-0.mrxs", - "P-2016_3606-04-1.mrxs", - "P-2016_3597-10-1.mrxs", - "P-2016_3988-10-1.mrxs", - "P-2016_3829-01-1.mrxs", - "P-2016_3926-02-1.mrxs", - "P-2019_3025-03-1.mrxs", - "P-2016_3760-04-1.mrxs", - "P-2016_3732-04-1.mrxs", - "P-2016_3667-09-1.mrxs", - "P-2019_3292-06-1.mrxs", - "P-2016_3988-02-1.mrxs", - "P-2016_3667-11-0.mrxs", - "P-2016_3606-05-1.mrxs", - "P-2016_3851-09-1.mrxs", - "P-2016_3627-09-1.mrxs", - "P-2016_3606-03-1.mrxs", - "P-2016_3627-10-1.mrxs", - "P-2016_3852-03-1.mrxs", - "P-2016_3851-07-1.mrxs", - "P-2016_3829-03-1.mrxs", - "P-2016_3627-06-1.mrxs", - "P-2016_3597-09-1.mrxs", - "P-2016_3926-01-1.mrxs", - "P-2016_3629-11-1.mrxs", - "P-2016_3760-03-1.mrxs", -} - def main() -> None: - folders = [ - # "archive tumor cases", #DONE - # "archive negative cases", # DONE - # "Prospective negative cases", #DONE - # "Prospective test cases", # DONE - # "Prospective tumor cases" # DONE - ] - - for folder in folders: - slides = [] - for slide in Path(SLIDES_PATH, folder).rglob("*.mrxs"): - if slide.name in BROKEN_SLIDES: - continue - slides.append(slide) - # slides.extend(list(Path(SLIDES_PATH, folder).rglob("*.mrxs"))) - - # slides = list(Path(SLIDES_PATH).rglob("*.mrxs")) - # process_slide(slides[0]) - process_items(slides, process_item=process_slide) - print(f"Processed {len(slides)} slides from {folder} folder") - + slides = list(Path(SLIDES_PATH).rglob("*.mrxs")) + # process_slide(slides[0]) + process_items(slides, process_item=process_slide) if __name__ == "__main__": main() diff --git a/preprocessing/tiler.py b/preprocessing/tiler.py index 55c5cda..f1b1561 100644 --- a/preprocessing/tiler.py +++ b/preprocessing/tiler.py @@ -10,8 +10,9 @@ from sklearn.model_selection import train_test_split -SLIDES_PATH = "/mnt/data/scans/AI scans/Prostata/" -TISSUE_MASKS_PATH = "./data/tissue_masks" +SLIDES_PATH = "/mnt/data/MOU/prostate/tile_level_annotations/" +TISSUE_MASKS_PATH = "./mask/tissue_masks" +TISSUE_MASKS_PATH = "/home/jovyan/staining/demo_data/masks" # level avg_mpp_x avg_mpp_y @@ -25,19 +26,20 @@ # 7 29.936095 29.994331 # 8 59.872189 59.988661 # 9 119.744379 119.97732 -SlideMPP = 0.46 +SlideMPP = 0.23 source = OpenSlideTileSource(mpp=SlideMPP, tile_extent=512, stride=256) -TISSUE_PERCENTAGE = 0.5 +MIN_TISSUE_PERCENTAGE = 0.0 class TissueMask(PyvipsMask[TileMetadata]): def forward_tile( self, tile_labels: TileMetadata, class_overlaps: dict[int, float] ) -> TileMetadata | None: - if class_overlaps.get(0, 0) > TISSUE_PERCENTAGE: + tissue = 1.0 - class_overlaps.get(0, 0) + if tissue <= MIN_TISSUE_PERCENTAGE: return None return tile_labels @@ -57,62 +59,11 @@ def handler(slide_path: Path) -> TiledSlideMetadata: return slide, tiles -BROKEN_SLIDES = { - "P-2016_3829-04-1.mrxs", - "P-2016_3732-06-1.mrxs", - "P-2016_3732-03-1.mrxs", - "P-2016_3760-06-1.mrxs", - "P-2016_3629-13-1.mrxs", - "P-2016_3926-03-1.mrxs", - "P-2016_3852-02-1.mrxs", - "P-2016_3988-07-1.mrxs", - "P-2016_3852-01-1.mrxs", - "P-2016_3851-02-1.mrxs", - "P-2016_3629-12-1.mrxs", - "P-2016_3667-10-0.mrxs", - "P-2016_3606-04-1.mrxs", - "P-2016_3597-10-1.mrxs", - "P-2016_3988-10-1.mrxs", - "P-2016_3829-01-1.mrxs", - "P-2016_3926-02-1.mrxs", - "P-2019_3025-03-1.mrxs", - "P-2016_3760-04-1.mrxs", - "P-2016_3732-04-1.mrxs", - "P-2016_3667-09-1.mrxs", - "P-2019_3292-06-1.mrxs", - "P-2016_3988-02-1.mrxs", - "P-2016_3667-11-0.mrxs", - "P-2016_3606-05-1.mrxs", - "P-2016_3851-09-1.mrxs", - "P-2016_3627-09-1.mrxs", - "P-2016_3606-03-1.mrxs", - "P-2016_3627-10-1.mrxs", - "P-2016_3852-03-1.mrxs", - "P-2016_3851-07-1.mrxs", - "P-2016_3829-03-1.mrxs", - "P-2016_3627-06-1.mrxs", - "P-2016_3597-09-1.mrxs", - "P-2016_3926-01-1.mrxs", - "P-2016_3629-11-1.mrxs", - "P-2016_3760-03-1.mrxs", -} def main() -> None: - folders = [ - "archive tumor cases", - "archive negative cases", - "Prospective negative cases", - "Prospective test cases", - "Prospective tumor cases", - ] - - slides = [] - for folder in folders: - for slide in Path(SLIDES_PATH, folder).rglob("*.mrxs"): - if slide.name in BROKEN_SLIDES: - continue - slides.append(slide) + slides = list(Path(SLIDES_PATH).rglob("*.mrxs")) + slides, test_slides = train_test_split(slides, test_size=0.2) train_slides, val_slides = train_test_split(slides, test_size=0.1) @@ -150,4 +101,15 @@ def main() -> None: if __name__ == "__main__": - main() + #main() + slides = [("/home/jovyan/staining/demo_data/P-2016_0077-08-1_hed_h0.6_e1.5.tiff")] + train_slides_df, train_tiles_df = tiling(slides=slides, handler=handler) + + mlflow.set_experiment(experiment_name="Stain-Normalization") + with mlflow.start_run(run_name="P-2016_0077-08-1_hed all tissue tiles") as _: + save_mlflow_dataset( + slides=train_slides_df, + tiles=train_tiles_df, + dataset_name="P-2016_0077-08-1_hed", + ) + diff --git a/pyproject.toml b/pyproject.toml index 8cba123..22faeae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "Stain Normalization" version = "0.1.0" authors = [{name = "Adam Lopatka"}] -requires-python = ">=3.11,<3.13" +requires-python = "3.12.5" readme = "README.md" license = { file = "LICENSE" } dependencies = [ diff --git a/stain_normalization/analysis/__init__.py b/stain_normalization/analysis/__init__.py new file mode 100644 index 0000000..33f4d99 --- /dev/null +++ b/stain_normalization/analysis/__init__.py @@ -0,0 +1,20 @@ +from .analyzer import StainAnalyzer +from .report import ( + REPORT_METRICS, + aggregate_average, + aggregate_rms, + aggregate_max, + aggregate_penalized_mean, + aggregate_power_mean, +) + + +__all__ = [ + "StainAnalyzer", + "REPORT_METRICS", + "aggregate_average", + "aggregate_rms", + "aggregate_max", + "aggregate_penalized_mean", + "aggregate_power_mean", +] diff --git a/stain_normalization/analysis/analyzer.py b/stain_normalization/analysis/analyzer.py new file mode 100644 index 0000000..9b0b6c5 --- /dev/null +++ b/stain_normalization/analysis/analyzer.py @@ -0,0 +1,189 @@ +"""Stain normalization analysis tool. + +Compares images using selected metrics, accumulates results, +and provides statistics and saving. +""" +from pathlib import Path +from typing import Union, Optional + +import numpy as np +import pandas as pd +from rationai.staining import estimate_stain_vectors +from skimage.metrics import structural_similarity + +from ..metrics.vector_metrics import compare_vectors +from ..metrics.image_metrics import compute_nmi, compute_pcc, compute_lab_brightness_psnr + + +class StainAnalyzer: + """Compare images using selected metrics, accumulate results. + + Args: + reference: Optional fixed reference image. If given, stain vectors + and NMI are pre-computed once. + metrics: Which metrics to compute. None = all. + """ + + AVAILABLE_METRICS = ['vectors', 'ssim', 'pcc', 'nmi', 'lab_psnr'] + PAIRED_ONLY = {'ssim', 'pcc', 'lab_psnr'} + + def __init__( + self, + reference: Optional[np.ndarray] = None, + metrics: Optional[list[str]] = None, + ): + self.metrics = metrics or self.AVAILABLE_METRICS + self._results: list[dict] = [] + + for m in self.metrics: + if m not in self.AVAILABLE_METRICS: + raise ValueError(f"Unknown metric '{m}'. Available: {self.AVAILABLE_METRICS}") + + # if we have reference image, precompute stain vectors and NMI + self._ref_img = reference + self._ref_vectors = None + self._ref_nmi = None + + if reference is not None: + if 'vectors' in self.metrics: + self._ref_vectors = estimate_stain_vectors(reference) + if 'nmi' in self.metrics: + self._ref_nmi = compute_nmi(reference) + + @property + def results(self) -> pd.DataFrame: + """Accumulated comparison results as DataFrame.""" + return pd.DataFrame(self._results) + + def clear(self) -> None: + """Clear accumulated results.""" + self._results.clear() + + def compare( + self, + image: np.ndarray, + image_id: Optional[str] = None, + reference: Optional[np.ndarray] = None, + ) -> dict: + """Compare an image against the reference and store the result. + + Args: + image: Image to compare. + image_id: Optional identifier for this comparison. + reference: Override reference image for this call. + + Returns: + Dict with metric results. + """ + + ref_img = reference if reference is not None else self._ref_img + if ref_img is None: + raise ValueError("No reference image. Pass one to __init__ or to compare().") + + is_paired = reference is not None + + if reference is not None: + ref_vectors = ( + estimate_stain_vectors(ref_img) + if 'vectors' in self.metrics else None + ) + ref_nmi = compute_nmi(ref_img) if 'nmi' in self.metrics else None + else: + ref_vectors = self._ref_vectors + ref_nmi = self._ref_nmi + + result = {'id': image_id} if image_id is not None else {} + + if 'vectors' in self.metrics: + img_vectors = estimate_stain_vectors(image) + vec_result = compare_vectors(ref_vectors, img_vectors) + result.update(vec_result) + img_vectors_paired = ( + img_vectors[[1, 0]] if vec_result['was_swapped'] else img_vectors + ) + for j, val in enumerate(ref_vectors.flatten()): + result[f'ref_vec_{j}'] = float(val) + for j, val in enumerate(img_vectors_paired.flatten()): + result[f'img_vec_{j}'] = float(val) + + if 'ssim' in self.metrics and is_paired: + result['ssim'] = float(structural_similarity( + ref_img, image, channel_axis=-1, data_range=255, + )) + + if 'pcc' in self.metrics and is_paired: + result['pcc'] = compute_pcc(ref_img, image) + + if 'nmi' in self.metrics: + img_nmi = compute_nmi(image) + result['ref_nmi'] = ref_nmi + result['nmi'] = img_nmi + result['nmi_diff'] = img_nmi - ref_nmi + + if 'lab_psnr' in self.metrics and is_paired: + result['lab_brightness_psnr'] = compute_lab_brightness_psnr(ref_img, image) + + self._results.append(result) + return result + + # --- Statistics --- + + def get_statistics(self) -> pd.DataFrame: + """Summary statistics for accumulated results. + + Returns: + DataFrame with mean, std, min, max, percentiles. + """ + df = self.results + numeric_cols = df.select_dtypes(include=[np.number]).columns + return df[numeric_cols].describe(percentiles=[0.05, 0.25, 0.5, 0.75, 0.95]) + + def get_baseline_ranges( + self, + percentile_low: float = 5, + percentile_high: float = 95, + ) -> dict: + """Get acceptable value ranges for each metric. + + Args: + percentile_low: Lower percentile bound. Defaults to 5. + percentile_high: Upper percentile bound. Defaults to 95. + + Returns: + Dict mapping metric names to (low, high) tuples. + """ + df = self.results + numeric_cols = df.select_dtypes(include=[np.number]).columns + return { + col: ( + float(df[col].quantile(percentile_low / 100)), + float(df[col].quantile(percentile_high / 100)), + ) + for col in numeric_cols + } + + def save_csv(self, output_dir: Union[str, Path]) -> Path: + """Save accumulated results and statistics as CSV files. + + Args: + output_dir: Directory to save files into. + + Returns: + Path to the output directory. + """ + output_dir = Path(output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + + self.results.to_csv(output_dir / "results.csv", index=True) + self.get_statistics().to_csv(output_dir / "statistics.csv") + + ranges = self.get_baseline_ranges() + with open(output_dir / "baseline_ranges.txt", 'w') as f: + f.write("Baseline Metric Ranges (5th-95th percentile)\n") + f.write("=" * 55 + "\n\n") + for metric, (low, high) in sorted(ranges.items()): + f.write(f"{metric:30s}: [{low:8.4f}, {high:8.4f}]\n") + + return output_dir + + diff --git a/stain_normalization/callbacks/__init__.py b/stain_normalization/callbacks/__init__.py index 86a8ce0..4aeaf95 100644 --- a/stain_normalization/callbacks/__init__.py +++ b/stain_normalization/callbacks/__init__.py @@ -1,6 +1,7 @@ +from stain_normalization.callbacks._base import NormalizationCallback from stain_normalization.callbacks.analysis_export import AnalysisExport -from stain_normalization.callbacks.save_wsi import SaveWSI from stain_normalization.callbacks.tiles_export import TilesExport +from stain_normalization.callbacks.wsi_assembler import WSIAssembler -__all__ = ["AnalysisExport", "SaveWSI", "TilesExport"] +__all__ = ["AnalysisExport", "NormalizationCallback", "TilesExport", "WSIAssembler"] diff --git a/stain_normalization/callbacks/_base.py b/stain_normalization/callbacks/_base.py new file mode 100644 index 0000000..4dba6cc --- /dev/null +++ b/stain_normalization/callbacks/_base.py @@ -0,0 +1,25 @@ +import numpy as np +import torch +from lightning import Callback +from omegaconf import DictConfig + + +class NormalizationCallback(Callback): + """Base callback providing denormalization helpers for model outputs.""" + + def __init__(self, normalization_config: DictConfig) -> None: + super().__init__() + self.mean = torch.tensor(normalization_config.mean).view(3, 1, 1) + self.std = torch.tensor(normalization_config.std).view(3, 1, 1) + + def denormalize(self, tensor: torch.Tensor) -> torch.Tensor: + """Reverse normalization: tensor → [0, 1] float.""" + device = tensor.device + return (tensor * self.std.to(device)) + self.mean.to(device) + + def tensor_to_image(self, tensor: torch.Tensor) -> np.ndarray: + """Convert model output tensor to uint8 HWC numpy array.""" + return ( + self.denormalize(tensor).clamp(0, 1).mul(255).byte() + .permute(1, 2, 0).cpu().numpy() + ) diff --git a/stain_normalization/callbacks/analysis_export.py b/stain_normalization/callbacks/analysis_export.py index f1e307d..ad0f603 100644 --- a/stain_normalization/callbacks/analysis_export.py +++ b/stain_normalization/callbacks/analysis_export.py @@ -1,78 +1,24 @@ from pathlib import Path import mlflow -import numpy as np -import pandas as pd import torch -from lightning import Callback, LightningModule, Trainer -from rationai.staining import estimate_stain_vectors -from skimage.metrics import structural_similarity as ssim -from skimage.color import rgb2lab -from skimage.metrics import peak_signal_noise_ratio +from lightning import LightningModule, Trainer +from omegaconf import DictConfig +from ..analysis.analyzer import StainAnalyzer +from ._base import NormalizationCallback -class AnalysisExport(Callback): - """Callback for exporting analysis metrics during testing.""" - def __init__(self, output_dir: str | Path, normalization_config) -> None: - super().__init__() +class AnalysisExport(NormalizationCallback): + """Exports analysis metrics during testing.""" + + def __init__(self, output_dir: str | Path, normalization_config: DictConfig) -> None: + super().__init__(normalization_config) self.output_dir = Path(output_dir) self.output_dir.mkdir(parents=True, exist_ok=True) - normalization = normalization_config - self.mean = torch.tensor(normalization.mean).view(3, 1, 1) - self.std = torch.tensor(normalization.std).view(3, 1, 1) - - self.df_diff = pd.DataFrame() - self.df_raw = pd.DataFrame() - - def denormalize(self, tensor: torch.Tensor) -> torch.Tensor: - std = self.std.to(tensor.device) - mean = self.mean.to(tensor.device) - return (tensor * std) + mean - - def tensor_to_image(self, tensor: torch.Tensor) -> np.ndarray: - tensor = self.denormalize(tensor).clamp(0, 1) - return (tensor * 255).byte().permute(1, 2, 0).cpu().numpy() - - def _estimate_vectors(self, img: np.ndarray) -> np.ndarray: - """Estimate stain vectors for a given image.""" - return estimate_stain_vectors(img, i0=240, alpha=1, beta=0.15) - - def _compare_vectors(self, vec1: np.ndarray, vec2: np.ndarray) -> float: - """Compute Euclidean distance between two stain vector estimates.""" - return float(np.linalg.norm(vec1 - vec2)) - - def _compute_ssim(self, img1: np.ndarray, img2: np.ndarray) -> float: - """Compute Structural Similarity Index (SSIM) between two images.""" - return float(ssim(img1, img2, channel_axis=-1, data_range=255)) - - def _compute_nmi(self, img: np.ndarray) -> float: - """Compute Normalized Median Intensity (NMI) of an image.""" - avg_rgb = img.mean(axis=2) - median_val = np.median(avg_rgb) - p95_val = np.percentile(avg_rgb, 95) - return median_val / p95_val if p95_val != 0 else 0.0 - - def _compute_pcc(self, img1: np.ndarray, img2: np.ndarray) -> float: - """Compute Pearson Correlation Coefficient (PCC) between two images.""" - img1_flat = img1.flatten().astype(np.float64) - img2_flat = img2.flatten().astype(np.float64) - if img1_flat.size == 0 or img2_flat.size == 0: - return 0.0 - - return float(np.corrcoef(img1_flat, img2_flat)[0, 1]) - - def _compute_lab_brightness_psnr(self, img1: np.ndarray, img2: np.ndarray) -> float: - """Compute PSNR on the L (lightness) channel in Lab color space. - """ - lab1 = rgb2lab(img1.astype(np.float32) / 255.0) - lab2 = rgb2lab(img2.astype(np.float32) / 255.0) - - L1 = lab1[:, :, 0] - L2 = lab2[:, :, 0] - - return float(peak_signal_noise_ratio(L1, L2, data_range=100.0)) + self.mod_analyzer = StainAnalyzer() + self.pred_analyzer = StainAnalyzer() def on_test_batch_end( self, @@ -83,84 +29,27 @@ def on_test_batch_end( batch_idx: int, dataloader_idx: int = 0, ) -> None: - """Called at the end of each test batch. - - Computes metrics for each sample and accumulates results. - """ + """Computes metrics for each sample and accumulates results.""" for b in range(len(outputs)): - predicted_tensor = outputs[b] original_img = batch[1][b]["original_image"].astype("uint8") modified_img = (batch[1][b]["modified_image"] * 255).astype("uint8") - index = batch[1][b]["index"] - predicted_img = self.tensor_to_image(predicted_tensor) - - vec_original = self._estimate_vectors(original_img) - vec_modified = self._estimate_vectors(modified_img) - vec_predicted = self._estimate_vectors(predicted_img) - - vec_diff_mod = self._compare_vectors(vec_original, vec_modified) - vec_diff_pred = self._compare_vectors(vec_original, vec_predicted) - - ssim_mod = self._compute_ssim(original_img, modified_img) - ssim_pred = self._compute_ssim(original_img, predicted_img) + predicted_img = self.tensor_to_image(outputs[b]) - nmi_original = self._compute_nmi(original_img) - nmi_modified = self._compute_nmi(modified_img) - nmi_predicted = self._compute_nmi(predicted_img) - - pcc_mod = self._compute_pcc(original_img, modified_img) - pcc_pred = self._compute_pcc(original_img, predicted_img) - - lab_psnr_mod = self._compute_lab_brightness_psnr(original_img, modified_img) - lab_psnr_pred = self._compute_lab_brightness_psnr(original_img, predicted_img) - - diff_row = { - "index": index, - "vector_diff_modified_vs_original": vec_diff_mod, - "vector_diff_predicted_vs_original": vec_diff_pred, - "ssim_modified_vs_original": ssim_mod, - "ssim_predicted_vs_original": ssim_pred, - "nmi_diff_modified_vs_original": nmi_modified - nmi_original, - "nmi_diff_predicted_vs_original": nmi_predicted - nmi_original, - "pcc_modified_vs_original": pcc_mod, - "pcc_predicted_vs_original": pcc_pred, - "lab_brightness_psnr_mod_vs_orig": lab_psnr_mod, - "lab_brightness_psnr_pred_vs_orig": lab_psnr_pred, - } - - raw_row = {"index": index} - for name, vec, nmi in zip( - ["original", "modified", "predicted"], - [vec_original, vec_modified, vec_predicted], - [nmi_original, nmi_modified, nmi_predicted], - strict=False, - ): - vectors = vec.flatten() - for j, val in enumerate(vectors): - raw_row[f"{name}_vec_{j}"] = val - raw_row[f"{name}_nmi"] = nmi - - self.df_diff = pd.concat( - [self.df_diff, pd.DataFrame([diff_row])], ignore_index=True - ) - self.df_raw = pd.concat( - [self.df_raw, pd.DataFrame([raw_row])], ignore_index=True - ) + self.mod_analyzer.compare(modified_img, reference=original_img) + self.pred_analyzer.compare(predicted_img, reference=original_img) def on_test_end( self, trainer: Trainer, pl_module: LightningModule, ) -> None: - """Called at the end of testing. - - Saves the collected metrics as CSV files and logs them as mlflow artifacts. - """ - diff_path = self.output_dir / "analysis_differences.csv" - raw_path = self.output_dir / "analysis_raw_vectors.csv" + """Saves collected metrics as CSV files and logs them as mlflow artifacts.""" + metrics_dir = self.output_dir / "analysis_metrics" - self.df_diff.to_csv(diff_path, index=False) - self.df_raw.to_csv(raw_path, index=False) + mod_dir = self.mod_analyzer.save_csv(metrics_dir / "modified") + pred_dir = self.pred_analyzer.save_csv(metrics_dir / "predicted") - mlflow.log_artifact(str(diff_path), artifact_path="analysis_metrics") - mlflow.log_artifact(str(raw_path), artifact_path="analysis_metrics") + for f in mod_dir.glob("*"): + mlflow.log_artifact(str(f), artifact_path="analysis_metrics/modified") + for f in pred_dir.glob("*"): + mlflow.log_artifact(str(f), artifact_path="analysis_metrics/predicted") diff --git a/stain_normalization/callbacks/save_wsi.py b/stain_normalization/callbacks/save_wsi.py deleted file mode 100644 index b74ba83..0000000 --- a/stain_normalization/callbacks/save_wsi.py +++ /dev/null @@ -1,56 +0,0 @@ -from pathlib import Path - -import torch -from lightning.pytorch.callbacks import Callback -from omegaconf import DictConfig -from PIL import Image - - -class SaveWSI(Callback): - def __init__( - self, output_dir: str | Path, normalization_config: DictConfig - ) -> None: - super().__init__() - self.output_root = Path(output_dir) - self.mean = torch.tensor(normalization_config.mean).view(3, 1, 1) - self.std = torch.tensor(normalization_config.std).view(3, 1, 1) - - def denormalize(self, tensor: torch.Tensor) -> torch.Tensor: - device = tensor.device - std = self.std.to(device) - mean = self.mean.to(device) - return (tensor * std) + mean - - def on_predict_batch_end( - self, - trainer, - pl_module, - outputs: list[torch.Tensor], - batch: tuple[torch.Tensor, list], - batch_idx: int, - dataloader_idx: int = 0, - ) -> None: - for b in range(len(outputs)): - predicted_image = outputs[b] - metadata = batch[1][b] - - slide_name = metadata["slide_name"] - level = metadata["level"] - original_image = Image.fromarray(metadata["original_image"].astype("uint8")) - filename = f"{metadata['xy']}.png" - - base_folder = self.output_root / str(slide_name) / str(level) - predicted_folder = base_folder / "predicted" - original_folder = base_folder / "original" - - predicted_folder.mkdir(parents=True, exist_ok=True) - original_folder.mkdir(parents=True, exist_ok=True) - - original_image.save(original_folder / filename) - - predicted_image = self.denormalize(predicted_image).clamp(0, 1) - predicted_image = (predicted_image * 255).byte() - predicted_image = Image.fromarray( - predicted_image.permute(1, 2, 0).cpu().numpy() - ) - predicted_image.save(predicted_folder / filename) diff --git a/stain_normalization/callbacks/tiles_export.py b/stain_normalization/callbacks/tiles_export.py index 0c44f07..bfd52f9 100644 --- a/stain_normalization/callbacks/tiles_export.py +++ b/stain_normalization/callbacks/tiles_export.py @@ -1,34 +1,23 @@ from pathlib import Path import torch -from lightning import Callback, LightningModule, Trainer +from lightning import LightningModule, Trainer from omegaconf import DictConfig from PIL import Image +from ._base import NormalizationCallback -class TilesExport(Callback): + +class TilesExport(NormalizationCallback): def __init__( self, output_dir: str | Path, normalization_config: DictConfig ) -> None: - super().__init__() + super().__init__(normalization_config) self.output_dir = Path(output_dir) self.output_dir.mkdir(parents=True, exist_ok=True) - normalization = normalization_config - self.mean = torch.tensor(normalization.mean).view(3, 1, 1) - self.std = torch.tensor(normalization.std).view(3, 1, 1) - - def denormalize(self, tensor: torch.Tensor) -> torch.Tensor: - device = tensor.device - std = self.std.to(device) - mean = self.mean.to(device) - return (tensor * std) + mean - def tensor_to_image(self, tensor: torch.Tensor) -> Image.Image: - tensor = self.denormalize(tensor) - tensor = tensor.clamp(0, 1) - tensor = (tensor * 255).byte() - return Image.fromarray(tensor.permute(1, 2, 0).cpu().numpy()) + return Image.fromarray(super().tensor_to_image(tensor)) def on_test_batch_end( self, @@ -39,7 +28,27 @@ def on_test_batch_end( batch_idx: int, dataloader_idx: int = 0, ) -> None: - self._save_images(outputs, batch, is_predict=False) + _, data = batch + for b in range(len(outputs)): + slide_name = data[b]["slide_name"] + xy = data[b]["xy"] + + slide_dir = self.output_dir / slide_name + slide_dir.mkdir(parents=True, exist_ok=True) + + self.tensor_to_image(outputs[b]).save( + slide_dir / f"{xy}_predicted.png" + ) + + original_image = Image.fromarray( + data[b]["original_image"].astype("uint8") + ) + original_image.save(slide_dir / f"{xy}_original.png") + + modified_image = Image.fromarray( + (data[b]["modified_image"] * 255).astype("uint8") + ) + modified_image.save(slide_dir / f"{xy}_modified.png") def on_predict_batch_end( self, @@ -50,27 +59,12 @@ def on_predict_batch_end( batch_idx: int, dataloader_idx: int = 0, ) -> None: - self._save_images(outputs, batch, is_predict=True) - - def _save_images(self, outputs, batch, is_predict: bool) -> None: - inputs, data = batch + _, data = batch for b in range(len(outputs)): - index = data[b].get("index", 0) - - predicted_image = outputs[b] - name = data[b].get("name", str(index)) - predicted_image = self.tensor_to_image(predicted_image) - - predicted_image.save(self.output_dir / f"{name}.png") + slide_name = data[b]["slide_name"] + xy = data[b]["xy"] - if not is_predict: - original_image = Image.fromarray( - data[b]["original_image"].astype("uint8") - ) - original_image.save(self.output_dir / f"{name}_original.png") + slide_dir = self.output_dir / slide_name + slide_dir.mkdir(parents=True, exist_ok=True) - if "modified_image" in data[b]: - modified_image = Image.fromarray( - (data[b]["modified_image"] * 255).astype("uint8") - ) - modified_image.save(self.output_dir / f"{name}_modified.png") + self.tensor_to_image(outputs[b]).save(slide_dir / f"{xy}.png") diff --git a/stain_normalization/callbacks/wsi_assembler.py b/stain_normalization/callbacks/wsi_assembler.py new file mode 100644 index 0000000..31a0745 --- /dev/null +++ b/stain_normalization/callbacks/wsi_assembler.py @@ -0,0 +1,232 @@ +"""Callback for assembling predicted tiles into whole-slide pyramid TIFFs.""" + +# DEV: Key design decisions: +# DEV: - One slide buffer at a time — slides are processed sequentially by ConcatDataset, +# DEV: so we open a buffer on the first tile of a slide and close it when the slide changes. +# DEV: This keeps peak temp disk at ~one slide (~5-10 GB at level 0 with sparse tissue), +# DEV: regardless of how many slides are in the predict set. +# DEV: - Buffers are zero-initialized (sparse mmap) — only pages where tiles actually land +# DEV: get written to disk. White fill for untouched background is applied by pyvips at save. +# DEV: - Running average (uint8 result + uint8 count) instead of float32 sum accumulator. +# DEV: Cuts temp disk ~4x vs the sum approach. Float math is done only per-tile (512x512). +# DEV: - Slide transition is detected per-tile inside the batch loop, not per-batch, +# DEV: so transition batches (last tiles of A + first tiles of B) are handled correctly. + +import tempfile +import traceback +from dataclasses import dataclass +from pathlib import Path + +import numpy as np +import torch +from lightning import LightningModule, Trainer +from omegaconf import DictConfig +from PIL.ImageCms import createProfile + +from ._base import NormalizationCallback + + +def _srgb_icc_bytes() -> bytes: + """Generate sRGB ICC profile bytes for embedding in TIFFs.""" + raw = createProfile("sRGB") + # Pillow < 10: .tobuffer(), Pillow >= 10: .tobytes() + try: + return raw.tobuffer() + except AttributeError: + from PIL.ImageCms import ImageCmsProfile + return ImageCmsProfile(raw).tobytes() + + +@dataclass +class _SlideMeta: + path: str + level: int + extent_x: int + extent_y: int + tile_extent_x: int + tile_extent_y: int + mpp_x: float + mpp_y: float + + +@dataclass +class _SlideBuffers: + meta: _SlideMeta + temp_dir: tempfile.TemporaryDirectory + result_buffer: np.memmap + count_buffer: np.memmap + + +class WSIAssembler(NormalizationCallback): + """Assembles predicted tiles back into whole-slide pyramid TIFFs.""" + + def __init__( + self, + output_dir: str | Path, + normalization_config: DictConfig, + temp_dir: str | Path | None = None, + ) -> None: + super().__init__(normalization_config) + self.output_dir = Path(output_dir) + self.temp_dir = str(temp_dir) if temp_dir else None + self._slide_meta: dict[str, _SlideMeta] = {} + self._active: _SlideBuffers | None = None + self._active_name: str | None = None + + def on_predict_start(self, trainer: Trainer, pl_module: LightningModule) -> None: + self.output_dir.mkdir(parents=True, exist_ok=True) + slides_df = trainer.datamodule.predict.slides + + # Cache metadata only — buffers are opened lazily per slide + for _, row in slides_df.iterrows(): + name = Path(row.path).stem + self._slide_meta[name] = _SlideMeta( + path=row.path, + level=int(row.level), + extent_x=int(row.extent_x), + extent_y=int(row.extent_y), + tile_extent_x=int(row.tile_extent_x), + tile_extent_y=int(row.tile_extent_y), + mpp_x=float(row.mpp_x), + mpp_y=float(row.mpp_y), + ) + + def _open_slide(self, slide_name: str) -> None: + """Allocate memmap buffers for one slide.""" + meta = self._slide_meta[slide_name] + h, w = meta.extent_y, meta.extent_x + + tmp = tempfile.TemporaryDirectory( + prefix=f"wsi_{slide_name}_", dir=self.temp_dir + ) + result_buf = np.memmap( + Path(tmp.name) / "result.raw", + dtype=np.uint8, + mode="w+", + shape=(h, w, 3), + ) + count_buf = np.memmap( + Path(tmp.name) / "count.raw", + dtype=np.uint8, + mode="w+", + shape=(h, w), + ) + + self._active = _SlideBuffers( + meta=meta, + temp_dir=tmp, + result_buffer=result_buf, + count_buffer=count_buf, + ) + self._active_name = slide_name + + def _close_slide(self) -> None: + """Save and free the currently active slide.""" + if self._active is None: + return + try: + self._save_slide(self._active_name, self._active) + except Exception: + traceback.print_exc() + finally: + del self._active.result_buffer + del self._active.count_buffer + self._active.temp_dir.cleanup() + self._active = None + self._active_name = None + + def on_predict_batch_end( + self, + trainer: Trainer, + pl_module: LightningModule, + outputs: list[torch.Tensor], + batch: tuple[torch.Tensor, list[dict]], + batch_idx: int, + dataloader_idx: int = 0, + ) -> None: + for b in range(len(outputs)): + metadata = batch[1][b] + slide_name = metadata["slide_name"] + + if slide_name not in self._slide_meta: + print(f"Unknown slide '{slide_name}', skipping tile.") + continue + + if slide_name != self._active_name: + if self._active_name is not None: + print(f"Slide transition: {self._active_name} → {slide_name}") + self._close_slide() + self._open_slide(slide_name) + + tile = self.tensor_to_image(outputs[b]) + x, y = (int(v) for v in metadata["xy"].split("_")) + self._place_tile(tile, x, y) + + def _place_tile(self, tile: np.ndarray, x: int, y: int) -> None: + """Place a predicted tile into the active slide buffer with overlap averaging.""" + sb = self._active + ex, ey = sb.meta.extent_x, sb.meta.extent_y + + h, w = min(tile.shape[0], ey - y), min(tile.shape[1], ex - x) + tile = tile[:h, :w] + + region = sb.result_buffer[y:y + h, x:x + w] + count = sb.count_buffer[y:y + h, x:x + w] + + # Running average: avg = (old * n + new) / (n + 1) + overlap = count > 0 + if overlap.any(): + n = count[:, :, np.newaxis].astype(np.float32) + blended = np.where( + overlap[:, :, np.newaxis], + (region.astype(np.float32) * n + tile) / (n + 1), + tile, + ) + sb.result_buffer[y:y + h, x:x + w] = np.clip(blended, 0, 255).astype(np.uint8) + else: + sb.result_buffer[y:y + h, x:x + w] = tile + + sb.count_buffer[y:y + h, x:x + w] = count + 1 + + def on_predict_end(self, trainer: Trainer, pl_module: LightningModule) -> None: + self._close_slide() + self._slide_meta.clear() + + def _save_slide(self, slide_name: str, sb: _SlideBuffers) -> None: + # Imported here — module-level import causes OpenSlide segfault (libtiff conflict). + import pyvips + + meta = sb.meta + sb.result_buffer.flush() + sb.count_buffer.flush() + + result_path = Path(sb.temp_dir.name) / "result.raw" + count_path = Path(sb.temp_dir.name) / "count.raw" + + result_img = pyvips.Image.rawload(str(result_path), meta.extent_x, meta.extent_y, 3) + result_img = result_img.copy(interpretation=pyvips.Interpretation.SRGB) + + count_img = pyvips.Image.rawload(str(count_path), meta.extent_x, meta.extent_y, 1) + mask = count_img > 0 + # add white background for untouched areas (count=0) + white = (pyvips.Image.black(meta.extent_x, meta.extent_y, bands=3) + 255).cast( + pyvips.BandFormat.UCHAR + ) + final_img = mask.ifthenelse(result_img, white) + + final_img.set_type( + pyvips.GValue.blob_type, "icc-profile-data", _srgb_icc_bytes() + ) + + output_path = self.output_dir / f"{slide_name}_norm.tiff" + final_img.tiffsave( + str(output_path), + bigtiff=True, + compression=pyvips.enums.ForeignTiffCompression.DEFLATE, + tile=True, + tile_width=512, + tile_height=512, + pyramid=True, + xres=1000.0 / meta.mpp_x, + yres=1000.0 / meta.mpp_y, + ) diff --git a/stain_normalization/data/data_module.py b/stain_normalization/data/data_module.py index eb1f7ae..bd462bf 100644 --- a/stain_normalization/data/data_module.py +++ b/stain_normalization/data/data_module.py @@ -1,14 +1,12 @@ from collections.abc import Iterable -from typing import Any from hydra.utils import instantiate from lightning import LightningDataModule from omegaconf import DictConfig -from torch import Tensor from torch.utils.data import DataLoader from stain_normalization.data.utils import collate_fn -from stain_normalization.typing import Input +from stain_normalization.type_aliases import Batch, PredictBatch class DataModule(LightningDataModule): @@ -32,7 +30,7 @@ def setup(self, stage: str) -> None: case "predict": self.predict = instantiate(self.datasets["predict"]) - def train_dataloader(self) -> Iterable[Input]: + def train_dataloader(self) -> Iterable[Batch]: return DataLoader( self.train, batch_size=self.batch_size, @@ -42,7 +40,7 @@ def train_dataloader(self) -> Iterable[Input]: persistent_workers=self.num_workers > 0, ) - def val_dataloader(self) -> Iterable[Input]: + def val_dataloader(self) -> Iterable[Batch]: return DataLoader( self.val, batch_size=self.batch_size, @@ -50,7 +48,7 @@ def val_dataloader(self) -> Iterable[Input]: persistent_workers=self.num_workers > 0, ) - def test_dataloader(self) -> Iterable[Input]: + def test_dataloader(self) -> Iterable[PredictBatch]: return DataLoader( self.test, batch_size=self.batch_size, @@ -58,7 +56,7 @@ def test_dataloader(self) -> Iterable[Input]: collate_fn=collate_fn, ) - def predict_dataloader(self) -> Iterable[tuple[Tensor, list[dict[str, Any]]]]: + def predict_dataloader(self) -> Iterable[PredictBatch]: return DataLoader( self.predict, batch_size=self.batch_size, diff --git a/stain_normalization/data/datasets/predict_dataset.py b/stain_normalization/data/datasets/predict_dataset.py index ebd85fb..9fc6650 100644 --- a/stain_normalization/data/datasets/predict_dataset.py +++ b/stain_normalization/data/datasets/predict_dataset.py @@ -6,7 +6,7 @@ from rationai.mlkit.data.datasets import MetaTiledSlides, OpenSlideTilesDataset from torch.utils.data import Dataset -from stain_normalization.typing import PredictSample +from stain_normalization.type_aliases import PredictSample class PredictDataset(MetaTiledSlides[PredictSample]): @@ -54,7 +54,6 @@ def __len__(self) -> int: def __getitem__(self, idx: int) -> PredictSample: input_image_255 = self.slide_tiles[idx] slide_name = self.slide_tiles.slide_path.stem - level = self.slide_tiles.level x = self.slide_tiles.tiles.iloc[idx]["x"] y = self.slide_tiles.tiles.iloc[idx]["y"] @@ -66,11 +65,6 @@ def __getitem__(self, idx: int) -> PredictSample: input_image = self.to_tensor(image=input_image)["image"] return input_image, { - "original_image": input_image_255, - "modified_image": None, "slide_name": slide_name, - "level": level, - "index": idx, "xy": f"{x}_{y}", - "name": f"{slide_name}-{x}-{y}", } diff --git a/stain_normalization/data/datasets/test_dataset.py b/stain_normalization/data/datasets/test_dataset.py index 151b373..ef094f9 100644 --- a/stain_normalization/data/datasets/test_dataset.py +++ b/stain_normalization/data/datasets/test_dataset.py @@ -6,7 +6,7 @@ from rationai.mlkit.data.datasets import MetaTiledSlides, OpenSlideTilesDataset from torch.utils.data import Dataset -from stain_normalization.typing import PredictSample +from stain_normalization.type_aliases import PredictSample class TestDataset(MetaTiledSlides[PredictSample]): @@ -57,6 +57,9 @@ def __len__(self) -> int: def __getitem__(self, idx: int) -> PredictSample: original_image_255 = self.slide_tiles[idx] + slide_name = self.slide_tiles.slide_path.stem + x = self.slide_tiles.tiles.iloc[idx]["x"] + y = self.slide_tiles.tiles.iloc[idx]["y"] # Create "wrong" image to use as input. Outputs image in float 0-1 modified_image_raw = self.modify(image=original_image_255)["image"] @@ -76,6 +79,7 @@ def __getitem__(self, idx: int) -> PredictSample: "original_image_tensor": original_image, "original_image": original_image_255, "modified_image": modified_image_raw, - "index": idx, + "slide_name": slide_name, + "xy": f"{x}_{y}", }, ) diff --git a/stain_normalization/data/datasets/train_dataset.py b/stain_normalization/data/datasets/train_dataset.py index 2a0fab7..1d46afe 100644 --- a/stain_normalization/data/datasets/train_dataset.py +++ b/stain_normalization/data/datasets/train_dataset.py @@ -6,7 +6,7 @@ from rationai.mlkit.data.datasets import MetaTiledSlides, OpenSlideTilesDataset from torch.utils.data import Dataset -from stain_normalization.typing import Sample +from stain_normalization.type_aliases import Sample class TrainDataset(MetaTiledSlides[Sample]): diff --git a/stain_normalization/data/modification/exposure_adjustment.py b/stain_normalization/data/modification/exposure_adjustment.py index 69e9e5c..e1ff4c2 100644 --- a/stain_normalization/data/modification/exposure_adjustment.py +++ b/stain_normalization/data/modification/exposure_adjustment.py @@ -36,6 +36,8 @@ def apply(self, img: NDArray, **params) -> NDArray: """ brightness_factor = np.random.uniform(*self.brightness_range) img = img.astype(np.float32) + if img.max() > 1.0: + img = img / 255.0 img = np.clip(img * brightness_factor, 0.0, 1.0) return img diff --git a/stain_normalization/metrics/__init__.py b/stain_normalization/metrics/__init__.py new file mode 100644 index 0000000..0f4f8f3 --- /dev/null +++ b/stain_normalization/metrics/__init__.py @@ -0,0 +1,16 @@ +from .image_metrics import ( + compute_lab_brightness_psnr, + compute_nmi, + compute_pcc, +) +from .vector_metrics import ( + compare_vectors, +) + + +__all__ = [ + "compare_vectors", + "compute_lab_brightness_psnr", + "compute_nmi", + "compute_pcc", +] diff --git a/stain_normalization/metrics/image_metrics.py b/stain_normalization/metrics/image_metrics.py new file mode 100644 index 0000000..28f806d --- /dev/null +++ b/stain_normalization/metrics/image_metrics.py @@ -0,0 +1,56 @@ +import numpy as np +from skimage.color import rgb2lab +from skimage.metrics import peak_signal_noise_ratio + + +def compute_nmi(img: np.ndarray) -> float: + """Normalized Median Intensity — measures relative brightness of an image. + + Args: + img: RGB image. + + Returns: + Ratio of median to 95th percentile intensity. + """ + avg_rgb = img.mean(axis=2) + median_val = np.median(avg_rgb) + p95_val = np.percentile(avg_rgb, 95) + + if p95_val == 0: + return 0.0 + + return float(median_val / p95_val) + + +def compute_pcc(img1: np.ndarray, img2: np.ndarray) -> float: + """Pearson Correlation Coefficient between two images. + + Args: + img1: First image. + img2: Second image. + + Returns: + PCC value, or 0.0 if either image has zero variance. + """ + img1_flat = img1.flatten().astype(np.float64) + img2_flat = img2.flatten().astype(np.float64) + + if np.std(img1_flat) == 0 or np.std(img2_flat) == 0: + return 0.0 + + return float(np.corrcoef(img1_flat, img2_flat)[0, 1]) + + +def compute_lab_brightness_psnr(img1: np.ndarray, img2: np.ndarray) -> float: + """PSNR on the L* channel in Lab color space. + + Args: + img1: First RGB image. + img2: Second RGB image. + + Returns: + PSNR in dB on the lightness channel. + """ + lab1 = rgb2lab(img1.astype(np.float32) / 255.0) + lab2 = rgb2lab(img2.astype(np.float32) / 255.0) + return float(peak_signal_noise_ratio(lab1[:, :, 0], lab2[:, :, 0], data_range=100.0)) diff --git a/stain_normalization/metrics/vector_metrics.py b/stain_normalization/metrics/vector_metrics.py new file mode 100644 index 0000000..1348f72 --- /dev/null +++ b/stain_normalization/metrics/vector_metrics.py @@ -0,0 +1,75 @@ +import numpy as np +from skimage.color import rgb2lab + + +def _od_to_lab(od_vector: np.ndarray) -> np.ndarray: + """Convert optical density vector to Lab color. + + Args: + od_vector: Stain vector in optical density space. + + Returns: + Color in Lab space as [L, a, b]. + """ + # Calculate RGB from optical density by reversing the process in estimate_stain_vectors. + # default i0=240 (transmitted light intensity) + rgb = np.clip(240 * np.exp(-od_vector), 0, 255) / 255.0 + return rgb2lab(rgb.reshape(1, 1, 3)).flatten() + + +def delta_e76(lab1: np.ndarray, lab2: np.ndarray) -> float: + """CIE76 Delta E with dL=0 (chromaticity only). + + CIE76 Delta E is sqrt(dL^2 + da^2 + db^2). We set dL=0 because we compare + dyes not colors, so brightness is irrelevant. + + Args: + lab1: First color in Lab space. + lab2: Second color in Lab space. + + Returns: + sqrt(da^2 + db^2). + """ + da = lab1[1] - lab2[1] + db = lab1[2] - lab2[2] + return float(np.sqrt(da**2 + db**2)) + + +def compare_vectors( + vecs1: np.ndarray, + vecs2: np.ndarray, +) -> dict: + """Compare two sets of stain vectors in Lab chromaticity space. + + Args: + vecs1: Stain vectors from the first image in OD space. + vecs2: Stain vectors from the second image in OD space. + + Returns: + Dict with d_hematoxylin, d_eosin and was_swapped. + Returns NaN distances if either vector set contains NaN. + """ + if np.any(np.isnan(vecs1)) or np.any(np.isnan(vecs2)): + return { + 'd_hematoxylin': float('nan'), 'd_eosin': float('nan'), + 'was_swapped': False, + } + + + sim_straight = np.dot(vecs1[0], vecs2[0]) + np.dot(vecs1[1], vecs2[1]) + sim_swapped = np.dot(vecs1[0], vecs2[1]) + np.dot(vecs1[1], vecs2[0]) + was_swapped = sim_swapped > sim_straight + vecs2_paired = vecs2[[1, 0]] if was_swapped else vecs2 + + lab1_a = _od_to_lab(vecs1[0]) + lab1_b = _od_to_lab(vecs1[1]) + lab2_a = _od_to_lab(vecs2_paired[0]) + lab2_b = _od_to_lab(vecs2_paired[1]) + + return { + 'd_hematoxylin': delta_e76(lab1_a, lab2_a), + 'd_eosin': delta_e76(lab1_b, lab2_b), + 'was_swapped': was_swapped, + } + + diff --git a/stain_normalization/modeling/l1ssim_loss.py b/stain_normalization/modeling/l1ssim_loss.py index 474fe14..be13691 100644 --- a/stain_normalization/modeling/l1ssim_loss.py +++ b/stain_normalization/modeling/l1ssim_loss.py @@ -8,14 +8,11 @@ https://github.com/graphdeco-inria/gaussian-splatting/blob/472689c0dc70417448fb451bf529ae532d32c095/utils/loss_utils.py """ -from math import exp - import torch import torch.nn as nn import torch.nn.functional as F -from torch.autograd import Variable - +from math import exp class L1SSIMLoss(nn.Module): def __init__(self, lambda_dssim: float = 0.6, lambda_l1: float = 0.2, lambda_lum: float = 0.2, lambda_gdl: float = 0.1): @@ -45,14 +42,14 @@ def forward(self, image: torch.Tensor, target_image: torch.Tensor) -> torch.Tens gdl_loss = gradient_loss(image, target_image) # Luminance / brightness loss - lum_loss = brightness_loss(image, target_image) + brig_loss = brightness_loss(image, target_image) # total weighted loss total_loss = ( self.lambda_l1 * l1_loss + self.lambda_dssim * ssim_loss + self.lambda_gdl * gdl_loss - + self.lambda_lum * lum_loss + + self.lambda_lum * brig_loss ) return total_loss @@ -91,21 +88,10 @@ def gaussian(window_size, sigma): return gauss / gauss.sum() -def luminance_loss( - pred: torch.Tensor, - target: torch.Tensor, - he_weights=[0.33, 0.33, 0.33]) -> torch.Tensor: - device = pred.device - weights = torch.tensor(he_weights, device=device).view(1, 3, 1, 1) - - lum_pred = (pred * weights).sum(dim=1, keepdim=True) - lum_target = (target * weights).sum(dim=1, keepdim=True) - - lum_loss = F.l1_loss(lum_pred, lum_target) - return lum_loss - -def brightness_loss(pred, target, he_weights=[0.33, 0.33, 0.33]): +def brightness_loss(pred, target, he_weights=None): device = pred.device + if he_weights is None: + he_weights = [0.33, 0.33, 0.33] weights = torch.tensor(he_weights, device=device).view(1, 3, 1, 1) pred_mean = (pred * weights).mean(dim=[2, 3], keepdim=True) diff --git a/stain_normalization/stain_normalization_model.py b/stain_normalization/stain_normalization_model.py index c1f9da1..7530224 100644 --- a/stain_normalization/stain_normalization_model.py +++ b/stain_normalization/stain_normalization_model.py @@ -1,5 +1,3 @@ -from typing import Any - from lightning import LightningModule from torch import Tensor, stack from torch.optim import Adam @@ -9,7 +7,7 @@ from torchmetrics.regression import MeanAbsoluteError from stain_normalization.modeling import L1SSIMLoss, UNet -from stain_normalization.typing import Input, Outputs +from stain_normalization.type_aliases import Batch, Outputs, PredictBatch class StainNormalizationModel(LightningModule): @@ -22,16 +20,15 @@ def __init__(self) -> None: { "ssim": StructuralSimilarityIndexMeasure(), "l1": MeanAbsoluteError() - } ) self.test_metrics = self.val_metrics.clone(prefix="test/") self.val_metrics.prefix = "validation/" - def forward(self, x: Input) -> Outputs: + def forward(self, x: Tensor) -> Outputs: return self.unet(x) - def training_step(self, batch: Input) -> Tensor: + def training_step(self, batch: Batch) -> Tensor: inputs, targets = batch outputs = self(inputs) @@ -40,7 +37,7 @@ def training_step(self, batch: Input) -> Tensor: return loss - def validation_step(self, batch: Input) -> None: + def validation_step(self, batch: Batch) -> None: inputs, targets = batch outputs = self(inputs) @@ -53,7 +50,7 @@ def validation_step(self, batch: Input) -> None: on_epoch=True, ) - def test_step(self, batch: Input) -> Outputs: + def test_step(self, batch: PredictBatch) -> Outputs: inputs, data = batch outputs = self(inputs) targets = stack([item["original_image_tensor"] for item in data]) @@ -65,7 +62,7 @@ def test_step(self, batch: Input) -> Outputs: ) return outputs - def predict_step(self, batch: tuple[Tensor, Any], batch_idx: int) -> Outputs: + def predict_step(self, batch: PredictBatch, batch_idx: int) -> Outputs: inputs = batch[0] return self(inputs) diff --git a/stain_normalization/type_aliases.py b/stain_normalization/type_aliases.py new file mode 100644 index 0000000..535edda --- /dev/null +++ b/stain_normalization/type_aliases.py @@ -0,0 +1,13 @@ +from typing import Any, TypeAlias + +from torch import Tensor + + +Sample: TypeAlias = tuple[Tensor, Tensor] +PredictSample: TypeAlias = tuple[Tensor, dict[str, Any]] + +# Batches - after collate +Batch: TypeAlias = tuple[Tensor, Tensor] +PredictBatch: TypeAlias = tuple[Tensor, list[dict[str, Any]]] + +Outputs: TypeAlias = Tensor diff --git a/stain_normalization/typing.py b/stain_normalization/typing.py deleted file mode 100644 index a379a3c..0000000 --- a/stain_normalization/typing.py +++ /dev/null @@ -1,11 +0,0 @@ -from typing import Any, TypeAlias - -from torch import Tensor - - -PredictSample: TypeAlias = tuple[Tensor, dict[str, Any]] -Sample: TypeAlias = tuple[Tensor, Tensor] - -Input: TypeAlias = Sample - -Outputs: TypeAlias = Tensor From ec45c50816be07f38470b8b6e6a44d89e058e885 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni.cz> Date: Wed, 11 Mar 2026 05:37:22 +0100 Subject: [PATCH 80/85] fix: resolve mypy/ruff --- analyze_dataset.py | 49 +++++--- demo.py | 8 +- preprocessing/mask_generator.py | 2 +- preprocessing/tiler.py | 6 +- pyproject.toml | 6 +- stain_normalization/analysis/__init__.py | 16 +-- stain_normalization/analysis/analyzer.py | 108 +++++++++++------- stain_normalization/callbacks/_base.py | 13 ++- .../callbacks/analysis_export.py | 13 ++- stain_normalization/callbacks/tiles_export.py | 19 ++- .../callbacks/wsi_assembler.py | 40 ++++--- .../data/datasets/predict_dataset.py | 2 +- .../data/datasets/train_dataset.py | 8 +- .../modification/combiend_modification.py | 6 +- .../data/modification/exposure_adjustment.py | 6 +- .../data/modification/hed_factor.py | 6 +- .../data/modification/hvs_modification.py | 6 +- stain_normalization/metrics/__init__.py | 4 +- stain_normalization/metrics/image_metrics.py | 14 ++- stain_normalization/metrics/vector_metrics.py | 26 ++--- stain_normalization/modeling/l1ssim_loss.py | 99 ++++++++++------ stain_normalization/modeling/unet.py | 30 +++-- .../stain_normalization_model.py | 5 +- stain_normalization/type_aliases.py | 6 +- 24 files changed, 290 insertions(+), 208 deletions(-) diff --git a/analyze_dataset.py b/analyze_dataset.py index d10064f..2af28fa 100644 --- a/analyze_dataset.py +++ b/analyze_dataset.py @@ -14,9 +14,12 @@ """ import argparse +from collections.abc import Generator from pathlib import Path +from typing import Any import numpy as np +import pandas as pd from PIL import Image from rationai.mlkit.data.datasets import MetaTiledSlides, OpenSlideTilesDataset from tqdm import tqdm @@ -25,11 +28,13 @@ from stain_normalization.analysis.report import REPORT_METRICS -def load_image(path: str | Path) -> np.ndarray: +def load_image(path: str | Path) -> np.ndarray[Any, Any]: return np.array(Image.open(path).convert("RGB")) -def iterate_tiles(slides, tiles): +def iterate_tiles( + slides: pd.DataFrame, tiles: pd.DataFrame +) -> Generator[tuple[str, Any, str], None, None]: """Yield (slide_name, tile_uint8, image_id) for each tile.""" for _, slide in slides.iterrows(): slide_name = Path(slide.path).stem @@ -47,11 +52,13 @@ def iterate_tiles(slides, tiles): ) for i in range(len(dataset)): - image_id = f"{slide_name}_{slide_tiles.iloc[i]['x']}_{slide_tiles.iloc[i]['y']}" + image_id = ( + f"{slide_name}_{slide_tiles.iloc[i]['x']}_{slide_tiles.iloc[i]['y']}" + ) yield slide_name, dataset[i], image_id -def run_reference_mode(args): +def run_reference_mode(args: argparse.Namespace) -> tuple[StainAnalyzer, int]: """Compare all tiles in a dataset against a single reference image.""" ref_img = load_image(args.reference) slides, tiles = MetaTiledSlides.load_slides_and_tiles(paths=[], uris=args.uri) @@ -69,10 +76,14 @@ def run_reference_mode(args): return analyzer, len(analyzer.results) -def run_paired_mode(args): +def run_paired_mode(args: argparse.Namespace) -> tuple[StainAnalyzer, int]: """Compare matching tiles between two datasets (original vs compared).""" - orig_slides, orig_tiles = MetaTiledSlides.load_slides_and_tiles(paths=[], uris=args.original) - comp_slides, comp_tiles = MetaTiledSlides.load_slides_and_tiles(paths=[], uris=args.compared) + orig_slides, orig_tiles = MetaTiledSlides.load_slides_and_tiles( + paths=[], uris=args.original + ) + comp_slides, comp_tiles = MetaTiledSlides.load_slides_and_tiles( + paths=[], uris=args.compared + ) print(f"Original: {len(orig_slides)} slides, {len(orig_tiles)} tiles") print(f"Compared: {len(comp_slides)} slides, {len(comp_tiles)} tiles") @@ -86,30 +97,38 @@ def run_paired_mode(args): comp_iter = iterate_tiles(comp_slides, comp_tiles) for (_, orig_tile, image_id), (_, comp_tile, _) in tqdm( - zip(orig_iter, comp_iter), total=len(orig_tiles) + zip(orig_iter, comp_iter, strict=False), total=len(orig_tiles) ): analyzer.compare(comp_tile, image_id=image_id, reference=orig_tile) return analyzer, len(analyzer.results) -def main(): +def main() -> None: parser = argparse.ArgumentParser( description="Dataset stain analysis", formatter_class=argparse.RawDescriptionHelpFormatter, ) - parser.add_argument("--output", default="./analysis_output", - help="Output directory (default: ./analysis_output)") - parser.add_argument("--max-tiles", type=int, default=None, - help="Limit number of tiles to analyze") + parser.add_argument( + "--output", + default="./analysis_output", + help="Output directory (default: ./analysis_output)", + ) + parser.add_argument( + "--max-tiles", type=int, default=None, help="Limit number of tiles to analyze" + ) # Mode 1: reference image parser.add_argument("--reference", help="Path to reference image") parser.add_argument("--uri", nargs="+", help="MLflow dataset URI(s) to analyze") # Mode 2: two datasets - parser.add_argument("--original", nargs="+", help="MLflow URI(s) for original dataset") - parser.add_argument("--compared", nargs="+", help="MLflow URI(s) for compared dataset") + parser.add_argument( + "--original", nargs="+", help="MLflow URI(s) for original dataset" + ) + parser.add_argument( + "--compared", nargs="+", help="MLflow URI(s) for compared dataset" + ) args = parser.parse_args() diff --git a/demo.py b/demo.py index 69ca771..6b362f3 100644 --- a/demo.py +++ b/demo.py @@ -18,7 +18,7 @@ class StainNormalizerDemo: NORMALIZE_TRANSFORM = A.Normalize(mean=MEAN, std=STD, max_pixel_value=1) TO_TENSOR = ToTensorV2() - def __init__(self, use_cpu=True): + def __init__(self, use_cpu: bool = True) -> None: self.device = torch.device( "cpu" if use_cpu else ("cuda" if torch.cuda.is_available() else "cpu") ) @@ -51,11 +51,11 @@ def tensor_to_image(self, tensor: torch.Tensor) -> Image.Image: tensor = (tensor * 255).byte() return Image.fromarray(tensor.permute(1, 2, 0).cpu().numpy()) - def save_image(self, tensor: torch.Tensor, path: Path): + def save_image(self, tensor: torch.Tensor, path: Path) -> None: img = self.tensor_to_image(tensor.squeeze(0)) img.save(path) - def predict_image(self, input_path: Path, output_path: Path): + def predict_image(self, input_path: Path, output_path: Path) -> None: with torch.no_grad(): input_tensor = self.load_image(input_path) output = self.model(input_tensor) @@ -63,7 +63,7 @@ def predict_image(self, input_path: Path, output_path: Path): print(f"Saved normalized image to {output_path}") -def main(): +def main() -> None: parser = argparse.ArgumentParser(description="Stain Normalization Demo") parser.add_argument( "--input", diff --git a/preprocessing/mask_generator.py b/preprocessing/mask_generator.py index 234f846..335cd4e 100644 --- a/preprocessing/mask_generator.py +++ b/preprocessing/mask_generator.py @@ -34,11 +34,11 @@ def process_slide(slide_path: Path) -> None: write_big_tiff(mask, path=mask_path, mpp_x=xres, mpp_y=yres) - def main() -> None: slides = list(Path(SLIDES_PATH).rglob("*.mrxs")) # process_slide(slides[0]) process_items(slides, process_item=process_slide) + if __name__ == "__main__": main() diff --git a/preprocessing/tiler.py b/preprocessing/tiler.py index f1b1561..9c8f390 100644 --- a/preprocessing/tiler.py +++ b/preprocessing/tiler.py @@ -59,12 +59,9 @@ def handler(slide_path: Path) -> TiledSlideMetadata: return slide, tiles - - def main() -> None: slides = list(Path(SLIDES_PATH).rglob("*.mrxs")) - slides, test_slides = train_test_split(slides, test_size=0.2) train_slides, val_slides = train_test_split(slides, test_size=0.1) @@ -101,7 +98,7 @@ def main() -> None: if __name__ == "__main__": - #main() + # main() slides = [("/home/jovyan/staining/demo_data/P-2016_0077-08-1_hed_h0.6_e1.5.tiff")] train_slides_df, train_tiles_df = tiling(slides=slides, handler=handler) @@ -112,4 +109,3 @@ def main() -> None: tiles=train_tiles_df, dataset_name="P-2016_0077-08-1_hed", ) - diff --git a/pyproject.toml b/pyproject.toml index 22faeae..4558ddc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "Stain Normalization" version = "0.1.0" authors = [{name = "Adam Lopatka"}] -requires-python = "3.12.5" +requires-python = "==3.12.5" readme = "README.md" license = { file = "LICENSE" } dependencies = [ @@ -31,10 +31,10 @@ train = "python -m stain_normalization mode=fit" validate = "python -m stain_normalization mode=validate" test = "python -m stain_normalization mode=test" predict = "python -m stain_normalization mode=predict" -l = { composite = ["lint", "format", "mypy"] } +l = { composite = ["lint", "format", "mypy_check"] } lint = "ruff check --fix" format = "ruff format" -mypy = "mypy ." +mypy_check = "mypy ." # post_install = { composite = [ # "pre-commit autoupdate", # "pre-commit install", diff --git a/stain_normalization/analysis/__init__.py b/stain_normalization/analysis/__init__.py index 33f4d99..16504d5 100644 --- a/stain_normalization/analysis/__init__.py +++ b/stain_normalization/analysis/__init__.py @@ -1,20 +1,6 @@ -from .analyzer import StainAnalyzer -from .report import ( - REPORT_METRICS, - aggregate_average, - aggregate_rms, - aggregate_max, - aggregate_penalized_mean, - aggregate_power_mean, -) +from stain_normalization.analysis.analyzer import StainAnalyzer __all__ = [ "StainAnalyzer", - "REPORT_METRICS", - "aggregate_average", - "aggregate_rms", - "aggregate_max", - "aggregate_penalized_mean", - "aggregate_power_mean", ] diff --git a/stain_normalization/analysis/analyzer.py b/stain_normalization/analysis/analyzer.py index 9b0b6c5..7511cfe 100644 --- a/stain_normalization/analysis/analyzer.py +++ b/stain_normalization/analysis/analyzer.py @@ -3,16 +3,21 @@ Compares images using selected metrics, accumulates results, and provides statistics and saving. """ + from pathlib import Path -from typing import Union, Optional +from typing import Any, ClassVar import numpy as np import pandas as pd from rationai.staining import estimate_stain_vectors from skimage.metrics import structural_similarity -from ..metrics.vector_metrics import compare_vectors -from ..metrics.image_metrics import compute_nmi, compute_pcc, compute_lab_brightness_psnr +from stain_normalization.metrics.image_metrics import ( + compute_lab_brightness_psnr, + compute_nmi, + compute_pcc, +) +from stain_normalization.metrics.vector_metrics import compare_vectors class StainAnalyzer: @@ -24,20 +29,28 @@ class StainAnalyzer: metrics: Which metrics to compute. None = all. """ - AVAILABLE_METRICS = ['vectors', 'ssim', 'pcc', 'nmi', 'lab_psnr'] - PAIRED_ONLY = {'ssim', 'pcc', 'lab_psnr'} + AVAILABLE_METRICS: ClassVar[list[str]] = [ + "vectors", + "ssim", + "pcc", + "nmi", + "lab_psnr", + ] + PAIRED_ONLY: ClassVar[set[str]] = {"ssim", "pcc", "lab_psnr"} def __init__( self, - reference: Optional[np.ndarray] = None, - metrics: Optional[list[str]] = None, - ): + reference: np.ndarray[Any, Any] | None = None, + metrics: list[str] | None = None, + ) -> None: self.metrics = metrics or self.AVAILABLE_METRICS - self._results: list[dict] = [] + self._results: list[dict[str, Any]] = [] for m in self.metrics: if m not in self.AVAILABLE_METRICS: - raise ValueError(f"Unknown metric '{m}'. Available: {self.AVAILABLE_METRICS}") + raise ValueError( + f"Unknown metric '{m}'. Available: {self.AVAILABLE_METRICS}" + ) # if we have reference image, precompute stain vectors and NMI self._ref_img = reference @@ -45,9 +58,9 @@ def __init__( self._ref_nmi = None if reference is not None: - if 'vectors' in self.metrics: + if "vectors" in self.metrics: self._ref_vectors = estimate_stain_vectors(reference) - if 'nmi' in self.metrics: + if "nmi" in self.metrics: self._ref_nmi = compute_nmi(reference) @property @@ -61,10 +74,10 @@ def clear(self) -> None: def compare( self, - image: np.ndarray, - image_id: Optional[str] = None, - reference: Optional[np.ndarray] = None, - ) -> dict: + image: np.ndarray[Any, Any], + image_id: str | None = None, + reference: np.ndarray[Any, Any] | None = None, + ) -> dict[str, Any]: """Compare an image against the reference and store the result. Args: @@ -75,53 +88,62 @@ def compare( Returns: Dict with metric results. """ - ref_img = reference if reference is not None else self._ref_img if ref_img is None: - raise ValueError("No reference image. Pass one to __init__ or to compare().") + raise ValueError( + "No reference image. Pass one to __init__ or to compare()." + ) is_paired = reference is not None if reference is not None: ref_vectors = ( - estimate_stain_vectors(ref_img) - if 'vectors' in self.metrics else None + estimate_stain_vectors(ref_img) if "vectors" in self.metrics else None ) - ref_nmi = compute_nmi(ref_img) if 'nmi' in self.metrics else None + ref_nmi = compute_nmi(ref_img) if "nmi" in self.metrics else None else: ref_vectors = self._ref_vectors ref_nmi = self._ref_nmi - result = {'id': image_id} if image_id is not None else {} + result: dict[str, Any] = {"id": image_id} if image_id is not None else {} - if 'vectors' in self.metrics: + if "vectors" in self.metrics: + assert ( + ref_vectors is not None + ) # refrence image deosnt have stain vectors (too much background) img_vectors = estimate_stain_vectors(image) vec_result = compare_vectors(ref_vectors, img_vectors) result.update(vec_result) img_vectors_paired = ( - img_vectors[[1, 0]] if vec_result['was_swapped'] else img_vectors + img_vectors[[1, 0]] if vec_result["was_swapped"] else img_vectors ) for j, val in enumerate(ref_vectors.flatten()): - result[f'ref_vec_{j}'] = float(val) + result[f"ref_vec_{j}"] = float(val) for j, val in enumerate(img_vectors_paired.flatten()): - result[f'img_vec_{j}'] = float(val) - - if 'ssim' in self.metrics and is_paired: - result['ssim'] = float(structural_similarity( - ref_img, image, channel_axis=-1, data_range=255, - )) + result[f"img_vec_{j}"] = float(val) + + if "ssim" in self.metrics and is_paired: + result["ssim"] = float( + structural_similarity( + ref_img, + image, + channel_axis=-1, + data_range=255, + ) + ) - if 'pcc' in self.metrics and is_paired: - result['pcc'] = compute_pcc(ref_img, image) + if "pcc" in self.metrics and is_paired: + result["pcc"] = compute_pcc(ref_img, image) - if 'nmi' in self.metrics: + if "nmi" in self.metrics: + assert ref_nmi is not None img_nmi = compute_nmi(image) - result['ref_nmi'] = ref_nmi - result['nmi'] = img_nmi - result['nmi_diff'] = img_nmi - ref_nmi + result["ref_nmi"] = ref_nmi + result["nmi"] = img_nmi + result["nmi_diff"] = img_nmi - ref_nmi - if 'lab_psnr' in self.metrics and is_paired: - result['lab_brightness_psnr'] = compute_lab_brightness_psnr(ref_img, image) + if "lab_psnr" in self.metrics and is_paired: + result["lab_brightness_psnr"] = compute_lab_brightness_psnr(ref_img, image) self._results.append(result) return result @@ -142,7 +164,7 @@ def get_baseline_ranges( self, percentile_low: float = 5, percentile_high: float = 95, - ) -> dict: + ) -> dict[str, tuple[float, float]]: """Get acceptable value ranges for each metric. Args: @@ -162,7 +184,7 @@ def get_baseline_ranges( for col in numeric_cols } - def save_csv(self, output_dir: Union[str, Path]) -> Path: + def save_csv(self, output_dir: str | Path) -> Path: """Save accumulated results and statistics as CSV files. Args: @@ -178,12 +200,10 @@ def save_csv(self, output_dir: Union[str, Path]) -> Path: self.get_statistics().to_csv(output_dir / "statistics.csv") ranges = self.get_baseline_ranges() - with open(output_dir / "baseline_ranges.txt", 'w') as f: + with open(output_dir / "baseline_ranges.txt", "w") as f: f.write("Baseline Metric Ranges (5th-95th percentile)\n") f.write("=" * 55 + "\n\n") for metric, (low, high) in sorted(ranges.items()): f.write(f"{metric:30s}: [{low:8.4f}, {high:8.4f}]\n") return output_dir - - diff --git a/stain_normalization/callbacks/_base.py b/stain_normalization/callbacks/_base.py index 4dba6cc..240b516 100644 --- a/stain_normalization/callbacks/_base.py +++ b/stain_normalization/callbacks/_base.py @@ -1,3 +1,5 @@ +from typing import Any + import numpy as np import torch from lightning import Callback @@ -17,9 +19,14 @@ def denormalize(self, tensor: torch.Tensor) -> torch.Tensor: device = tensor.device return (tensor * self.std.to(device)) + self.mean.to(device) - def tensor_to_image(self, tensor: torch.Tensor) -> np.ndarray: + def tensor_to_image(self, tensor: torch.Tensor) -> np.ndarray[Any, Any]: """Convert model output tensor to uint8 HWC numpy array.""" return ( - self.denormalize(tensor).clamp(0, 1).mul(255).byte() - .permute(1, 2, 0).cpu().numpy() + self.denormalize(tensor) + .clamp(0, 1) + .mul(255) + .byte() + .permute(1, 2, 0) + .cpu() + .numpy() ) diff --git a/stain_normalization/callbacks/analysis_export.py b/stain_normalization/callbacks/analysis_export.py index ad0f603..f9ae5ca 100644 --- a/stain_normalization/callbacks/analysis_export.py +++ b/stain_normalization/callbacks/analysis_export.py @@ -1,18 +1,21 @@ from pathlib import Path +from typing import Any import mlflow import torch from lightning import LightningModule, Trainer from omegaconf import DictConfig -from ..analysis.analyzer import StainAnalyzer -from ._base import NormalizationCallback +from stain_normalization.analysis.analyzer import StainAnalyzer +from stain_normalization.callbacks._base import NormalizationCallback class AnalysisExport(NormalizationCallback): """Exports analysis metrics during testing.""" - def __init__(self, output_dir: str | Path, normalization_config: DictConfig) -> None: + def __init__( + self, output_dir: str | Path, normalization_config: DictConfig + ) -> None: super().__init__(normalization_config) self.output_dir = Path(output_dir) self.output_dir.mkdir(parents=True, exist_ok=True) @@ -20,12 +23,12 @@ def __init__(self, output_dir: str | Path, normalization_config: DictConfig) -> self.mod_analyzer = StainAnalyzer() self.pred_analyzer = StainAnalyzer() - def on_test_batch_end( + def on_test_batch_end( # type: ignore[override] # narrowed Lightning STEP_OUTPUT self, trainer: Trainer, pl_module: LightningModule, outputs: list[torch.Tensor], - batch: tuple[torch.Tensor, list[dict]], + batch: tuple[torch.Tensor, list[dict[str, Any]]], batch_idx: int, dataloader_idx: int = 0, ) -> None: diff --git a/stain_normalization/callbacks/tiles_export.py b/stain_normalization/callbacks/tiles_export.py index bfd52f9..48bf495 100644 --- a/stain_normalization/callbacks/tiles_export.py +++ b/stain_normalization/callbacks/tiles_export.py @@ -1,11 +1,12 @@ from pathlib import Path +from typing import Any import torch from lightning import LightningModule, Trainer from omegaconf import DictConfig from PIL import Image -from ._base import NormalizationCallback +from stain_normalization.callbacks._base import NormalizationCallback class TilesExport(NormalizationCallback): @@ -16,15 +17,15 @@ def __init__( self.output_dir = Path(output_dir) self.output_dir.mkdir(parents=True, exist_ok=True) - def tensor_to_image(self, tensor: torch.Tensor) -> Image.Image: + def tensor_to_image(self, tensor: torch.Tensor) -> Image.Image: # type: ignore[override] # intentional: PIL Image is not subtype of ndarray return Image.fromarray(super().tensor_to_image(tensor)) - def on_test_batch_end( + def on_test_batch_end( # type: ignore[override] # narrowed Lightning STEP_OUTPUT self, trainer: Trainer, pl_module: LightningModule, outputs: list[torch.Tensor], - batch: tuple[torch.Tensor, list], + batch: tuple[torch.Tensor, list[dict[str, Any]]], batch_idx: int, dataloader_idx: int = 0, ) -> None: @@ -36,13 +37,9 @@ def on_test_batch_end( slide_dir = self.output_dir / slide_name slide_dir.mkdir(parents=True, exist_ok=True) - self.tensor_to_image(outputs[b]).save( - slide_dir / f"{xy}_predicted.png" - ) + self.tensor_to_image(outputs[b]).save(slide_dir / f"{xy}_predicted.png") - original_image = Image.fromarray( - data[b]["original_image"].astype("uint8") - ) + original_image = Image.fromarray(data[b]["original_image"].astype("uint8")) original_image.save(slide_dir / f"{xy}_original.png") modified_image = Image.fromarray( @@ -55,7 +52,7 @@ def on_predict_batch_end( trainer: Trainer, pl_module: LightningModule, outputs: list[torch.Tensor], - batch: tuple[torch.Tensor, list], + batch: tuple[torch.Tensor, list[dict[str, Any]]], batch_idx: int, dataloader_idx: int = 0, ) -> None: diff --git a/stain_normalization/callbacks/wsi_assembler.py b/stain_normalization/callbacks/wsi_assembler.py index 31a0745..fa1f794 100644 --- a/stain_normalization/callbacks/wsi_assembler.py +++ b/stain_normalization/callbacks/wsi_assembler.py @@ -16,6 +16,7 @@ import traceback from dataclasses import dataclass from pathlib import Path +from typing import Any import numpy as np import torch @@ -23,7 +24,7 @@ from omegaconf import DictConfig from PIL.ImageCms import createProfile -from ._base import NormalizationCallback +from stain_normalization.callbacks._base import NormalizationCallback def _srgb_icc_bytes() -> bytes: @@ -31,9 +32,10 @@ def _srgb_icc_bytes() -> bytes: raw = createProfile("sRGB") # Pillow < 10: .tobuffer(), Pillow >= 10: .tobytes() try: - return raw.tobuffer() + return raw.tobuffer() # type: ignore[attr-defined] # deprecated in Pillow >=10 except AttributeError: from PIL.ImageCms import ImageCmsProfile + return ImageCmsProfile(raw).tobytes() @@ -52,9 +54,9 @@ class _SlideMeta: @dataclass class _SlideBuffers: meta: _SlideMeta - temp_dir: tempfile.TemporaryDirectory - result_buffer: np.memmap - count_buffer: np.memmap + temp_dir: tempfile.TemporaryDirectory[str] + result_buffer: np.memmap[Any, Any] + count_buffer: np.memmap[Any, Any] class WSIAssembler(NormalizationCallback): @@ -75,7 +77,7 @@ def __init__( def on_predict_start(self, trainer: Trainer, pl_module: LightningModule) -> None: self.output_dir.mkdir(parents=True, exist_ok=True) - slides_df = trainer.datamodule.predict.slides + slides_df = trainer.datamodule.predict.slides # type: ignore[attr-defined] # Lightning stub gap # Cache metadata only — buffers are opened lazily per slide for _, row in slides_df.iterrows(): @@ -124,6 +126,7 @@ def _close_slide(self) -> None: """Save and free the currently active slide.""" if self._active is None: return + assert self._active_name is not None try: self._save_slide(self._active_name, self._active) except Exception: @@ -140,7 +143,7 @@ def on_predict_batch_end( trainer: Trainer, pl_module: LightningModule, outputs: list[torch.Tensor], - batch: tuple[torch.Tensor, list[dict]], + batch: tuple[torch.Tensor, list[dict[str, Any]]], batch_idx: int, dataloader_idx: int = 0, ) -> None: @@ -162,16 +165,17 @@ def on_predict_batch_end( x, y = (int(v) for v in metadata["xy"].split("_")) self._place_tile(tile, x, y) - def _place_tile(self, tile: np.ndarray, x: int, y: int) -> None: + def _place_tile(self, tile: np.ndarray[Any, Any], x: int, y: int) -> None: """Place a predicted tile into the active slide buffer with overlap averaging.""" + assert self._active is not None sb = self._active ex, ey = sb.meta.extent_x, sb.meta.extent_y h, w = min(tile.shape[0], ey - y), min(tile.shape[1], ex - x) tile = tile[:h, :w] - region = sb.result_buffer[y:y + h, x:x + w] - count = sb.count_buffer[y:y + h, x:x + w] + region = sb.result_buffer[y : y + h, x : x + w] + count = sb.count_buffer[y : y + h, x : x + w] # Running average: avg = (old * n + new) / (n + 1) overlap = count > 0 @@ -182,11 +186,13 @@ def _place_tile(self, tile: np.ndarray, x: int, y: int) -> None: (region.astype(np.float32) * n + tile) / (n + 1), tile, ) - sb.result_buffer[y:y + h, x:x + w] = np.clip(blended, 0, 255).astype(np.uint8) + sb.result_buffer[y : y + h, x : x + w] = np.clip(blended, 0, 255).astype( + np.uint8 + ) else: - sb.result_buffer[y:y + h, x:x + w] = tile + sb.result_buffer[y : y + h, x : x + w] = tile - sb.count_buffer[y:y + h, x:x + w] = count + 1 + sb.count_buffer[y : y + h, x : x + w] = count + 1 def on_predict_end(self, trainer: Trainer, pl_module: LightningModule) -> None: self._close_slide() @@ -203,10 +209,14 @@ def _save_slide(self, slide_name: str, sb: _SlideBuffers) -> None: result_path = Path(sb.temp_dir.name) / "result.raw" count_path = Path(sb.temp_dir.name) / "count.raw" - result_img = pyvips.Image.rawload(str(result_path), meta.extent_x, meta.extent_y, 3) + result_img = pyvips.Image.rawload( + str(result_path), meta.extent_x, meta.extent_y, 3 + ) result_img = result_img.copy(interpretation=pyvips.Interpretation.SRGB) - count_img = pyvips.Image.rawload(str(count_path), meta.extent_x, meta.extent_y, 1) + count_img = pyvips.Image.rawload( + str(count_path), meta.extent_x, meta.extent_y, 1 + ) mask = count_img > 0 # add white background for untouched areas (count=0) white = (pyvips.Image.black(meta.extent_x, meta.extent_y, bands=3) + 255).cast( diff --git a/stain_normalization/data/datasets/predict_dataset.py b/stain_normalization/data/datasets/predict_dataset.py index 9fc6650..fff842a 100644 --- a/stain_normalization/data/datasets/predict_dataset.py +++ b/stain_normalization/data/datasets/predict_dataset.py @@ -64,7 +64,7 @@ def __getitem__(self, idx: int) -> PredictSample: input_image = self.to_tensor(image=input_image)["image"] - return input_image, { + return input_image, { # type: ignore[return-value] # untyped import "slide_name": slide_name, "xy": f"{x}_{y}", } diff --git a/stain_normalization/data/datasets/train_dataset.py b/stain_normalization/data/datasets/train_dataset.py index 1d46afe..8716e6f 100644 --- a/stain_normalization/data/datasets/train_dataset.py +++ b/stain_normalization/data/datasets/train_dataset.py @@ -56,11 +56,11 @@ def __len__(self) -> int: return len(self.slide_tiles) def __getitem__(self, idx: int) -> Sample: - original_image = self.slide_tiles[idx] + original_image_255 = self.slide_tiles[idx] # Create "wrong" image to use as input. Outputs image in float 0-1 - modified_image = self.modify(image=original_image)["image"] - original_image = original_image / 255.0 + modified_image = self.modify(image=original_image_255)["image"] + original_image = original_image_255 / 255.0 if self.normalize: original_image = self.normalize(image=original_image)["image"] @@ -69,4 +69,4 @@ def __getitem__(self, idx: int) -> Sample: original_image = self.to_tensor(image=original_image)["image"] modified_image = self.to_tensor(image=modified_image)["image"] - return modified_image, original_image + return modified_image, original_image # type: ignore[return-value] # untyped import diff --git a/stain_normalization/data/modification/combiend_modification.py b/stain_normalization/data/modification/combiend_modification.py index 6eebbc3..c353d18 100644 --- a/stain_normalization/data/modification/combiend_modification.py +++ b/stain_normalization/data/modification/combiend_modification.py @@ -1,3 +1,5 @@ +from typing import Any + import numpy as np from albumentations import ImageOnlyTransform from numpy.typing import NDArray @@ -5,7 +7,7 @@ from skimage.color import combine_stains, hed_from_rgb, rgb_from_hed, separate_stains -class CombinedModifications(ImageOnlyTransform): +class CombinedModifications(ImageOnlyTransform): # type: ignore[misc] # untyped import """Apply combined modifications to the H&E channels in HED color space. Attributes: @@ -26,7 +28,7 @@ def __init__( self.intensity_range = intensity_range self.brightness_range = brightness_range - def apply(self, img: NDArray, **params) -> NDArray: + def apply(self, img: NDArray[Any], **params: Any) -> NDArray[Any]: """Apply intensity and brightness adjustments to H and E channels. Args: diff --git a/stain_normalization/data/modification/exposure_adjustment.py b/stain_normalization/data/modification/exposure_adjustment.py index e1ff4c2..bb35169 100644 --- a/stain_normalization/data/modification/exposure_adjustment.py +++ b/stain_normalization/data/modification/exposure_adjustment.py @@ -1,9 +1,11 @@ +from typing import Any + import numpy as np from albumentations import ImageOnlyTransform from numpy.typing import NDArray -class ExposureAdjustment(ImageOnlyTransform): +class ExposureAdjustment(ImageOnlyTransform): # type: ignore[misc] # untyped import """Adjust the exposure of an image by scaling its brightness. Attributes: @@ -23,7 +25,7 @@ def __init__( super().__init__(always_apply, p) self.brightness_range = brightness_range - def apply(self, img: NDArray, **params) -> NDArray: + def apply(self, img: NDArray[Any], **params: Any) -> NDArray[Any]: """Apply brightness scaling to the image. Args: diff --git a/stain_normalization/data/modification/hed_factor.py b/stain_normalization/data/modification/hed_factor.py index a5031c9..d93bbd6 100644 --- a/stain_normalization/data/modification/hed_factor.py +++ b/stain_normalization/data/modification/hed_factor.py @@ -1,10 +1,12 @@ +from typing import Any + import numpy as np from albumentations import ImageOnlyTransform from numpy.typing import NDArray from skimage.color import combine_stains, hed_from_rgb, rgb_from_hed, separate_stains -class HEDFactor(ImageOnlyTransform): +class HEDFactor(ImageOnlyTransform): # type: ignore[misc] # untyped import """Adjust the intensity of Hematoxylin and Eosin stains in HED color space. Attributes: @@ -25,7 +27,7 @@ def __init__( self.h_range = h_range self.e_range = e_range - def apply(self, img: NDArray, **params) -> NDArray: + def apply(self, img: NDArray[Any], **params: Any) -> NDArray[Any]: """Apply the modification to the image. Args: diff --git a/stain_normalization/data/modification/hvs_modification.py b/stain_normalization/data/modification/hvs_modification.py index 53e4697..ca96b9e 100644 --- a/stain_normalization/data/modification/hvs_modification.py +++ b/stain_normalization/data/modification/hvs_modification.py @@ -1,10 +1,12 @@ +from typing import Any + import numpy as np from albumentations import ImageOnlyTransform from numpy.typing import NDArray from skimage.color import hsv2rgb, rgb2hsv -class HVSModification(ImageOnlyTransform): +class HVSModification(ImageOnlyTransform): # type: ignore[misc] # untyped import """Randomly modify hue, saturation, and value (brightness) of an image in HSV color space. Attributes: @@ -30,7 +32,7 @@ def __init__( self.saturation_range = saturation_range self.value_range = value_range - def apply(self, img: NDArray, **params) -> NDArray: + def apply(self, img: NDArray[Any], **params: Any) -> NDArray[Any]: """Apply the modifications to an image. Args: diff --git a/stain_normalization/metrics/__init__.py b/stain_normalization/metrics/__init__.py index 0f4f8f3..8153500 100644 --- a/stain_normalization/metrics/__init__.py +++ b/stain_normalization/metrics/__init__.py @@ -1,9 +1,9 @@ -from .image_metrics import ( +from stain_normalization.metrics.image_metrics import ( compute_lab_brightness_psnr, compute_nmi, compute_pcc, ) -from .vector_metrics import ( +from stain_normalization.metrics.vector_metrics import ( compare_vectors, ) diff --git a/stain_normalization/metrics/image_metrics.py b/stain_normalization/metrics/image_metrics.py index 28f806d..daad52e 100644 --- a/stain_normalization/metrics/image_metrics.py +++ b/stain_normalization/metrics/image_metrics.py @@ -1,9 +1,11 @@ +from typing import Any + import numpy as np from skimage.color import rgb2lab from skimage.metrics import peak_signal_noise_ratio -def compute_nmi(img: np.ndarray) -> float: +def compute_nmi(img: np.ndarray[Any, Any]) -> float: """Normalized Median Intensity — measures relative brightness of an image. Args: @@ -22,7 +24,7 @@ def compute_nmi(img: np.ndarray) -> float: return float(median_val / p95_val) -def compute_pcc(img1: np.ndarray, img2: np.ndarray) -> float: +def compute_pcc(img1: np.ndarray[Any, Any], img2: np.ndarray[Any, Any]) -> float: """Pearson Correlation Coefficient between two images. Args: @@ -41,7 +43,9 @@ def compute_pcc(img1: np.ndarray, img2: np.ndarray) -> float: return float(np.corrcoef(img1_flat, img2_flat)[0, 1]) -def compute_lab_brightness_psnr(img1: np.ndarray, img2: np.ndarray) -> float: +def compute_lab_brightness_psnr( + img1: np.ndarray[Any, Any], img2: np.ndarray[Any, Any] +) -> float: """PSNR on the L* channel in Lab color space. Args: @@ -53,4 +57,6 @@ def compute_lab_brightness_psnr(img1: np.ndarray, img2: np.ndarray) -> float: """ lab1 = rgb2lab(img1.astype(np.float32) / 255.0) lab2 = rgb2lab(img2.astype(np.float32) / 255.0) - return float(peak_signal_noise_ratio(lab1[:, :, 0], lab2[:, :, 0], data_range=100.0)) + return float( + peak_signal_noise_ratio(lab1[:, :, 0], lab2[:, :, 0], data_range=100.0) + ) diff --git a/stain_normalization/metrics/vector_metrics.py b/stain_normalization/metrics/vector_metrics.py index 1348f72..1e791dd 100644 --- a/stain_normalization/metrics/vector_metrics.py +++ b/stain_normalization/metrics/vector_metrics.py @@ -1,8 +1,10 @@ +from typing import Any + import numpy as np from skimage.color import rgb2lab -def _od_to_lab(od_vector: np.ndarray) -> np.ndarray: +def _od_to_lab(od_vector: np.ndarray[Any, Any]) -> np.ndarray[Any, Any]: """Convert optical density vector to Lab color. Args: @@ -17,7 +19,7 @@ def _od_to_lab(od_vector: np.ndarray) -> np.ndarray: return rgb2lab(rgb.reshape(1, 1, 3)).flatten() -def delta_e76(lab1: np.ndarray, lab2: np.ndarray) -> float: +def delta_e76(lab1: np.ndarray[Any, Any], lab2: np.ndarray[Any, Any]) -> float: """CIE76 Delta E with dL=0 (chromaticity only). CIE76 Delta E is sqrt(dL^2 + da^2 + db^2). We set dL=0 because we compare @@ -36,9 +38,9 @@ def delta_e76(lab1: np.ndarray, lab2: np.ndarray) -> float: def compare_vectors( - vecs1: np.ndarray, - vecs2: np.ndarray, -) -> dict: + vecs1: np.ndarray[Any, Any], + vecs2: np.ndarray[Any, Any], +) -> dict[str, float | bool]: """Compare two sets of stain vectors in Lab chromaticity space. Args: @@ -51,11 +53,11 @@ def compare_vectors( """ if np.any(np.isnan(vecs1)) or np.any(np.isnan(vecs2)): return { - 'd_hematoxylin': float('nan'), 'd_eosin': float('nan'), - 'was_swapped': False, + "d_hematoxylin": float("nan"), + "d_eosin": float("nan"), + "was_swapped": False, } - sim_straight = np.dot(vecs1[0], vecs2[0]) + np.dot(vecs1[1], vecs2[1]) sim_swapped = np.dot(vecs1[0], vecs2[1]) + np.dot(vecs1[1], vecs2[0]) was_swapped = sim_swapped > sim_straight @@ -67,9 +69,7 @@ def compare_vectors( lab2_b = _od_to_lab(vecs2_paired[1]) return { - 'd_hematoxylin': delta_e76(lab1_a, lab2_a), - 'd_eosin': delta_e76(lab1_b, lab2_b), - 'was_swapped': was_swapped, + "d_hematoxylin": delta_e76(lab1_a, lab2_a), + "d_eosin": delta_e76(lab1_b, lab2_b), + "was_swapped": was_swapped, } - - diff --git a/stain_normalization/modeling/l1ssim_loss.py b/stain_normalization/modeling/l1ssim_loss.py index be13691..32b920b 100644 --- a/stain_normalization/modeling/l1ssim_loss.py +++ b/stain_normalization/modeling/l1ssim_loss.py @@ -1,46 +1,56 @@ """Original SSIM code based on pytorch-ssim by Evan Su (MIT License). https://github.com/Po-Hsun-Su/pytorch-ssim . -""" -""" -The SSIM is based on implementation from gaussian-splatting and slightly simplified (pre-computed windows and removal of unused arguments) +The SSIM is based on implementation from gaussian-splatting and slightly simplified +(pre-computed windows and removal of unused arguments). https://github.com/graphdeco-inria/gaussian-splatting/blob/472689c0dc70417448fb451bf529ae532d32c095/utils/loss_utils.py """ +from math import exp import torch import torch.nn as nn import torch.nn.functional as F -from math import exp + class L1SSIMLoss(nn.Module): - def __init__(self, lambda_dssim: float = 0.6, lambda_l1: float = 0.2, lambda_lum: float = 0.2, lambda_gdl: float = 0.1): + def __init__( + self, + lambda_dssim: float = 0.6, + lambda_l1: float = 0.2, + lambda_lum: float = 0.2, + lambda_gdl: float = 0.1, + ): super().__init__() - self.lambda_dssim = lambda_dssim - self.lambda_l1 = lambda_l1 - self.lambda_lum = lambda_lum - self.lambda_gdl = lambda_gdl + self.lambda_dssim = lambda_dssim + self.lambda_l1 = lambda_l1 + self.lambda_lum = lambda_lum + self.lambda_gdl = lambda_gdl # precompute SSIM windows to avoid repetation self.window_size = 11 self.channel = 3 self._1d_window = gaussian(self.window_size, 1.5).unsqueeze(1) - self._2d_window = self._1d_window.mm(self._1d_window.t()).float().unsqueeze(0).unsqueeze(0) - self.window = self._2d_window.expand(self.channel, 1, self.window_size, self.window_size).contiguous() + self._2d_window = ( + self._1d_window.mm(self._1d_window.t()).float().unsqueeze(0).unsqueeze(0) + ) + self.window = self._2d_window.expand( + self.channel, 1, self.window_size, self.window_size + ).contiguous() def forward(self, image: torch.Tensor, target_image: torch.Tensor) -> torch.Tensor: if self.window.device != image.device: self.window = self.window.to(image.device) # L1 color loss l1_loss = F.l1_loss(image, target_image, reduction="mean") - + # SSIM structural loss ssim_loss = 1.0 - self._ssim(image, target_image, self.window) - + # Gradient loss for edges gdl_loss = gradient_loss(image, target_image) - + # Luminance / brightness loss brig_loss = brightness_loss(image, target_image) @@ -51,34 +61,51 @@ def forward(self, image: torch.Tensor, target_image: torch.Tensor) -> torch.Tens + self.lambda_gdl * gdl_loss + self.lambda_lum * brig_loss ) - + return total_loss @torch.compile - def _ssim(self, img1, img2, window): + def _ssim( + self, img1: torch.Tensor, img2: torch.Tensor, window: torch.Tensor + ) -> torch.Tensor: # Modified _ssim that uses pre-computed window mu1 = F.conv2d(img1, window, padding=self.window_size // 2, groups=self.channel) mu2 = F.conv2d(img2, window, padding=self.window_size // 2, groups=self.channel) - + mu1_sq = mu1.pow(2) mu2_sq = mu2.pow(2) mu1_mu2 = mu1 * mu2 - - sigma1_sq = F.conv2d(img1 * img1, window, padding=self.window_size // 2, groups=self.channel) - mu1_sq - sigma2_sq = F.conv2d(img2 * img2, window, padding=self.window_size // 2, groups=self.channel) - mu2_sq - sigma12 = F.conv2d(img1 * img2, window, padding=self.window_size // 2, groups=self.channel) - mu1_mu2 - + + sigma1_sq = ( + F.conv2d( + img1 * img1, window, padding=self.window_size // 2, groups=self.channel + ) + - mu1_sq + ) + sigma2_sq = ( + F.conv2d( + img2 * img2, window, padding=self.window_size // 2, groups=self.channel + ) + - mu2_sq + ) + sigma12 = ( + F.conv2d( + img1 * img2, window, padding=self.window_size // 2, groups=self.channel + ) + - mu1_mu2 + ) + c1 = 0.01**2 c2 = 0.03**2 - + ssim_map = ((2 * mu1_mu2 + c1) * (2 * sigma12 + c2)) / ( (mu1_sq + mu2_sq + c1) * (sigma1_sq + sigma2_sq + c2) ) - + return ssim_map.mean() -def gaussian(window_size, sigma): +def gaussian(window_size: int, sigma: float) -> torch.Tensor: gauss = torch.Tensor( [ exp(-((x - window_size // 2) ** 2) / float(2 * sigma**2)) @@ -88,19 +115,22 @@ def gaussian(window_size, sigma): return gauss / gauss.sum() -def brightness_loss(pred, target, he_weights=None): +def brightness_loss( + pred: torch.Tensor, target: torch.Tensor, he_weights: list[float] | None = None +) -> torch.Tensor: device = pred.device if he_weights is None: he_weights = [0.33, 0.33, 0.33] weights = torch.tensor(he_weights, device=device).view(1, 3, 1, 1) - + pred_mean = (pred * weights).mean(dim=[2, 3], keepdim=True) - target_mean = (target * weights).mean(dim=[2, 3], keepdim=True) - + target_mean = (target * weights).mean(dim=[2, 3], keepdim=True) + return F.l1_loss(pred_mean, target_mean) - -def gradient_loss(image, target_image): - def gradient(x): + + +def gradient_loss(image: torch.Tensor, target_image: torch.Tensor) -> torch.Tensor: + def gradient(x: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]: dx = torch.abs(x[:, :, :-1, :] - x[:, :, 1:, :]) # Horizontal gradient dy = torch.abs(x[:, :, :, :-1] - x[:, :, :, 1:]) # Vertical gradient return dx, dy @@ -112,8 +142,3 @@ def gradient(x): loss_y = F.l1_loss(image_dy, target_dy, reduction="mean") return loss_x + loss_y - - - - - diff --git a/stain_normalization/modeling/unet.py b/stain_normalization/modeling/unet.py index 77f9b32..ebcb3b7 100644 --- a/stain_normalization/modeling/unet.py +++ b/stain_normalization/modeling/unet.py @@ -16,7 +16,9 @@ class DoubleConv(nn.Module): """(convolution => [BN] => ReLU) * 2.""" - def __init__(self, in_channels, out_channels, mid_channels=None): + def __init__( + self, in_channels: int, out_channels: int, mid_channels: int | None = None + ) -> None: super().__init__() if not mid_channels: mid_channels = out_channels @@ -29,32 +31,36 @@ def __init__(self, in_channels, out_channels, mid_channels=None): nn.ReLU(inplace=True), ) - def forward(self, x): + def forward(self, x: torch.Tensor) -> torch.Tensor: return self.double_conv(x) class Down(nn.Module): """Downscaling with maxpool then double conv.""" - def __init__(self, in_channels, out_channels): + def __init__(self, in_channels: int, out_channels: int) -> None: super().__init__() self.maxpool_conv = nn.Sequential( nn.MaxPool2d(2), DoubleConv(in_channels, out_channels) ) - def forward(self, x): + def forward(self, x: torch.Tensor) -> torch.Tensor: return self.maxpool_conv(x) class Up(nn.Module): """Upscaling then double conv.""" - def __init__(self, in_channels, out_channels, bilinear=True): + def __init__( + self, in_channels: int, out_channels: int, bilinear: bool = True + ) -> None: super().__init__() # if bilinear, use the normal convolutions to reduce the number of channels if bilinear: - self.up = nn.Upsample(scale_factor=2, mode="bilinear", align_corners=True) + self.up: nn.Upsample | nn.ConvTranspose2d = nn.Upsample( + scale_factor=2, mode="bilinear", align_corners=True + ) self.conv = DoubleConv(in_channels, out_channels, in_channels // 2) else: self.up = nn.ConvTranspose2d( @@ -62,7 +68,7 @@ def __init__(self, in_channels, out_channels, bilinear=True): ) self.conv = DoubleConv(in_channels, out_channels) - def forward(self, x1, x2): + def forward(self, x1: torch.Tensor, x2: torch.Tensor) -> torch.Tensor: x1 = self.up(x1) diffy = x2.size()[2] - x1.size()[2] diffx = x2.size()[3] - x1.size()[3] @@ -73,16 +79,18 @@ def forward(self, x1, x2): class OutConv(nn.Module): - def __init__(self, in_channels, out_channels): + def __init__(self, in_channels: int, out_channels: int) -> None: super().__init__() self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1) - def forward(self, x): + def forward(self, x: torch.Tensor) -> torch.Tensor: return self.conv(x) class UNet(nn.Module): - def __init__(self, in_channels=3, out_channels=3, bilinear=True): + def __init__( + self, in_channels: int = 3, out_channels: int = 3, bilinear: bool = True + ) -> None: super().__init__() self.in_conv = DoubleConv(in_channels, 64) self.down1 = Down(64, 128) @@ -97,7 +105,7 @@ def __init__(self, in_channels=3, out_channels=3, bilinear=True): self.up4 = Up(128, 64, bilinear) self.out_conv = OutConv(64, out_channels) - def forward(self, x): + def forward(self, x: torch.Tensor) -> torch.Tensor: x1 = self.in_conv(x) x2 = self.down1(x1) x3 = self.down2(x2) diff --git a/stain_normalization/stain_normalization_model.py b/stain_normalization/stain_normalization_model.py index 7530224..e383b29 100644 --- a/stain_normalization/stain_normalization_model.py +++ b/stain_normalization/stain_normalization_model.py @@ -17,10 +17,7 @@ def __init__(self) -> None: self.criterion = L1SSIMLoss() self.val_metrics = MetricCollection( - { - "ssim": StructuralSimilarityIndexMeasure(), - "l1": MeanAbsoluteError() - } + {"ssim": StructuralSimilarityIndexMeasure(), "l1": MeanAbsoluteError()} ) self.test_metrics = self.val_metrics.clone(prefix="test/") self.val_metrics.prefix = "validation/" diff --git a/stain_normalization/type_aliases.py b/stain_normalization/type_aliases.py index 535edda..e950436 100644 --- a/stain_normalization/type_aliases.py +++ b/stain_normalization/type_aliases.py @@ -3,11 +3,11 @@ from torch import Tensor -Sample: TypeAlias = tuple[Tensor, Tensor] -PredictSample: TypeAlias = tuple[Tensor, dict[str, Any]] +Sample: TypeAlias = tuple[Tensor, Tensor] +PredictSample: TypeAlias = tuple[Tensor, dict[str, Any]] # Batches - after collate Batch: TypeAlias = tuple[Tensor, Tensor] -PredictBatch: TypeAlias = tuple[Tensor, list[dict[str, Any]]] +PredictBatch: TypeAlias = tuple[Tensor, list[dict[str, Any]]] Outputs: TypeAlias = Tensor From b70caa7405be5b188f47fe3b62a30b1368f8006d Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni.cz> Date: Wed, 11 Mar 2026 06:54:06 +0100 Subject: [PATCH 81/85] fix: change project name --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4558ddc..db9f2ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "Stain Normalization" +name = "stain-normalization" version = "0.1.0" authors = [{name = "Adam Lopatka"}] requires-python = "==3.12.5" From 07a5406b0dfa701c14d9c6753ac8a64777382afd Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni.cz> Date: Wed, 11 Mar 2026 07:07:23 +0100 Subject: [PATCH 82/85] chore: uv.lock --- pyproject.toml | 5 + uv.lock | 2356 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2361 insertions(+) create mode 100644 uv.lock diff --git a/pyproject.toml b/pyproject.toml index db9f2ca..4acf72a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,3 +43,8 @@ mypy_check = "mypy ." [tool.pdm] distribution = false + +[tool.uv] +environments = ["sys_platform == 'linux'"] + +override-dependencies = ["mlflow>=2.15.1"] diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..b1e9be2 --- /dev/null +++ b/uv.lock @@ -0,0 +1,2356 @@ +version = 1 +revision = 3 +requires-python = "==3.12.5" +resolution-markers = [ + "sys_platform == 'linux'", +] +supported-markers = [ + "sys_platform == 'linux'", +] + +[manifest] +overrides = [{ name = "mlflow", specifier = ">=2.15.1" }] + +[[package]] +name = "affine" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/69/98/d2f0bb06385069e799fc7d2870d9e078cfa0fa396dc8a2b81227d0da08b9/affine-2.4.0.tar.gz", hash = "sha256:a24d818d6a836c131976d22f8c27b8d3ca32d0af64c1d8d29deb7bafa4da1eea", size = 17132, upload-time = "2023-01-19T23:44:30.696Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/f7/85273299ab57117850cc0a936c64151171fac4da49bc6fba0dad984a7c5f/affine-2.4.0-py3-none-any.whl", hash = "sha256:8a3df80e2b2378aef598a83c1392efd47967afec4242021a0b06b4c7cbc61a92", size = 15662, upload-time = "2023-01-19T23:44:28.833Z" }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs", marker = "sys_platform == 'linux'" }, + { name = "aiosignal", marker = "sys_platform == 'linux'" }, + { name = "attrs", marker = "sys_platform == 'linux'" }, + { name = "frozenlist", marker = "sys_platform == 'linux'" }, + { name = "multidict", marker = "sys_platform == 'linux'" }, + { name = "propcache", marker = "sys_platform == 'linux'" }, + { name = "yarl", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/a8/5a35dc56a06a2c90d4742cbf35294396907027f80eea696637945a106f25/aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29", size = 1737839, upload-time = "2026-01-03T17:30:19.422Z" }, + { url = "https://files.pythonhosted.org/packages/bf/62/4b9eeb331da56530bf2e198a297e5303e1c1ebdceeb00fe9b568a65c5a0c/aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3", size = 1703932, upload-time = "2026-01-03T17:30:21.756Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f6/af16887b5d419e6a367095994c0b1332d154f647e7dc2bd50e61876e8e3d/aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d", size = 1771906, upload-time = "2026-01-03T17:30:23.932Z" }, + { url = "https://files.pythonhosted.org/packages/ce/83/397c634b1bcc24292fa1e0c7822800f9f6569e32934bdeef09dae7992dfb/aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463", size = 1871020, upload-time = "2026-01-03T17:30:26Z" }, + { url = "https://files.pythonhosted.org/packages/86/f6/a62cbbf13f0ac80a70f71b1672feba90fdb21fd7abd8dbf25c0105fb6fa3/aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc", size = 1755181, upload-time = "2026-01-03T17:30:27.554Z" }, + { url = "https://files.pythonhosted.org/packages/0a/87/20a35ad487efdd3fba93d5843efdfaa62d2f1479eaafa7453398a44faf13/aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf", size = 1561794, upload-time = "2026-01-03T17:30:29.254Z" }, + { url = "https://files.pythonhosted.org/packages/de/95/8fd69a66682012f6716e1bc09ef8a1a2a91922c5725cb904689f112309c4/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033", size = 1697900, upload-time = "2026-01-03T17:30:31.033Z" }, + { url = "https://files.pythonhosted.org/packages/e5/66/7b94b3b5ba70e955ff597672dad1691333080e37f50280178967aff68657/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f", size = 1728239, upload-time = "2026-01-03T17:30:32.703Z" }, + { url = "https://files.pythonhosted.org/packages/47/71/6f72f77f9f7d74719692ab65a2a0252584bf8d5f301e2ecb4c0da734530a/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679", size = 1740527, upload-time = "2026-01-03T17:30:34.695Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b4/75ec16cbbd5c01bdaf4a05b19e103e78d7ce1ef7c80867eb0ace42ff4488/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423", size = 1554489, upload-time = "2026-01-03T17:30:36.864Z" }, + { url = "https://files.pythonhosted.org/packages/52/8f/bc518c0eea29f8406dcf7ed1f96c9b48e3bc3995a96159b3fc11f9e08321/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce", size = 1767852, upload-time = "2026-01-03T17:30:39.433Z" }, + { url = "https://files.pythonhosted.org/packages/9d/f2/a07a75173124f31f11ea6f863dc44e6f09afe2bca45dd4e64979490deab1/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a", size = 1722379, upload-time = "2026-01-03T17:30:41.081Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "albucore" +version = "0.0.24" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "opencv-python-headless", marker = "sys_platform == 'linux'" }, + { name = "simsimd", marker = "sys_platform == 'linux'" }, + { name = "stringzilla", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/13/69/d4cbcf2a5768bf91cd14ffef783520458431e5d2b22fbc08418d3ba09a88/albucore-0.0.24.tar.gz", hash = "sha256:f2cab5431fadf94abf87fd0c89d9f59046e49fe5de34afea8f89bc8390253746", size = 16981, upload-time = "2025-03-09T18:46:51.409Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/e2/91f145e1f32428e9e1f21f46a7022ffe63d11f549ee55c3b9265ff5207fc/albucore-0.0.24-py3-none-any.whl", hash = "sha256:adef6e434e50e22c2ee127b7a3e71f2e35fa088bcf54431e18970b62d97d0005", size = 15372, upload-time = "2025-03-09T18:46:50.177Z" }, +] + +[[package]] +name = "albumentations" +version = "2.0.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "albucore", marker = "sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "opencv-python-headless", marker = "sys_platform == 'linux'" }, + { name = "pydantic", marker = "sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'linux'" }, + { name = "scipy", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/f4/85eb56c3217b53bcfc2d12e840a0b18ca60902086321cafa5a730f9c0470/albumentations-2.0.8.tar.gz", hash = "sha256:4da95e658e490de3c34af8fcdffed09e36aa8a4edd06ca9f9e7e3ea0b0b16856", size = 354460, upload-time = "2025-05-27T21:23:17.415Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/64/013409c451a44b61310fb757af4527f3de57fc98a00f40448de28b864290/albumentations-2.0.8-py3-none-any.whl", hash = "sha256:c4c4259aaf04a7386ad85c7fdcb73c6c7146ca3057446b745cc035805acb1017", size = 369423, upload-time = "2025-05-27T21:23:15.609Z" }, +] + +[[package]] +name = "alembic" +version = "1.18.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mako", marker = "sys_platform == 'linux'" }, + { name = "sqlalchemy", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/94/13/8b084e0f2efb0275a1d534838844926f798bd766566b1375174e2448cd31/alembic-1.18.4.tar.gz", hash = "sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc", size = 2056725, upload-time = "2026-02-10T16:00:47.195Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/29/6533c317b74f707ea28f8d633734dbda2119bbadfc61b2f3640ba835d0f7/alembic-1.18.4-py3-none-any.whl", hash = "sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a", size = 263893, upload-time = "2026-02-10T16:00:49.997Z" }, +] + +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "antlr4-python3-runtime" +version = "4.9.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3e/38/7859ff46355f76f8d19459005ca000b6e7012f2f1ca597746cbcd1fbfe5e/antlr4-python3-runtime-4.9.3.tar.gz", hash = "sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b", size = 117034, upload-time = "2021-11-06T17:52:23.524Z" } + +[[package]] +name = "anyio" +version = "4.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, +] + +[[package]] +name = "attrs" +version = "25.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, +] + +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, +] + +[[package]] +name = "cachetools" +version = "7.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/dd/57fe3fdb6e65b25a5987fd2cdc7e22db0aef508b91634d2e57d22928d41b/cachetools-7.0.5.tar.gz", hash = "sha256:0cd042c24377200c1dcd225f8b7b12b0ca53cc2c961b43757e774ebe190fd990", size = 37367, upload-time = "2026-03-09T20:51:29.451Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/f3/39cf3367b8107baa44f861dc802cbf16263c945b62d8265d36034fc07bea/cachetools-7.0.5-py3-none-any.whl", hash = "sha256:46bc8ebefbe485407621d0a4264b23c080cedd913921bad7ac3ed2f26c183114", size = 13918, upload-time = "2026-03-09T20:51:27.33Z" }, +] + +[[package]] +name = "certifi" +version = "2026.2.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/35/02daf95b9cd686320bb622eb148792655c9412dbb9b67abb5694e5910a24/charset_normalizer-3.4.5.tar.gz", hash = "sha256:95adae7b6c42a6c5b5b559b1a99149f090a57128155daeea91732c8d970d8644", size = 134804, upload-time = "2026-03-06T06:03:19.46Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/d8/a54f7c0b96f1df3563e9190f04daf981e365a9b397eedfdfb5dbef7e5c6c/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0294916d6ccf2d069727d65973c3a1ca477d68708db25fd758dd28b0827cff54", size = 189356, upload-time = "2026-03-06T06:01:16.511Z" }, + { url = "https://files.pythonhosted.org/packages/42/69/2bf7f76ce1446759a5787cb87d38f6a61eb47dbbdf035cfebf6347292a65/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dc57a0baa3eeedd99fafaef7511b5a6ef4581494e8168ee086031744e2679467", size = 206369, upload-time = "2026-03-06T06:01:17.853Z" }, + { url = "https://files.pythonhosted.org/packages/10/9c/949d1a46dab56b959d9a87272482195f1840b515a3380e39986989a893ae/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ed1a9a204f317ef879b32f9af507d47e49cd5e7f8e8d5d96358c98373314fc60", size = 203285, upload-time = "2026-03-06T06:01:19.473Z" }, + { url = "https://files.pythonhosted.org/packages/67/5c/ae30362a88b4da237d71ea214a8c7eb915db3eec941adda511729ac25fa2/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ad83b8f9379176c841f8865884f3514d905bcd2a9a3b210eaa446e7d2223e4d", size = 196274, upload-time = "2026-03-06T06:01:20.728Z" }, + { url = "https://files.pythonhosted.org/packages/b2/07/c9f2cb0e46cb6d64fdcc4f95953747b843bb2181bda678dc4e699b8f0f9a/charset_normalizer-3.4.5-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:a118e2e0b5ae6b0120d5efa5f866e58f2bb826067a646431da4d6a2bdae7950e", size = 184715, upload-time = "2026-03-06T06:01:22.194Z" }, + { url = "https://files.pythonhosted.org/packages/36/64/6b0ca95c44fddf692cd06d642b28f63009d0ce325fad6e9b2b4d0ef86a52/charset_normalizer-3.4.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:754f96058e61a5e22e91483f823e07df16416ce76afa4ebf306f8e1d1296d43f", size = 193426, upload-time = "2026-03-06T06:01:23.795Z" }, + { url = "https://files.pythonhosted.org/packages/50/bc/a730690d726403743795ca3f5bb2baf67838c5fea78236098f324b965e40/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0c300cefd9b0970381a46394902cd18eaf2aa00163f999590ace991989dcd0fc", size = 191780, upload-time = "2026-03-06T06:01:25.053Z" }, + { url = "https://files.pythonhosted.org/packages/97/4f/6c0bc9af68222b22951552d73df4532b5be6447cee32d58e7e8c74ecbb7b/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c108f8619e504140569ee7de3f97d234f0fbae338a7f9f360455071ef9855a95", size = 185805, upload-time = "2026-03-06T06:01:26.294Z" }, + { url = "https://files.pythonhosted.org/packages/dd/b9/a523fb9b0ee90814b503452b2600e4cbc118cd68714d57041564886e7325/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d1028de43596a315e2720a9849ee79007ab742c06ad8b45a50db8cdb7ed4a82a", size = 208342, upload-time = "2026-03-06T06:01:27.55Z" }, + { url = "https://files.pythonhosted.org/packages/4d/61/c59e761dee4464050713e50e27b58266cc8e209e518c0b378c1580c959ba/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:19092dde50335accf365cce21998a1c6dd8eafd42c7b226eb54b2747cdce2fac", size = 193661, upload-time = "2026-03-06T06:01:29.051Z" }, + { url = "https://files.pythonhosted.org/packages/1c/43/729fa30aad69783f755c5ad8649da17ee095311ca42024742701e202dc59/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4354e401eb6dab9aed3c7b4030514328a6c748d05e1c3e19175008ca7de84fb1", size = 204819, upload-time = "2026-03-06T06:01:30.298Z" }, + { url = "https://files.pythonhosted.org/packages/87/33/d9b442ce5a91b96fc0840455a9e49a611bbadae6122778d0a6a79683dd31/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a68766a3c58fde7f9aaa22b3786276f62ab2f594efb02d0a1421b6282e852e98", size = 198080, upload-time = "2026-03-06T06:01:31.478Z" }, + { url = "https://files.pythonhosted.org/packages/c5/60/3a621758945513adfd4db86827a5bafcc615f913dbd0b4c2ed64a65731be/charset_normalizer-3.4.5-py3-none-any.whl", hash = "sha256:9db5e3fcdcee89a78c04dffb3fe33c79f77bd741a624946db2591c81b2fc85b0", size = 55455, upload-time = "2026-03-06T06:03:17.827Z" }, +] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, +] + +[[package]] +name = "cligj" +version = "0.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ea/0d/837dbd5d8430fd0f01ed72c4cfb2f548180f4c68c635df84ce87956cff32/cligj-0.7.2.tar.gz", hash = "sha256:a4bc13d623356b373c2c27c53dbd9c68cae5d526270bfa71f6c6fa69669c6b27", size = 9803, upload-time = "2021-05-28T21:23:27.935Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/86/43fa9f15c5b9fb6e82620428827cd3c284aa933431405d1bcf5231ae3d3e/cligj-0.7.2-py3-none-any.whl", hash = "sha256:c1ca117dbce1fe20a5809dc96f01e1c2840f6dcc939b3ddbb1111bf330ba82df", size = 7069, upload-time = "2021-05-28T21:23:26.877Z" }, +] + +[[package]] +name = "cloudpickle" +version = "3.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, +] + +[[package]] +name = "contourpy" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, + { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, + { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, + { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, + { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, + { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, +] + +[[package]] +name = "cryptography" +version = "46.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, + { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, + { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, + { url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" }, + { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, + { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" }, + { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" }, + { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, + { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, + { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, + { url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" }, + { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, + { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" }, + { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, + { url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" }, + { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, + { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, + { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, +] + +[[package]] +name = "cuda-bindings" +version = "12.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-pathfinder", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/c1/dabe88f52c3e3760d861401bb994df08f672ec893b8f7592dc91626adcf3/cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fda147a344e8eaeca0c6ff113d2851ffca8f7dfc0a6c932374ee5c47caa649c8", size = 12151019, upload-time = "2025-10-21T14:51:43.167Z" }, +] + +[[package]] +name = "cuda-pathfinder" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/de/8ca2b613042550dcf9ef50c596c8b1f602afda92cf9032ac28a73f6ee410/cuda_pathfinder-1.4.2-py3-none-any.whl", hash = "sha256:eb354abc20278f8609dc5b666a24648655bef5613c6dfe78a238a6fd95566754", size = 44779, upload-time = "2026-03-10T21:57:30.974Z" }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, +] + +[[package]] +name = "databricks-sdk" +version = "0.97.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth", marker = "sys_platform == 'linux'" }, + { name = "protobuf", marker = "sys_platform == 'linux'" }, + { name = "requests", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/6d/abe91ddf26be80619a02ddfc34ee3cd92da285967fff0173fcff54c433c3/databricks_sdk-0.97.0.tar.gz", hash = "sha256:e3d0e0d283963f72fc2b995dd97a6c0e988062214cd28aca4e1a5ff71c264dcb", size = 871058, upload-time = "2026-03-09T08:16:10.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/02/11e9893cf1a6c0b495260838582a2a2a6037e1e6ce2c32164cf9e7a2353a/databricks_sdk-0.97.0-py3-none-any.whl", hash = "sha256:fbb758e3374ce5b7f697182781c0b8904e341eac4a3722a07138cacde09d4e7b", size = 822045, upload-time = "2026-03-09T08:16:08.044Z" }, +] + +[[package]] +name = "datasets" +version = "4.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill", marker = "sys_platform == 'linux'" }, + { name = "filelock", marker = "sys_platform == 'linux'" }, + { name = "fsspec", extra = ["http"], marker = "sys_platform == 'linux'" }, + { name = "httpx", marker = "sys_platform == 'linux'" }, + { name = "huggingface-hub", marker = "sys_platform == 'linux'" }, + { name = "multiprocess", marker = "sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'linux'" }, + { name = "pandas", marker = "sys_platform == 'linux'" }, + { name = "pyarrow", marker = "sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'linux'" }, + { name = "requests", marker = "sys_platform == 'linux'" }, + { name = "tqdm", marker = "sys_platform == 'linux'" }, + { name = "xxhash", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/9c/ba18de0b70858533e422ed6cfe0e46789473cef7fc7fc3653e23fa494730/datasets-4.7.0.tar.gz", hash = "sha256:4984cdfc65d04464da7f95205a55cb50515fd94ae3176caacb50a1b7273792e2", size = 602008, upload-time = "2026-03-09T19:01:49.298Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/03/c6d9c3119cf712f638fe763e887ecaac6acbb62bf1e2acc3cbde0df340fd/datasets-4.7.0-py3-none-any.whl", hash = "sha256:d5fe3025ec6acc3b5649f10d5576dff5e054134927604e6913c1467a04adc3c2", size = 527530, upload-time = "2026-03-09T19:01:47.443Z" }, +] + +[[package]] +name = "dill" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976, upload-time = "2025-04-16T00:41:48.867Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668, upload-time = "2025-04-16T00:41:47.671Z" }, +] + +[[package]] +name = "docker" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests", marker = "sys_platform == 'linux'" }, + { name = "urllib3", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834, upload-time = "2024-05-23T11:13:57.216Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774, upload-time = "2024-05-23T11:13:55.01Z" }, +] + +[[package]] +name = "donfig" +version = "0.8.1.post1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/71/80cc718ff6d7abfbabacb1f57aaa42e9c1552bfdd01e64ddd704e4a03638/donfig-0.8.1.post1.tar.gz", hash = "sha256:3bef3413a4c1c601b585e8d297256d0c1470ea012afa6e8461dc28bfb7c23f52", size = 19506, upload-time = "2024-05-23T14:14:31.513Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl", hash = "sha256:2a3175ce74a06109ff9307d90a230f81215cbac9a751f4d1c6194644b8204f9d", size = 21592, upload-time = "2024-05-23T14:13:55.283Z" }, +] + +[[package]] +name = "fastapi" +version = "0.135.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc", marker = "sys_platform == 'linux'" }, + { name = "pydantic", marker = "sys_platform == 'linux'" }, + { name = "starlette", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, + { name = "typing-inspection", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e7/7b/f8e0211e9380f7195ba3f3d40c292594fd81ba8ec4629e3854c353aaca45/fastapi-0.135.1.tar.gz", hash = "sha256:d04115b508d936d254cea545b7312ecaa58a7b3a0f84952535b4c9afae7668cd", size = 394962, upload-time = "2026-03-01T18:18:29.369Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/72/42e900510195b23a56bde950d26a51f8b723846bfcaa0286e90287f0422b/fastapi-0.135.1-py3-none-any.whl", hash = "sha256:46e2fc5745924b7c840f71ddd277382af29ce1cdb7d5eab5bf697e3fb9999c9e", size = 116999, upload-time = "2026-03-01T18:18:30.831Z" }, +] + +[[package]] +name = "filelock" +version = "3.25.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/8b/4c32ecde6bea6486a2a5d05340e695174351ff6b06cf651a74c005f9df00/filelock-3.25.1.tar.gz", hash = "sha256:b9a2e977f794ef94d77cdf7d27129ac648a61f585bff3ca24630c1629f701aa9", size = 40319, upload-time = "2026-03-09T19:38:47.309Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl", hash = "sha256:18972df45473c4aa2c7921b609ee9ca4925910cc3a0fb226c96b92fc224ef7bf", size = 26720, upload-time = "2026-03-09T19:38:45.718Z" }, +] + +[[package]] +name = "flask" +version = "3.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blinker", marker = "sys_platform == 'linux'" }, + { name = "click", marker = "sys_platform == 'linux'" }, + { name = "itsdangerous", marker = "sys_platform == 'linux'" }, + { name = "jinja2", marker = "sys_platform == 'linux'" }, + { name = "markupsafe", marker = "sys_platform == 'linux'" }, + { name = "werkzeug", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/00/35d85dcce6c57fdc871f3867d465d780f302a175ea360f62533f12b27e2b/flask-3.1.3.tar.gz", hash = "sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb", size = 759004, upload-time = "2026-02-19T05:00:57.678Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl", hash = "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", size = 103424, upload-time = "2026-02-19T05:00:56.027Z" }, +] + +[[package]] +name = "flask-cors" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flask", marker = "sys_platform == 'linux'" }, + { name = "werkzeug", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/70/74/0fc0fa68d62f21daef41017dafab19ef4b36551521260987eb3a5394c7ba/flask_cors-6.0.2.tar.gz", hash = "sha256:6e118f3698249ae33e429760db98ce032a8bf9913638d085ca0f4c5534ad2423", size = 13472, upload-time = "2025-12-12T20:31:42.861Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/af/72ad54402e599152de6d067324c46fe6a4f531c7c65baf7e96c63db55eaf/flask_cors-6.0.2-py3-none-any.whl", hash = "sha256:e57544d415dfd7da89a9564e1e3a9e515042df76e12130641ca6f3f2f03b699a", size = 13257, upload-time = "2025-12-12T20:31:41.3Z" }, +] + +[[package]] +name = "fonttools" +version = "4.62.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/96/686339e0fda8142b7ebed39af53f4a5694602a729662f42a6209e3be91d0/fonttools-4.62.0.tar.gz", hash = "sha256:0dc477c12b8076b4eb9af2e440421b0433ffa9e1dcb39e0640a6c94665ed1098", size = 3579521, upload-time = "2026-03-09T16:50:06.217Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/8c/c52a4310de58deeac7e9ea800892aec09b00bb3eb0c53265b31ec02be115/fonttools-4.62.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d732938633681d6e2324e601b79e93f7f72395ec8681f9cdae5a8c08bc167e72", size = 5032975, upload-time = "2026-03-09T16:48:55.718Z" }, + { url = "https://files.pythonhosted.org/packages/0b/a1/d16318232964d786907b9b3613b8409f74cf0be2da400854509d3a864e43/fonttools-4.62.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:31a804c16d76038cc4e3826e07678efb0a02dc4f15396ea8e07088adbfb2578e", size = 4988544, upload-time = "2026-03-09T16:48:57.715Z" }, + { url = "https://files.pythonhosted.org/packages/b2/8d/7e745ca3e65852adc5e52a83dc213fe1b07d61cb5b394970fcd4b1199d1e/fonttools-4.62.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:090e74ac86e68c20150e665ef8e7e0c20cb9f8b395302c9419fa2e4d332c3b51", size = 4971296, upload-time = "2026-03-09T16:48:59.678Z" }, + { url = "https://files.pythonhosted.org/packages/e6/d4/b717a4874175146029ca1517e85474b1af80c9d9a306fc3161e71485eea5/fonttools-4.62.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8f086120e8be9e99ca1288aa5ce519833f93fe0ec6ebad2380c1dee18781f0b5", size = 5122503, upload-time = "2026-03-09T16:49:02.464Z" }, + { url = "https://files.pythonhosted.org/packages/9c/57/c2487c281dde03abb2dec244fd67059b8d118bd30a653cbf69e94084cb23/fonttools-4.62.0-py3-none-any.whl", hash = "sha256:75064f19a10c50c74b336aa5ebe7b1f89fd0fb5255807bfd4b0c6317098f4af3", size = 1152427, upload-time = "2026-03-09T16:50:04.074Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "fsspec" +version = "2026.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/7c/f60c259dcbf4f0c47cc4ddb8f7720d2dcdc8888c8e5ad84c73ea4531cc5b/fsspec-2026.2.0.tar.gz", hash = "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff", size = 313441, upload-time = "2026-02-05T21:50:53.743Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl", hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437", size = 202505, upload-time = "2026-02-05T21:50:51.819Z" }, +] + +[package.optional-dependencies] +http = [ + { name = "aiohttp", marker = "sys_platform == 'linux'" }, +] + +[[package]] +name = "geopandas" +version = "1.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'linux'" }, + { name = "pandas", marker = "sys_platform == 'linux'" }, + { name = "pyogrio", marker = "sys_platform == 'linux'" }, + { name = "pyproj", marker = "sys_platform == 'linux'" }, + { name = "shapely", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/ba/8e6b2091878e99e86a36a814dcaeff652ed48bdb03d53e78e15aaa63a914/geopandas-1.1.3.tar.gz", hash = "sha256:91a31989b6f566012838d21d5f8033f37dce882079ccb7cfdc40d5ccce7f284f", size = 336718, upload-time = "2026-03-09T21:49:09.545Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/78/6a04792ace63a93e162f1305392d500ae8ddcb620e7eb88a22fd622b35bb/geopandas-1.1.3-py3-none-any.whl", hash = "sha256:90d62a64f95eaa3be2ccc115c5f3d6e24208bb11983b390fdc0621a3eccd0230", size = 342514, upload-time = "2026-03-09T21:49:07.973Z" }, +] + +[[package]] +name = "gitdb" +version = "4.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, +] + +[[package]] +name = "gitpython" +version = "3.1.46" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/b5/59d16470a1f0dfe8c793f9ef56fd3826093fc52b3bd96d6b9d6c26c7e27b/gitpython-3.1.46.tar.gz", hash = "sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f", size = 215371, upload-time = "2026-01-01T15:37:32.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", size = 208620, upload-time = "2026-01-01T15:37:30.574Z" }, +] + +[[package]] +name = "google-auth" +version = "2.49.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography", marker = "sys_platform == 'linux'" }, + { name = "pyasn1-modules", marker = "sys_platform == 'linux'" }, + { name = "rsa", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/59/7371175bfd949abfb1170aa076352131d7281bd9449c0f978604fc4431c3/google_auth-2.49.0.tar.gz", hash = "sha256:9cc2d9259d3700d7a257681f81052db6737495a1a46b610597f4b8bafe5286ae", size = 333444, upload-time = "2026-03-06T21:53:06.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/45/de64b823b639103de4b63dd193480dce99526bd36be6530c2dba85bf7817/google_auth-2.49.0-py3-none-any.whl", hash = "sha256:f893ef7307f19cf53700b7e2f61b5a6affe3aa0edf9943b13788920ab92d8d87", size = 240676, upload-time = "2026-03-06T21:52:38.304Z" }, +] + +[[package]] +name = "google-crc32c" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/03/41/4b9c02f99e4c5fb477122cd5437403b552873f014616ac1d19ac8221a58d/google_crc32c-1.8.0.tar.gz", hash = "sha256:a428e25fb7691024de47fecfbff7ff957214da51eddded0da0ae0e0f03a2cf79", size = 14192, upload-time = "2025-12-16T00:35:25.142Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/a9/a780cc66f86335a6019f557a8aaca8fbb970728f0efd2430d15ff1beae0e/google_crc32c-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14f87e04d613dfa218d6135e81b78272c3b904e2a7053b841481b38a7d901411", size = 33364, upload-time = "2025-12-16T00:40:22.96Z" }, + { url = "https://files.pythonhosted.org/packages/21/3f/3457ea803db0198c9aaca2dd373750972ce28a26f00544b6b85088811939/google_crc32c-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb5c869c2923d56cb0c8e6bcdd73c009c36ae39b652dbe46a05eb4ef0ad01454", size = 33740, upload-time = "2025-12-16T00:40:23.96Z" }, +] + +[[package]] +name = "graphene" +version = "3.4.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "graphql-core", marker = "sys_platform == 'linux'" }, + { name = "graphql-relay", marker = "sys_platform == 'linux'" }, + { name = "python-dateutil", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/f6/bf62ff950c317ed03e77f3f6ddd7e34aaa98fe89d79ebd660c55343d8054/graphene-3.4.3.tar.gz", hash = "sha256:2a3786948ce75fe7e078443d37f609cbe5bb36ad8d6b828740ad3b95ed1a0aaa", size = 44739, upload-time = "2024-11-09T20:44:25.757Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/e0/61d8e98007182e6b2aca7cf65904721fb2e4bce0192272ab9cb6f69d8812/graphene-3.4.3-py2.py3-none-any.whl", hash = "sha256:820db6289754c181007a150db1f7fff544b94142b556d12e3ebc777a7bf36c71", size = 114894, upload-time = "2024-11-09T20:44:23.851Z" }, +] + +[[package]] +name = "graphql-core" +version = "3.2.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/c5/36aa96205c3ecbb3d34c7c24189e4553c7ca2ebc7e1dd07432339b980272/graphql_core-3.2.8.tar.gz", hash = "sha256:015457da5d996c924ddf57a43f4e959b0b94fb695b85ed4c29446e508ed65cf3", size = 513181, upload-time = "2026-03-05T19:55:37.332Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/41/cb887d9afc5dabd78feefe6ccbaf83ff423c206a7a1b7aeeac05120b2125/graphql_core-3.2.8-py3-none-any.whl", hash = "sha256:cbee07bee1b3ed5e531723685369039f32ff815ef60166686e0162f540f1520c", size = 207349, upload-time = "2026-03-05T19:55:35.911Z" }, +] + +[[package]] +name = "graphql-relay" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "graphql-core", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/13/98fbf8d67552f102488ffc16c6f559ce71ea15f6294728d33928ab5ff14d/graphql-relay-3.2.0.tar.gz", hash = "sha256:1ff1c51298356e481a0be009ccdff249832ce53f30559c1338f22a0e0d17250c", size = 50027, upload-time = "2022-04-16T11:03:45.447Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/16/a4cf06adbc711bd364a73ce043b0b08d8fa5aae3df11b6ee4248bcdad2e0/graphql_relay-3.2.0-py3-none-any.whl", hash = "sha256:c9b22bd28b170ba1fe674c74384a8ff30a76c8e26f88ac3aa1584dd3179953e5", size = 16940, upload-time = "2022-04-16T11:03:43.895Z" }, +] + +[[package]] +name = "greenlet" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/51/1664f6b78fc6ebbd98019a1fd730e83fa78f2db7058f72b1463d3612b8db/greenlet-3.3.2.tar.gz", hash = "sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2", size = 188267, upload-time = "2026-02-20T20:54:15.531Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/23/0eae412a4ade4e6623ff7626e38998cb9b11e9ff1ebacaa021e4e108ec15/greenlet-3.3.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd", size = 601217, upload-time = "2026-02-20T20:47:31.462Z" }, + { url = "https://files.pythonhosted.org/packages/f8/16/5b1678a9c07098ecb9ab2dd159fafaf12e963293e61ee8d10ecb55273e5e/greenlet-3.3.2-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac", size = 611792, upload-time = "2026-02-20T20:55:58.423Z" }, + { url = "https://files.pythonhosted.org/packages/50/1f/5155f55bd71cabd03765a4aac9ac446be129895271f73872c36ebd4b04b6/greenlet-3.3.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070", size = 613875, upload-time = "2026-02-20T20:21:01.102Z" }, + { url = "https://files.pythonhosted.org/packages/fc/dd/845f249c3fcd69e32df80cdab059b4be8b766ef5830a3d0aa9d6cad55beb/greenlet-3.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79", size = 1571467, upload-time = "2026-02-20T20:49:33.495Z" }, + { url = "https://files.pythonhosted.org/packages/2a/50/2649fe21fcc2b56659a452868e695634722a6655ba245d9f77f5656010bf/greenlet-3.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395", size = 1640001, upload-time = "2026-02-20T20:21:09.154Z" }, +] + +[[package]] +name = "gunicorn" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/13/ef67f59f6a7896fdc2c1d62b5665c5219d6b0a9a1784938eb9a28e55e128/gunicorn-25.1.0.tar.gz", hash = "sha256:1426611d959fa77e7de89f8c0f32eed6aa03ee735f98c01efba3e281b1c47616", size = 594377, upload-time = "2026-02-13T11:09:58.989Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/73/4ad5b1f6a2e21cf1e85afdaad2b7b1a933985e2f5d679147a1953aaa192c/gunicorn-25.1.0-py3-none-any.whl", hash = "sha256:d0b1236ccf27f72cfe14bce7caadf467186f19e865094ca84221424e839b8b8b", size = 197067, upload-time = "2026-02-13T11:09:57.146Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "hf-xet" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/cb/9bb543bd987ffa1ee48202cc96a756951b734b79a542335c566148ade36c/hf_xet-1.3.2.tar.gz", hash = "sha256:e130ee08984783d12717444e538587fa2119385e5bd8fc2bb9f930419b73a7af", size = 643646, upload-time = "2026-02-27T17:26:08.051Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/ca/907890ce6ef5598b5920514f255ed0a65f558f820515b18db75a51b2f878/hf_xet-1.3.2-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7c2a054a97c44e136b1f7f5a78f12b3efffdf2eed3abc6746fc5ea4b39511633", size = 4180750, upload-time = "2026-02-27T17:25:43.125Z" }, + { url = "https://files.pythonhosted.org/packages/8c/ad/bc7f41f87173d51d0bce497b171c4ee0cbde1eed2d7b4216db5d0ada9f50/hf_xet-1.3.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:06b724a361f670ae557836e57801b82c75b534812e351a87a2c739f77d1e0635", size = 3961035, upload-time = "2026-02-27T17:25:41.837Z" }, + { url = "https://files.pythonhosted.org/packages/73/38/600f4dda40c4a33133404d9fe644f1d35ff2d9babb4d0435c646c63dd107/hf_xet-1.3.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:305f5489d7241a47e0458ef49334be02411d1d0f480846363c1c8084ed9916f7", size = 4161378, upload-time = "2026-02-27T17:26:00.365Z" }, + { url = "https://files.pythonhosted.org/packages/00/b3/7bc1ff91d1ac18420b7ad1e169b618b27c00001b96310a89f8a9294fe509/hf_xet-1.3.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:06cdbde243c85f39a63b28e9034321399c507bcd5e7befdd17ed2ccc06dfe14e", size = 4398020, upload-time = "2026-02-27T17:26:03.977Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi", marker = "sys_platform == 'linux'" }, + { name = "h11", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio", marker = "sys_platform == 'linux'" }, + { name = "certifi", marker = "sys_platform == 'linux'" }, + { name = "httpcore", marker = "sys_platform == 'linux'" }, + { name = "idna", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "huey" +version = "2.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/29/3428d52eb8e85025e264a291641a9f9d6407cc1e51d1b630f6ac5815999a/huey-2.6.0.tar.gz", hash = "sha256:8d11f8688999d65266af1425b831f6e3773e99415027177b8734b0ffd5e251f6", size = 221068, upload-time = "2026-01-06T03:01:02.055Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/34/fae9ac8f1c3a552fd3f7ff652b94c78d219dedc5fce0c0a4232457760a00/huey-2.6.0-py3-none-any.whl", hash = "sha256:1b9df9d370b49c6d5721ba8a01ac9a787cf86b3bdc584e4679de27b920395c3f", size = 76951, upload-time = "2026-01-06T03:01:00.808Z" }, +] + +[[package]] +name = "huggingface-hub" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock", marker = "sys_platform == 'linux'" }, + { name = "fsspec", marker = "sys_platform == 'linux'" }, + { name = "hf-xet", marker = "(platform_machine == 'AMD64' and sys_platform == 'linux') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'amd64' and sys_platform == 'linux') or (platform_machine == 'arm64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "httpx", marker = "sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'linux'" }, + { name = "tqdm", marker = "sys_platform == 'linux'" }, + { name = "typer", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d5/7a/304cec37112382c4fe29a43bcb0d5891f922785d18745883d2aa4eb74e4b/huggingface_hub-1.6.0.tar.gz", hash = "sha256:d931ddad8ba8dfc1e816bf254810eb6f38e5c32f60d4184b5885662a3b167325", size = 717071, upload-time = "2026-03-06T14:19:18.524Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/e3/e3a44f54c8e2f28983fcf07f13d4260b37bd6a0d3a081041bc60b91d230e/huggingface_hub-1.6.0-py3-none-any.whl", hash = "sha256:ef40e2d5cb85e48b2c067020fa5142168342d5108a1b267478ed384ecbf18961", size = 612874, upload-time = "2026-03-06T14:19:16.844Z" }, +] + +[[package]] +name = "hydra-core" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "antlr4-python3-runtime", marker = "sys_platform == 'linux'" }, + { name = "omegaconf", marker = "sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/8e/07e42bc434a847154083b315779b0a81d567154504624e181caf2c71cd98/hydra-core-1.3.2.tar.gz", hash = "sha256:8a878ed67216997c3e9d88a8e72e7b4767e81af37afb4ea3334b269a4390a824", size = 3263494, upload-time = "2023-02-23T18:33:43.03Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/50/e0edd38dcd63fb26a8547f13d28f7a008bc4a3fd4eb4ff030673f22ad41a/hydra_core-1.3.2-py3-none-any.whl", hash = "sha256:fa0238a9e31df3373b35b0bfb672c34cc92718d21f81311d8996a16de1141d8b", size = 154547, upload-time = "2023-02-23T18:33:40.801Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "imagecodecs" +version = "2026.3.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/8d/dc18623e5e926ad53c626e128c8baaf4ec42e41029cf0a07381cfef79289/imagecodecs-2026.3.6.tar.gz", hash = "sha256:471b8a4d1b3843cbf7179b45f7d7261f0c0b28809efc1ca6c47822477b143b85", size = 9565259, upload-time = "2026-03-07T01:26:41.183Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/fa/f67c4e644fdf06503e120f9d1c8d8654b99066dea7093a674b67704fa4a4/imagecodecs-2026.3.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:30fa140bb1a112a889926af36977214ed52a22e4557356043259b5e2f79cfba5", size = 25604431, upload-time = "2026-03-07T01:26:00.703Z" }, + { url = "https://files.pythonhosted.org/packages/8f/29/93ea9cbab7f57b4e60480c51fc51d8e138e399d11797c981d5f6e79f9832/imagecodecs-2026.3.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:e30a14aa2e1c6c90e00375292726486c1d90bf003b1414d608ea4d1f62fd8a79", size = 26468592, upload-time = "2026-03-07T01:26:04.933Z" }, +] + +[[package]] +name = "imageio" +version = "2.37.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "pillow", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/84/93bcd1300216ea50811cee96873b84a1bebf8d0489ffaf7f2a3756bab866/imageio-2.37.3.tar.gz", hash = "sha256:bbb37efbfc4c400fcd534b367b91fcd66d5da639aaa138034431a1c5e0a41451", size = 389673, upload-time = "2026-03-09T11:31:12.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl", hash = "sha256:46f5bb8522cd421c0f5ae104d8268f569d856b29eb1a13b92829d1970f32c9f0", size = 317646, upload-time = "2026-03-09T11:31:10.771Z" }, +] + +[[package]] +name = "importlib-metadata" +version = "8.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, +] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "joblib" +version = "1.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs", marker = "sys_platform == 'linux'" }, + { name = "jsonschema-specifications", marker = "sys_platform == 'linux'" }, + { name = "referencing", marker = "sys_platform == 'linux'" }, + { name = "rpds-py", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + +[[package]] +name = "kiwisolver" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934, upload-time = "2026-03-09T13:13:27.166Z" }, + { url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537, upload-time = "2026-03-09T13:13:28.707Z" }, + { url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685, upload-time = "2026-03-09T13:13:30.528Z" }, + { url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024, upload-time = "2026-03-09T13:13:32.818Z" }, + { url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241, upload-time = "2026-03-09T13:13:34.435Z" }, + { url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742, upload-time = "2026-03-09T13:13:36.4Z" }, + { url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966, upload-time = "2026-03-09T13:13:38.204Z" }, + { url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417, upload-time = "2026-03-09T13:13:39.966Z" }, + { url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238, upload-time = "2026-03-09T13:13:41.698Z" }, + { url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947, upload-time = "2026-03-09T13:13:43.343Z" }, + { url = "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", size = 194295, upload-time = "2026-03-09T13:15:38.22Z" }, +] + +[[package]] +name = "lazy-loader" +version = "0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/ac/21a1f8aa3777f5658576777ea76bfb124b702c520bbe90edf4ae9915eafa/lazy_loader-0.5.tar.gz", hash = "sha256:717f9179a0dbed357012ddad50a5ad3d5e4d9a0b8712680d4e687f5e6e6ed9b3", size = 15294, upload-time = "2026-03-06T15:45:09.054Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl", hash = "sha256:ab0ea149e9c554d4ffeeb21105ac60bed7f3b4fd69b1d2360a4add51b170b005", size = 8044, upload-time = "2026-03-06T15:45:07.668Z" }, +] + +[[package]] +name = "lightning" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fsspec", extra = ["http"], marker = "sys_platform == 'linux'" }, + { name = "lightning-utilities", marker = "sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'linux'" }, + { name = "pytorch-lightning", marker = "sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'linux'" }, + { name = "torch", marker = "sys_platform == 'linux'" }, + { name = "torchmetrics", marker = "sys_platform == 'linux'" }, + { name = "tqdm", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/15/ad/a1c91a795521be252209d45fb080f28a4f1e7244d3b37121fcc6e3e43034/lightning-2.6.1.tar.gz", hash = "sha256:859104b98c61add6fe60d0c623abf749baf25f2950a66ebdfb4bd18aa7decba9", size = 663175, upload-time = "2026-01-30T14:59:13.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/6d/42640e15a8c34b57dc7ea922152440c0c6692214a08d5282b6e3eb46ddf4/lightning-2.6.1-py3-none-any.whl", hash = "sha256:30e1adac23004c713663928541bd72ecb1371b7abc9aff9f46b7fd2644988d30", size = 853631, upload-time = "2026-01-30T14:59:11.687Z" }, +] + +[[package]] +name = "lightning-utilities" +version = "0.15.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/45/7fa8f56b17dc0f0a41ec70dd307ecd6787254483549843bef4c30ab5adce/lightning_utilities-0.15.3.tar.gz", hash = "sha256:792ae0204c79f6859721ac7f386c237a33b0ed06ba775009cb894e010a842033", size = 33553, upload-time = "2026-02-22T14:48:53.348Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/f4/ead6e0e37209b07c9baa3e984ccdb0348ca370b77cea3aaea8ddbb097e00/lightning_utilities-0.15.3-py3-none-any.whl", hash = "sha256:6c55f1bee70084a1cbeaa41ada96e4b3a0fea5909e844dd335bd80f5a73c5f91", size = 31906, upload-time = "2026-02-22T14:48:52.488Z" }, +] + +[[package]] +name = "mako" +version = "1.3.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474, upload-time = "2025-04-10T12:44:31.16Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509, upload-time = "2025-04-10T12:50:53.297Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, +] + +[[package]] +name = "matplotlib" +version = "3.10.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy", marker = "sys_platform == 'linux'" }, + { name = "cycler", marker = "sys_platform == 'linux'" }, + { name = "fonttools", marker = "sys_platform == 'linux'" }, + { name = "kiwisolver", marker = "sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'linux'" }, + { name = "pillow", marker = "sys_platform == 'linux'" }, + { name = "pyparsing", marker = "sys_platform == 'linux'" }, + { name = "python-dateutil", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269, upload-time = "2025-12-10T22:56:51.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/f3/c5195b1ae57ef85339fd7285dfb603b22c8b4e79114bae5f4f0fcf688677/matplotlib-3.10.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ab4aabc72de4ff77b3ec33a6d78a68227bf1123465887f9905ba79184a1cc04", size = 8716944, upload-time = "2025-12-10T22:55:34.922Z" }, + { url = "https://files.pythonhosted.org/packages/00/f9/7638f5cc82ec8a7aa005de48622eecc3ed7c9854b96ba15bd76b7fd27574/matplotlib-3.10.8-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24d50994d8c5816ddc35411e50a86ab05f575e2530c02752e02538122613371f", size = 9550099, upload-time = "2025-12-10T22:55:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/57/61/78cd5920d35b29fd2a0fe894de8adf672ff52939d2e9b43cb83cd5ce1bc7/matplotlib-3.10.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:99eefd13c0dc3b3c1b4d561c1169e65fe47aab7b8158754d7c084088e2329466", size = 9613040, upload-time = "2025-12-10T22:55:38.715Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "mlflow" +version = "3.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "alembic", marker = "sys_platform == 'linux'" }, + { name = "cryptography", marker = "sys_platform == 'linux'" }, + { name = "docker", marker = "sys_platform == 'linux'" }, + { name = "flask", marker = "sys_platform == 'linux'" }, + { name = "flask-cors", marker = "sys_platform == 'linux'" }, + { name = "graphene", marker = "sys_platform == 'linux'" }, + { name = "gunicorn", marker = "sys_platform == 'linux'" }, + { name = "huey", marker = "sys_platform == 'linux'" }, + { name = "matplotlib", marker = "sys_platform == 'linux'" }, + { name = "mlflow-skinny", marker = "sys_platform == 'linux'" }, + { name = "mlflow-tracing", marker = "sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "pandas", marker = "sys_platform == 'linux'" }, + { name = "pyarrow", marker = "sys_platform == 'linux'" }, + { name = "scikit-learn", marker = "sys_platform == 'linux'" }, + { name = "scipy", marker = "sys_platform == 'linux'" }, + { name = "skops", marker = "sys_platform == 'linux'" }, + { name = "sqlalchemy", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/94/a583069259500182c070db798118aee7877d37bd1981e49af5ae9113b100/mlflow-3.10.1.tar.gz", hash = "sha256:609509ccc15eb9c17861748e537cbffa57d2caf488ff3e30efed62951a6977cf", size = 9542009, upload-time = "2026-03-05T11:15:22.677Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/18/ca682e740b90d5a930981cd375f878a453a713741b5b7d9c0d9516552b5e/mlflow-3.10.1-py3-none-any.whl", hash = "sha256:17bfbd76d4071498d6199c3fc53945e5f50997d14e3e2a6bfd4dc3cb8957f209", size = 10165655, upload-time = "2026-03-05T11:15:19.541Z" }, +] + +[[package]] +name = "mlflow-skinny" +version = "3.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools", marker = "sys_platform == 'linux'" }, + { name = "click", marker = "sys_platform == 'linux'" }, + { name = "cloudpickle", marker = "sys_platform == 'linux'" }, + { name = "databricks-sdk", marker = "sys_platform == 'linux'" }, + { name = "fastapi", marker = "sys_platform == 'linux'" }, + { name = "gitpython", marker = "sys_platform == 'linux'" }, + { name = "importlib-metadata", marker = "sys_platform == 'linux'" }, + { name = "opentelemetry-api", marker = "sys_platform == 'linux'" }, + { name = "opentelemetry-proto", marker = "sys_platform == 'linux'" }, + { name = "opentelemetry-sdk", marker = "sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'linux'" }, + { name = "protobuf", marker = "sys_platform == 'linux'" }, + { name = "pydantic", marker = "sys_platform == 'linux'" }, + { name = "python-dotenv", marker = "sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'linux'" }, + { name = "requests", marker = "sys_platform == 'linux'" }, + { name = "sqlparse", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, + { name = "uvicorn", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/65/5b2c28e74c167ba8a5afe59399ef44291a0f140487f534db1900f09f59f6/mlflow_skinny-3.10.1.tar.gz", hash = "sha256:3d1c5c30245b6e7065b492b09dd47be7528e0a14c4266b782fe58f9bcd1e0be0", size = 2478631, upload-time = "2026-03-05T10:49:01.47Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/52/17460157271e70b0d8444d27f8ad730ef7d95fb82fac59dc19f11519b921/mlflow_skinny-3.10.1-py3-none-any.whl", hash = "sha256:df1dd507d8ddadf53bfab2423c76cdcafc235cd1a46921a06d1a6b4dd04b023c", size = 2987098, upload-time = "2026-03-05T10:48:59.566Z" }, +] + +[[package]] +name = "mlflow-tracing" +version = "3.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools", marker = "sys_platform == 'linux'" }, + { name = "databricks-sdk", marker = "sys_platform == 'linux'" }, + { name = "opentelemetry-api", marker = "sys_platform == 'linux'" }, + { name = "opentelemetry-proto", marker = "sys_platform == 'linux'" }, + { name = "opentelemetry-sdk", marker = "sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'linux'" }, + { name = "protobuf", marker = "sys_platform == 'linux'" }, + { name = "pydantic", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/7a/4c3c1b7a52a5956b1af81bdd90892019d5927460d520bd4f52063f423029/mlflow_tracing-3.10.1.tar.gz", hash = "sha256:9e54d63cf776d29bb9e2278d35bf27352b93f7b35c8fe8452e9ba5e2a3c5b78f", size = 1243515, upload-time = "2026-03-05T10:46:29.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/9a/7ac1db2ed7b5e21c50fadf925a53f0c77452a8a855ee4a119b084c2fa5d3/mlflow_tracing-3.10.1-py3-none-any.whl", hash = "sha256:649c722cc58d54f1f40559023a6bd6f3f08150c3ce3c3bb27972b3e795890f47", size = 1495173, upload-time = "2026-03-05T10:46:27.395Z" }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + +[[package]] +name = "msgpack" +version = "1.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4d/f2/bfb55a6236ed8725a96b0aa3acbd0ec17588e6a2c3b62a93eb513ed8783f/msgpack-1.1.2.tar.gz", hash = "sha256:3b60763c1373dd60f398488069bcdc703cd08a711477b5d480eecc9f9626f47e", size = 173581, upload-time = "2025-10-08T09:15:56.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/60/a064b0345fc36c4c3d2c743c82d9100c40388d77f0b48b2f04d6041dbec1/msgpack-1.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c63eea553c69ab05b6747901b97d620bb2a690633c77f23feb0c6a947a8a7b8f", size = 417131, upload-time = "2025-10-08T09:15:05.136Z" }, + { url = "https://files.pythonhosted.org/packages/65/92/a5100f7185a800a5d29f8d14041f61475b9de465ffcc0f3b9fba606e4505/msgpack-1.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:372839311ccf6bdaf39b00b61288e0557916c3729529b301c52c2d88842add42", size = 427556, upload-time = "2025-10-08T09:15:06.837Z" }, + { url = "https://files.pythonhosted.org/packages/f5/87/ffe21d1bf7d9991354ad93949286f643b2bb6ddbeab66373922b44c3b8cc/msgpack-1.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2929af52106ca73fcb28576218476ffbb531a036c2adbcf54a3664de124303e9", size = 404920, upload-time = "2025-10-08T09:15:08.179Z" }, + { url = "https://files.pythonhosted.org/packages/ff/41/8543ed2b8604f7c0d89ce066f42007faac1eaa7d79a81555f206a5cdb889/msgpack-1.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be52a8fc79e45b0364210eef5234a7cf8d330836d0a64dfbb878efa903d84620", size = 415013, upload-time = "2025-10-08T09:15:09.83Z" }, +] + +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, + { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, + { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, + { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, + { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, +] + +[[package]] +name = "multiprocess" +version = "0.70.18" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/fd/2ae3826f5be24c6ed87266bc4e59c46ea5b059a103f3d7e7eb76a52aeecb/multiprocess-0.70.18.tar.gz", hash = "sha256:f9597128e6b3e67b23956da07cf3d2e5cba79e2f4e0fba8d7903636663ec6d0d", size = 1798503, upload-time = "2025-04-17T03:11:27.742Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/d8/0cba6cf51a1a31f20471fbc823a716170c73012ddc4fb85d706630ed6e8f/multiprocess-0.70.18-py310-none-any.whl", hash = "sha256:60c194974c31784019c1f459d984e8f33ee48f10fcf42c309ba97b30d9bd53ea", size = 134948, upload-time = "2025-04-17T03:11:20.223Z" }, + { url = "https://files.pythonhosted.org/packages/4b/88/9039f2fed1012ef584751d4ceff9ab4a51e5ae264898f0b7cbf44340a859/multiprocess-0.70.18-py311-none-any.whl", hash = "sha256:5aa6eef98e691281b3ad923be2832bf1c55dd2c859acd73e5ec53a66aae06a1d", size = 144462, upload-time = "2025-04-17T03:11:21.657Z" }, + { url = "https://files.pythonhosted.org/packages/bf/b6/5f922792be93b82ec6b5f270bbb1ef031fd0622847070bbcf9da816502cc/multiprocess-0.70.18-py312-none-any.whl", hash = "sha256:9b78f8e5024b573730bfb654783a13800c2c0f2dfc0c25e70b40d184d64adaa2", size = 150287, upload-time = "2025-04-17T03:11:22.69Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c3/ca84c19bd14cdfc21c388fdcebf08b86a7a470ebc9f5c3c084fc2dbc50f7/multiprocess-0.70.18-py38-none-any.whl", hash = "sha256:dbf705e52a154fe5e90fb17b38f02556169557c2dd8bb084f2e06c2784d8279b", size = 132636, upload-time = "2025-04-17T03:11:24.936Z" }, + { url = "https://files.pythonhosted.org/packages/6c/28/dd72947e59a6a8c856448a5e74da6201cb5502ddff644fbc790e4bd40b9a/multiprocess-0.70.18-py39-none-any.whl", hash = "sha256:e78ca805a72b1b810c690b6b4cc32579eba34f403094bbbae962b7b5bf9dfcb8", size = 133478, upload-time = "2025-04-17T03:11:26.253Z" }, +] + +[[package]] +name = "networkx" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, +] + +[[package]] +name = "numcodecs" +version = "0.16.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/bd/8a391e7c356366224734efd24da929cc4796fff468bfb179fe1af6548535/numcodecs-0.16.5.tar.gz", hash = "sha256:0d0fb60852f84c0bd9543cc4d2ab9eefd37fc8efcc410acd4777e62a1d300318", size = 6276387, upload-time = "2025-11-21T02:49:48.986Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/1e/98aaddf272552d9fef1f0296a9939d1487914a239e98678f6b20f8b0a5c8/numcodecs-0.16.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b554ab9ecf69de7ca2b6b5e8bc696bd9747559cb4dd5127bd08d7a28bec59c3a", size = 8534814, upload-time = "2025-11-21T02:49:28.547Z" }, + { url = "https://files.pythonhosted.org/packages/fb/53/78c98ef5c8b2b784453487f3e4d6c017b20747c58b470393e230c78d18e8/numcodecs-0.16.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad1a379a45bd3491deab8ae6548313946744f868c21d5340116977ea3be5b1d6", size = 9173471, upload-time = "2025-11-21T02:49:30.444Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/10/8b/c265f4823726ab832de836cdd184d0986dcf94480f81e8739692a7ac7af2/numpy-2.4.3.tar.gz", hash = "sha256:483a201202b73495f00dbc83796c6ae63137a9bdade074f7648b3e32613412dd", size = 20727743, upload-time = "2026-03-09T07:58:53.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/12/8c9f0c6c95f76aeb20fc4a699c33e9f827fa0d0f857747c73bb7b17af945/numpy-2.4.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32e3bef222ad6b052280311d1d60db8e259e4947052c3ae7dd6817451fc8a4c5", size = 15666601, upload-time = "2026-03-09T07:56:34.461Z" }, + { url = "https://files.pythonhosted.org/packages/bd/79/cc665495e4d57d0aa6fbcc0aa57aa82671dfc78fbf95fe733ed86d98f52a/numpy-2.4.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e7dd01a46700b1967487141a66ac1a3cf0dd8ebf1f08db37d46389401512ca97", size = 16621358, upload-time = "2026-03-09T07:56:36.852Z" }, + { url = "https://files.pythonhosted.org/packages/a8/40/b4ecb7224af1065c3539f5ecfff879d090de09608ad1008f02c05c770cb3/numpy-2.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:76f0f283506c28b12bba319c0fab98217e9f9b54e6160e9c79e9f7348ba32e9c", size = 17016135, upload-time = "2026-03-09T07:56:39.337Z" }, + { url = "https://files.pythonhosted.org/packages/f7/b1/6a88e888052eed951afed7a142dcdf3b149a030ca59b4c71eef085858e43/numpy-2.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:737f630a337364665aba3b5a77e56a68cc42d350edd010c345d65a3efa3addcc", size = 18345816, upload-time = "2026-03-09T07:56:42.31Z" }, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.8.4.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.10.2.21" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.3.3.83" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, +] + +[[package]] +name = "nvidia-cufile-cu12" +version = "1.13.1.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.9.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.7.3.90" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.5.8.93" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.27.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, +] + +[[package]] +name = "nvidia-nvshmem-cu12" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd", size = 139103095, upload-time = "2025-09-06T00:32:31.266Z" }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, +] + +[[package]] +name = "omegaconf" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "antlr4-python3-runtime", marker = "sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/48/6388f1bb9da707110532cb70ec4d2822858ddfb44f1cdf1233c20a80ea4b/omegaconf-2.3.0.tar.gz", hash = "sha256:d5d4b6d29955cc50ad50c46dc269bcd92c6e00f5f90d23ab5fee7bfca4ba4cc7", size = 3298120, upload-time = "2022-12-08T20:59:22.753Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/94/1843518e420fa3ed6919835845df698c7e27e183cb997394e4a670973a65/omegaconf-2.3.0-py3-none-any.whl", hash = "sha256:7b4df175cdb08ba400f45cae3bdcae7ba8365db4d165fc65fd04b050ab63b46b", size = 79500, upload-time = "2022-12-08T20:59:19.686Z" }, +] + +[[package]] +name = "opencv-python-headless" +version = "4.13.0.92" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/76/9417a6aef9def70e467a5bf560579f816148a4c658b7d525581b356eda9e/opencv_python_headless-4.13.0.92-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c8cfc8e87ed452b5cecb9419473ee5560a989859fe1d10d1ce11ae87b09a2cb", size = 33703709, upload-time = "2026-02-05T10:24:46.469Z" }, + { url = "https://files.pythonhosted.org/packages/92/ce/bd17ff5772938267fd49716e94ca24f616ff4cb1ff4c6be13085108037be/opencv_python_headless-4.13.0.92-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0525a3d2c0b46c611e2130b5fdebc94cf404845d8fa64d2f3a3b679572a5bd22", size = 56016764, upload-time = "2026-02-05T10:26:48.904Z" }, + { url = "https://files.pythonhosted.org/packages/8f/b4/b7bcbf7c874665825a8c8e1097e93ea25d1f1d210a3e20d4451d01da30aa/opencv_python_headless-4.13.0.92-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb60e36b237b1ebd40a912da5384b348df8ed534f6f644d8e0b4f103e272ba7d", size = 35010236, upload-time = "2026-02-05T10:28:11.031Z" }, + { url = "https://files.pythonhosted.org/packages/4b/33/b5db29a6c00eb8f50708110d8d453747ca125c8b805bc437b289dbdcc057/opencv_python_headless-4.13.0.92-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0bd48544f77c68b2941392fcdf9bcd2b9cdf00e98cb8c29b2455d194763cf99e", size = 60391106, upload-time = "2026-02-05T10:30:14.236Z" }, +] + +[[package]] +name = "openslide-bin" +version = "4.0.0.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/28/b2/b771cdafcbed945604360b771f06d792d99eb6329832351ac88395e1fcad/openslide_bin-4.0.0.12.tar.gz", hash = "sha256:fb5bae9f3d38f12a15be903d167abff1993093b63bce2e8a40eb61289dd19e67", size = 19805336, upload-time = "2026-03-09T03:37:57.901Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/b5/4a1c8d99996c3c73f29027b5af33818858dfa394491e5aa2b8bb135949c5/openslide_bin-4.0.0.12-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:8a975e09668e7659da3a51bf68b9e765df028b83aafd1ef44af7c0a293e11628", size = 4243166, upload-time = "2026-03-09T03:37:52.847Z" }, + { url = "https://files.pythonhosted.org/packages/a2/ee/80e653763fc3e89c408097cf8949ab67d3fd0811d308f4ba9b6d14240311/openslide_bin-4.0.0.12-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:5f82cf6fac11c039305f826b4640e4daa0d3547e07e45e3316392ac460664bb5", size = 4345974, upload-time = "2026-03-09T03:37:54.306Z" }, +] + +[[package]] +name = "openslide-python" +version = "1.4.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pillow", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/75/f3/11a90efe13994c84ca22bf7d28195e1e6912066dada1960136536c63cdc0/openslide_python-1.4.3.tar.gz", hash = "sha256:06eec2e05988c37b71ff1ee57d1f465c0dcbf26e655d1dcdab70217ad2769b82", size = 386553, upload-time = "2025-12-03T12:12:50.187Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/a7/571f5f92294c954a426f5fc05844bbc8be9da739bfeb5b20e2298f03f238/openslide_python-1.4.3-cp311-abi3-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:024aa25336326bcf7d8b6eed8ec302cc98e068cf534c09f728a8b2860b6ce71d", size = 36843, upload-time = "2025-12-03T12:12:38.527Z" }, + { url = "https://files.pythonhosted.org/packages/70/d7/3abe2aade6c89461d59274f2899362c608a0cf9cc8933ea6da869871db05/openslide_python-1.4.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:971990d2281f1abe294047961fa50e349edab3eff6d07370dceb4eef03c1c17b", size = 37427, upload-time = "2025-12-03T12:12:39.784Z" }, +] + +[[package]] +name = "opentelemetry-api" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2c/1d/4049a9e8698361cc1a1aa03a6c59e4fa4c71e0c0f94a30f988a6876a2ae6/opentelemetry_api-1.40.0.tar.gz", hash = "sha256:159be641c0b04d11e9ecd576906462773eb97ae1b657730f0ecf64d32071569f", size = 70851, upload-time = "2026-03-04T14:17:21.555Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/bf/93795954016c522008da367da292adceed71cca6ee1717e1d64c83089099/opentelemetry_api-1.40.0-py3-none-any.whl", hash = "sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9", size = 68676, upload-time = "2026-03-04T14:17:01.24Z" }, +] + +[[package]] +name = "opentelemetry-proto" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/77/dd38991db037fdfce45849491cb61de5ab000f49824a00230afb112a4392/opentelemetry_proto-1.40.0.tar.gz", hash = "sha256:03f639ca129ba513f5819810f5b1f42bcb371391405d99c168fe6937c62febcd", size = 45667, upload-time = "2026-03-04T14:17:31.194Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/b2/189b2577dde745b15625b3214302605b1353436219d42b7912e77fa8dc24/opentelemetry_proto-1.40.0-py3-none-any.whl", hash = "sha256:266c4385d88923a23d63e353e9761af0f47a6ed0d486979777fe4de59dc9b25f", size = 72073, upload-time = "2026-03-04T14:17:16.673Z" }, +] + +[[package]] +name = "opentelemetry-sdk" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api", marker = "sys_platform == 'linux'" }, + { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/fd/3c3125b20ba18ce2155ba9ea74acb0ae5d25f8cd39cfd37455601b7955cc/opentelemetry_sdk-1.40.0.tar.gz", hash = "sha256:18e9f5ec20d859d268c7cb3c5198c8d105d073714db3de50b593b8c1345a48f2", size = 184252, upload-time = "2026-03-04T14:17:31.87Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/c5/6a852903d8bfac758c6dc6e9a68b015d3c33f2f1be5e9591e0f4b69c7e0a/opentelemetry_sdk-1.40.0-py3-none-any.whl", hash = "sha256:787d2154a71f4b3d81f20524a8ce061b7db667d24e46753f32a7bc48f1c1f3f1", size = 141951, upload-time = "2026-03-04T14:17:17.961Z" }, +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/c0/4ae7973f3c2cfd2b6e321f1675626f0dab0a97027cc7a297474c9c8f3d04/opentelemetry_semantic_conventions-0.61b0.tar.gz", hash = "sha256:072f65473c5d7c6dc0355b27d6c9d1a679d63b6d4b4b16a9773062cb7e31192a", size = 145755, upload-time = "2026-03-04T14:17:32.664Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/37/cc6a55e448deaa9b27377d087da8615a3416d8ad523d5960b78dbeadd02a/opentelemetry_semantic_conventions-0.61b0-py3-none-any.whl", hash = "sha256:fa530a96be229795f8cef353739b618148b0fe2b4b3f005e60e262926c4d38e2", size = 231621, upload-time = "2026-03-04T14:17:19.33Z" }, +] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "pandas" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "python-dateutil", marker = "sys_platform == 'linux'" }, + { name = "pytz", marker = "sys_platform == 'linux'" }, + { name = "tzdata", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, + { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, + { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, +] + +[[package]] +name = "pillow" +version = "12.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/42/5c74462b4fd957fcd7b13b04fb3205ff8349236ea74c7c375766d6c82288/pillow-12.1.1.tar.gz", hash = "sha256:9ad8fa5937ab05218e2b6a4cff30295ad35afd2f83ac592e68c0d871bb0fdbc4", size = 46980264, upload-time = "2026-02-11T04:23:07.146Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/2e/1001613d941c67442f745aff0f7cc66dd8df9a9c084eb497e6a543ee6f7e/pillow-12.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb66b7cc26f50977108790e2456b7921e773f23db5630261102233eb355a3b79", size = 6234995, upload-time = "2026-02-11T04:20:51.032Z" }, + { url = "https://files.pythonhosted.org/packages/07/26/246ab11455b2549b9233dbd44d358d033a2f780fa9007b61a913c5b2d24e/pillow-12.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aee2810642b2898bb187ced9b349e95d2a7272930796e022efaf12e99dccd293", size = 8045012, upload-time = "2026-02-11T04:20:52.882Z" }, + { url = "https://files.pythonhosted.org/packages/b2/8b/07587069c27be7535ac1fe33874e32de118fbd34e2a73b7f83436a88368c/pillow-12.1.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a0b1cd6232e2b618adcc54d9882e4e662a089d5768cd188f7c245b4c8c44a397", size = 6349638, upload-time = "2026-02-11T04:20:54.444Z" }, + { url = "https://files.pythonhosted.org/packages/ff/79/6df7b2ee763d619cda2fb4fea498e5f79d984dae304d45a8999b80d6cf5c/pillow-12.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7aac39bcf8d4770d089588a2e1dd111cbaa42df5a94be3114222057d68336bd0", size = 7041540, upload-time = "2026-02-11T04:20:55.97Z" }, + { url = "https://files.pythonhosted.org/packages/2c/5e/2ba19e7e7236d7529f4d873bdaf317a318896bac289abebd4bb00ef247f0/pillow-12.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ab174cd7d29a62dd139c44bf74b698039328f45cb03b4596c43473a46656b2f3", size = 6462613, upload-time = "2026-02-11T04:20:57.542Z" }, + { url = "https://files.pythonhosted.org/packages/03/03/31216ec124bb5c3dacd74ce8efff4cc7f52643653bad4825f8f08c697743/pillow-12.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:339ffdcb7cbeaa08221cd401d517d4b1fe7a9ed5d400e4a8039719238620ca35", size = 7166745, upload-time = "2026-02-11T04:20:59.196Z" }, +] + +[[package]] +name = "prettytable" +version = "3.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/45/b0847d88d6cfeb4413566738c8bbf1e1995fad3d42515327ff32cc1eb578/prettytable-3.17.0.tar.gz", hash = "sha256:59f2590776527f3c9e8cf9fe7b66dd215837cca96a9c39567414cbc632e8ddb0", size = 67892, upload-time = "2025-11-14T17:33:20.212Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl", hash = "sha256:aad69b294ddbe3e1f95ef8886a060ed1666a0b83018bbf56295f6f226c43d287", size = 34433, upload-time = "2025-11-14T17:33:19.093Z" }, +] + +[[package]] +name = "propcache" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, + { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, + { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, + { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, + { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, +] + +[[package]] +name = "protobuf" +version = "6.33.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/25/7c72c307aafc96fa87062aa6291d9f7c94836e43214d43722e86037aac02/protobuf-6.33.5.tar.gz", hash = "sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c", size = 444465, upload-time = "2026-01-29T21:51:33.494Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/b1/c79468184310de09d75095ed1314b839eb2f72df71097db9d1404a1b2717/protobuf-6.33.5-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:9b71e0281f36f179d00cbcb119cb19dec4d14a81393e5ea220f64b286173e190", size = 324638, upload-time = "2026-01-29T21:51:26.423Z" }, + { url = "https://files.pythonhosted.org/packages/c5/f5/65d838092fd01c44d16037953fd4c2cc851e783de9b8f02b27ec4ffd906f/protobuf-6.33.5-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8afa18e1d6d20af15b417e728e9f60f3aa108ee76f23c3b2c07a2c3b546d3afd", size = 339411, upload-time = "2026-01-29T21:51:27.446Z" }, + { url = "https://files.pythonhosted.org/packages/9b/53/a9443aa3ca9ba8724fdfa02dd1887c1bcd8e89556b715cfbacca6b63dbec/protobuf-6.33.5-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:cbf16ba3350fb7b889fca858fb215967792dc125b35c7976ca4818bee3521cf0", size = 323465, upload-time = "2026-01-29T21:51:28.925Z" }, + { url = "https://files.pythonhosted.org/packages/57/bf/2086963c69bdac3d7cff1cc7ff79b8ce5ea0bec6797a017e1be338a46248/protobuf-6.33.5-py3-none-any.whl", hash = "sha256:69915a973dd0f60f31a08b8318b73eab2bd6a392c79184b3612226b0a3f8ec02", size = 170687, upload-time = "2026-01-29T21:51:32.557Z" }, +] + +[[package]] +name = "pyarrow" +version = "23.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/22/134986a4cc224d593c1afde5494d18ff629393d74cc2eddb176669f234a4/pyarrow-23.0.1.tar.gz", hash = "sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019", size = 1167336, upload-time = "2026-02-16T10:14:12.39Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/7c/3d841c366620e906d54430817531b877ba646310296df42ef697308c2705/pyarrow-23.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9", size = 44470940, upload-time = "2026-02-16T10:10:10.704Z" }, + { url = "https://files.pythonhosted.org/packages/2c/a5/da83046273d990f256cb79796a190bbf7ec999269705ddc609403f8c6b06/pyarrow-23.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05", size = 47586063, upload-time = "2026-02-16T10:10:17.95Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/b7d2ebcff47a514f47f9da1e74b7949138c58cfeb108cdd4ee62f43f0cf3/pyarrow-23.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67", size = 48173045, upload-time = "2026-02-16T10:10:25.363Z" }, + { url = "https://files.pythonhosted.org/packages/43/b2/b40961262213beaba6acfc88698eb773dfce32ecdf34d19291db94c2bd73/pyarrow-23.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730", size = 50621741, upload-time = "2026-02-16T10:10:33.477Z" }, +] + +[[package]] +name = "pyasn1" +version = "0.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/b6/6e630dff89739fcd427e3f72b3d905ce0acb85a45d4ec3e2678718a3487f/pyasn1-0.6.2.tar.gz", hash = "sha256:9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b", size = 146586, upload-time = "2026-01-16T18:04:18.534Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/b5/a96872e5184f354da9c84ae119971a0a4c221fe9b27a4d94bd43f2596727/pyasn1-0.6.2-py3-none-any.whl", hash = "sha256:1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf", size = 83371, upload-time = "2026-01-16T18:04:17.174Z" }, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.12.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types", marker = "sys_platform == 'linux'" }, + { name = "pydantic-core", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, + { name = "typing-inspection", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.41.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, + { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, + { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, + { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, + { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, + { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, + { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, + { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, + { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, + { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, + { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pyogrio" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi", marker = "sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/d4/12f86b1ed09721363da4c09622464b604c851a9223fc0c6b393fb2012208/pyogrio-0.12.1.tar.gz", hash = "sha256:e548ab705bb3e5383693717de1e6c76da97f3762ab92522cb310f93128a75ff1", size = 303289, upload-time = "2025-11-28T19:04:53.341Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/ca/5368571a8b00b941ccfbe6ea29a5566aaffd45d4eb1553b956f7755af43e/pyogrio-0.12.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:81096a5139532de5a8003ef02b41d5d2444cb382a9aecd1165b447eb549180d3", size = 31417048, upload-time = "2025-11-28T19:03:32.572Z" }, + { url = "https://files.pythonhosted.org/packages/ef/85/6eeb875f27bf498d657eb5dab9f58e4c48b36c9037122787abee9a1ba4ba/pyogrio-0.12.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:41b78863f782f7a113ed0d36a5dc74d59735bd3a82af53510899bb02a18b06bb", size = 30952115, upload-time = "2025-11-28T19:03:35.332Z" }, + { url = "https://files.pythonhosted.org/packages/36/f7/cf8bec9024625947e1a71441906f60a5fa6f9e4c441c4428037e73b1fcc8/pyogrio-0.12.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:8b65be8c4258b27cc8f919b21929cecdadda4c353e3637fa30850339ef4d15c5", size = 32537246, upload-time = "2025-11-28T19:03:37.969Z" }, +] + +[[package]] +name = "pyparsing" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, +] + +[[package]] +name = "pyproj" +version = "3.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/90/67bd7260b4ea9b8b20b4f58afef6c223ecb3abf368eb4ec5bc2cdef81b49/pyproj-3.7.2.tar.gz", hash = "sha256:39a0cf1ecc7e282d1d30f36594ebd55c9fae1fda8a2622cee5d100430628f88c", size = 226279, upload-time = "2025-08-14T12:05:42.18Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/ac/14a78d17943898a93ef4f8c6a9d4169911c994e3161e54a7cedeba9d8dde/pyproj-3.7.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:c203101d1dc3c038a56cff0447acc515dd29d6e14811406ac539c21eed422b2a", size = 9667087, upload-time = "2025-08-14T12:04:13.964Z" }, + { url = "https://files.pythonhosted.org/packages/b8/be/212882c450bba74fc8d7d35cbd57e4af84792f0a56194819d98106b075af/pyproj-3.7.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:1edc34266c0c23ced85f95a1ee8b47c9035eae6aca5b6b340327250e8e281630", size = 9552797, upload-time = "2025-08-14T12:04:16.624Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c0/c0f25c87b5d2a8686341c53c1792a222a480d6c9caf60311fec12c99ec26/pyproj-3.7.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aa9f26c21bc0e2dc3d224cb1eb4020cf23e76af179a7c66fea49b828611e4260", size = 10837036, upload-time = "2025-08-14T12:04:18.733Z" }, + { url = "https://files.pythonhosted.org/packages/5d/37/5cbd6772addde2090c91113332623a86e8c7d583eccb2ad02ea634c4a89f/pyproj-3.7.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9428b318530625cb389b9ddc9c51251e172808a4af79b82809376daaeabe5e9", size = 10775952, upload-time = "2025-08-14T12:04:20.709Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + +[[package]] +name = "pytorch-lightning" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fsspec", extra = ["http"], marker = "sys_platform == 'linux'" }, + { name = "lightning-utilities", marker = "sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'linux'" }, + { name = "torch", marker = "sys_platform == 'linux'" }, + { name = "torchmetrics", marker = "sys_platform == 'linux'" }, + { name = "tqdm", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/ac/ebd5f6f58691cbd4f73836e43e1727f3814311b960c41f88e259606ca2b2/pytorch_lightning-2.6.1.tar.gz", hash = "sha256:ba08f8901cf226fcca473046ad9346f414e99117762dc869c76e650d5b3d7bdc", size = 665563, upload-time = "2026-01-30T14:59:11.636Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/93/c8c361bf0a2fe50f828f32def460e8b8a14b93955d3fd302b1a9b63b19e4/pytorch_lightning-2.6.1-py3-none-any.whl", hash = "sha256:1f8118567ec829e3055f16cf1aa320883a86a47c836951bfd9dcfa34ec7ffd59", size = 857273, upload-time = "2026-01-30T14:59:10.141Z" }, +] + +[[package]] +name = "pytz" +version = "2026.1.post1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/56/db/b8721d71d945e6a8ac63c0fc900b2067181dbb50805958d4d4661cf7d277/pytz-2026.1.post1.tar.gz", hash = "sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1", size = 321088, upload-time = "2026-03-03T07:47:50.683Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/99/781fe0c827be2742bcc775efefccb3b048a3a9c6ce9aec0cbf4a101677e5/pytz-2026.1.post1-py2.py3-none-any.whl", hash = "sha256:f2fd16142fda348286a75e1a524be810bb05d444e5a081f37f7affc635035f7a", size = 510489, upload-time = "2026-03-03T07:47:49.167Z" }, +] + +[[package]] +name = "pyvips" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2d/6a/282936de9faac6addf6bc8792c18e006489d0023ffd8856b8643f54d0558/pyvips-3.1.1.tar.gz", hash = "sha256:84fe744d023b1084ac2516bb17064cacd41c7f8aabf8e524dd383534941b9301", size = 56951, upload-time = "2025-12-09T18:38:06.355Z" } + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, +] + +[[package]] +name = "rasterio" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "affine", marker = "sys_platform == 'linux'" }, + { name = "attrs", marker = "sys_platform == 'linux'" }, + { name = "certifi", marker = "sys_platform == 'linux'" }, + { name = "click", marker = "sys_platform == 'linux'" }, + { name = "cligj", marker = "sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "pyparsing", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/88/edb4b66b6cb2c13f123af5a3896bf70c0cbe73ab3cd4243cb4eb0212a0f6/rasterio-1.5.0.tar.gz", hash = "sha256:1e0ea56b02eea4989b36edf8e58a5a3ef40e1b7edcb04def2603accd5ab3ee7b", size = 452184, upload-time = "2026-01-05T16:06:47.169Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/14/89645988424c40cbcb8334f94305ffe094dd28d85c643341d9690704c9f0/rasterio-1.5.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e14d07a09833b6df6024ce7a57aee1e1977b3aec682e30b1e58ce773462f2382", size = 36128020, upload-time = "2026-01-05T16:05:25.556Z" }, + { url = "https://files.pythonhosted.org/packages/85/23/5a52319a98451ff910f42e5f7f4804bfb39f9327933a89daab685d1ce2dd/rasterio-1.5.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:26dbcffcf0d01fc121cbb92186bc1cb78e16efe62b17be45ad7494446b325cf8", size = 37634010, upload-time = "2026-01-05T16:05:28.673Z" }, +] + +[[package]] +name = "rationai-masks" +version = "1.1.1" +source = { git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git#b85e26bca50eddcf916128e2ee4f31b28ea23d28" } +dependencies = [ + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "openslide-python", marker = "sys_platform == 'linux'" }, + { name = "pandas", marker = "sys_platform == 'linux'" }, + { name = "pillow", marker = "sys_platform == 'linux'" }, + { name = "pyvips", marker = "sys_platform == 'linux'" }, + { name = "ray", marker = "sys_platform == 'linux'" }, + { name = "torch", marker = "sys_platform == 'linux'" }, + { name = "tqdm", marker = "sys_platform == 'linux'" }, +] + +[[package]] +name = "rationai-mlkit" +version = "0.3.1" +source = { git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/mlkit.git#7ae830792550cb7dd027d8685badbba8af9013a0" } +dependencies = [ + { name = "datasets", marker = "sys_platform == 'linux'" }, + { name = "gitpython", marker = "sys_platform == 'linux'" }, + { name = "hydra-core", marker = "sys_platform == 'linux'" }, + { name = "lightning", marker = "sys_platform == 'linux'" }, + { name = "mlflow", marker = "sys_platform == 'linux'" }, + { name = "openslide-python", marker = "sys_platform == 'linux'" }, + { name = "pyarrow", marker = "sys_platform == 'linux'" }, + { name = "rationai-masks", marker = "sys_platform == 'linux'" }, + { name = "ratiopath", marker = "sys_platform == 'linux'" }, + { name = "torch", marker = "sys_platform == 'linux'" }, +] + +[[package]] +name = "rationai-staining" +version = "0.3.0" +source = { git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git#c48d216e910df11f3e6891aa071ebb8d04635727" } +dependencies = [ + { name = "albumentations", marker = "sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "pillow", marker = "sys_platform == 'linux'" }, + { name = "scikit-image", marker = "sys_platform == 'linux'" }, +] + +[[package]] +name = "rationai-tiling" +version = "1.1.1" +source = { git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git#475e7938e9a7230b378fca1c67106eda84f69c85" } +dependencies = [ + { name = "mlflow", marker = "sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "openslide-python", marker = "sys_platform == 'linux'" }, + { name = "pandas", marker = "sys_platform == 'linux'" }, + { name = "pyvips", marker = "sys_platform == 'linux'" }, + { name = "rationai-masks", marker = "sys_platform == 'linux'" }, + { name = "ray", marker = "sys_platform == 'linux'" }, + { name = "tqdm", marker = "sys_platform == 'linux'" }, +] + +[[package]] +name = "ratiopath" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "albumentations", marker = "sys_platform == 'linux'" }, + { name = "geopandas", marker = "sys_platform == 'linux'" }, + { name = "imagecodecs", marker = "sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "openslide-python", marker = "sys_platform == 'linux'" }, + { name = "pandas", marker = "sys_platform == 'linux'" }, + { name = "pillow", marker = "sys_platform == 'linux'" }, + { name = "pyvips", marker = "sys_platform == 'linux'" }, + { name = "rasterio", marker = "sys_platform == 'linux'" }, + { name = "ray", extra = ["data"], marker = "sys_platform == 'linux'" }, + { name = "scikit-image", marker = "sys_platform == 'linux'" }, + { name = "scikit-learn", marker = "sys_platform == 'linux'" }, + { name = "shapely", marker = "sys_platform == 'linux'" }, + { name = "tifffile", marker = "sys_platform == 'linux'" }, + { name = "torch", marker = "sys_platform == 'linux'" }, + { name = "zarr", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7f/6a/39a4ffd53123a82faa876a5f8149d2fb044016af8ffe0f272bb805feebd6/ratiopath-1.2.0.tar.gz", hash = "sha256:fd9d1d2cfac5e2bcd8b69b0bbd268892576ef190db9d6a4172ddfc2e143d8484", size = 31144, upload-time = "2026-03-05T11:44:11.334Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/51/411c5823930ed095d440a36464aab31690ac3905fc49591c008011534c69/ratiopath-1.2.0-py3-none-any.whl", hash = "sha256:4a6f7e8ab2354d8103cbf7c3c494b918cc5246ac41a83e4a2e7b90cb3a456df1", size = 37188, upload-time = "2026-03-05T11:44:10.195Z" }, +] + +[[package]] +name = "ray" +version = "2.54.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click", marker = "sys_platform == 'linux'" }, + { name = "filelock", marker = "sys_platform == 'linux'" }, + { name = "jsonschema", marker = "sys_platform == 'linux'" }, + { name = "msgpack", marker = "sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'linux'" }, + { name = "protobuf", marker = "sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'linux'" }, + { name = "requests", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/ad/e07aca3637e9c3ec4857ec4366208099cf8488ece8061a9925ba29b66382/ray-2.54.0-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:795ae21d6b764245d3f521bc5833446d58569e7dfde9c5777417eb285d87450f", size = 72107346, upload-time = "2026-02-18T04:05:27.999Z" }, + { url = "https://files.pythonhosted.org/packages/9e/b9/cc5ea8460c3dc602e6b7198277a7c59ba2b8929374ab22efa8df9f3deac8/ray-2.54.0-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:a972afd5aa3dda99d0b2f369b5f62e5dd95865ab7d37bf2e0a0e0d2cfbd9b325", size = 72967230, upload-time = "2026-02-18T04:05:33.771Z" }, +] + +[package.optional-dependencies] +data = [ + { name = "fsspec", marker = "sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "pandas", marker = "sys_platform == 'linux'" }, + { name = "pyarrow", marker = "sys_platform == 'linux'" }, +] + +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs", marker = "sys_platform == 'linux'" }, + { name = "rpds-py", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi", marker = "sys_platform == 'linux'" }, + { name = "charset-normalizer", marker = "sys_platform == 'linux'" }, + { name = "idna", marker = "sys_platform == 'linux'" }, + { name = "urllib3", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "rich" +version = "14.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py", marker = "sys_platform == 'linux'" }, + { name = "pygments", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, +] + +[[package]] +name = "rpds-py" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, + { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, + { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, + { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, + { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, + { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, + { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, +] + +[[package]] +name = "rsa" +version = "4.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, +] + +[[package]] +name = "scikit-image" +version = "0.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "imageio", marker = "sys_platform == 'linux'" }, + { name = "lazy-loader", marker = "sys_platform == 'linux'" }, + { name = "networkx", marker = "sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'linux'" }, + { name = "pillow", marker = "sys_platform == 'linux'" }, + { name = "scipy", marker = "sys_platform == 'linux'" }, + { name = "tifffile", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/b4/2528bb43c67d48053a7a649a9666432dc307d66ba02e3a6d5c40f46655df/scikit_image-0.26.0.tar.gz", hash = "sha256:f5f970ab04efad85c24714321fcc91613fcb64ef2a892a13167df2f3e59199fa", size = 22729739, upload-time = "2025-12-20T17:12:21.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/2b/c70120a6880579fb42b91567ad79feb4772f7be72e8d52fec403a3dde0c6/scikit_image-0.26.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c717490cec9e276afb0438dd165b7c3072d6c416709cc0f9f5a4c1070d23a44", size = 13084214, upload-time = "2025-12-20T17:10:57.468Z" }, + { url = "https://files.pythonhosted.org/packages/f4/a2/70401a107d6d7466d64b466927e6b96fcefa99d57494b972608e2f8be50f/scikit_image-0.26.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7df650e79031634ac90b11e64a9eedaf5a5e06fcd09bcd03a34be01745744466", size = 13561683, upload-time = "2025-12-20T17:10:59.49Z" }, + { url = "https://files.pythonhosted.org/packages/13/a5/48bdfd92794c5002d664e0910a349d0a1504671ef5ad358150f21643c79a/scikit_image-0.26.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cefd85033e66d4ea35b525bb0937d7f42d4cdcfed2d1888e1570d5ce450d3932", size = 14112147, upload-time = "2025-12-20T17:11:02.083Z" }, + { url = "https://files.pythonhosted.org/packages/ee/b5/ac71694da92f5def5953ca99f18a10fe98eac2dd0a34079389b70b4d0394/scikit_image-0.26.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3f5bf622d7c0435884e1e141ebbe4b2804e16b2dd23ae4c6183e2ea99233be70", size = 14661625, upload-time = "2025-12-20T17:11:04.528Z" }, +] + +[[package]] +name = "scikit-learn" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib", marker = "sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "scipy", marker = "sys_platform == 'linux'" }, + { name = "threadpoolctl", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/47/f187b4636ff80cc63f21cd40b7b2d177134acaa10f6bb73746130ee8c2e5/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4", size = 8660492, upload-time = "2025-12-10T07:07:55.574Z" }, + { url = "https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a", size = 8931904, upload-time = "2025-12-10T07:07:57.666Z" }, +] + +[[package]] +name = "scipy" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842, upload-time = "2026-02-23T00:18:35.367Z" }, + { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890, upload-time = "2026-02-23T00:18:49.188Z" }, + { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557, upload-time = "2026-02-23T00:18:54.74Z" }, + { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856, upload-time = "2026-02-23T00:19:00.307Z" }, +] + +[[package]] +name = "setuptools" +version = "82.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, +] + +[[package]] +name = "shapely" +version = "2.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/bc/0989043118a27cccb4e906a46b7565ce36ca7b57f5a18b78f4f1b0f72d9d/shapely-2.1.2.tar.gz", hash = "sha256:2ed4ecb28320a433db18a5bf029986aa8afcfd740745e78847e330d5d94922a9", size = 315489, upload-time = "2025-09-24T13:51:41.432Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/29/a5397e75b435b9895cd53e165083faed5d12fd9626eadec15a83a2411f0f/shapely-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0bd308103340030feef6c111d3eb98d50dc13feea33affc8a6f9fa549e9458a3", size = 2988308, upload-time = "2025-09-24T13:50:33.862Z" }, + { url = "https://files.pythonhosted.org/packages/b9/37/e781683abac55dde9771e086b790e554811a71ed0b2b8a1e789b7430dd44/shapely-2.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1e7d4d7ad262a48bb44277ca12c7c78cb1b0f56b32c10734ec9a1d30c0b0c54b", size = 3099844, upload-time = "2025-09-24T13:50:35.459Z" }, + { url = "https://files.pythonhosted.org/packages/d8/f3/9876b64d4a5a321b9dc482c92bb6f061f2fa42131cba643c699f39317cb9/shapely-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e9eddfe513096a71896441a7c37db72da0687b34752c4e193577a145c71736fc", size = 3988842, upload-time = "2025-09-24T13:50:37.478Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a0/704c7292f7014c7e74ec84eddb7b109e1fbae74a16deae9c1504b1d15565/shapely-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:980c777c612514c0cf99bc8a9de6d286f5e186dcaf9091252fcd444e5638193d", size = 4152714, upload-time = "2025-09-24T13:50:39.9Z" }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "simsimd" +version = "6.5.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/99/8c/070a179eb509b689509dacbd0bc81aa2e36614aff2c8aa6dc6c440886206/simsimd-6.5.16.tar.gz", hash = "sha256:0a005c6e2dacec83f235a747f7dbecca46b5d4d1e183ecc1929ca556ee7d7564", size = 187216, upload-time = "2026-03-07T14:36:23.191Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/3c/35266c8d128ea42706d9436b54994039e2659fb37ed28f1c62e123a86631/simsimd-6.5.16-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13b8af340ad5cc1311cae6f8d778aef80bff1922260dee1a17ca60878eaac466", size = 385042, upload-time = "2026-03-07T14:34:54.873Z" }, + { url = "https://files.pythonhosted.org/packages/3c/28/7ae846998728326759eab771afd83ad721b6c10e9cef7da2b5ca9bdd4a7b/simsimd-6.5.16-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12ae4f5f2ade1152d2d3a0094f56fae636204d40595b385ea9b304410647a353", size = 583515, upload-time = "2026-03-07T14:34:56.578Z" }, + { url = "https://files.pythonhosted.org/packages/ce/b9/3a5717c988b6093a5fb15484754f7ffe5451a7559f3c1d5f2b3183199441/simsimd-6.5.16-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97bcda199d4be8f4372af6b781e96e7e8cd1838ce256a83deef75ac660dcd464", size = 421418, upload-time = "2026-03-07T14:34:58.316Z" }, + { url = "https://files.pythonhosted.org/packages/bb/65/e218050eb89390c64ddc327f36da8e3b471483f11c0f3683c2bf891d2dab/simsimd-6.5.16-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a59ef1ab3d0f6d4f1dcac43e1b2db9b8e73c00e72714716e061bfd27dde2d652", size = 619558, upload-time = "2026-03-07T14:34:59.847Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "skops" +version = "0.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'linux'" }, + { name = "prettytable", marker = "sys_platform == 'linux'" }, + { name = "scikit-learn", marker = "sys_platform == 'linux'" }, + { name = "scipy", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/0c/5ec987633e077dd0076178ea6ade2d6e57780b34afea0b497fb507d7a1ed/skops-0.13.0.tar.gz", hash = "sha256:66949fd3c95cbb5c80270fbe40293c0fe1e46cb4a921860e42584dd9c20ebeb1", size = 581312, upload-time = "2025-08-06T09:48:14.916Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/e8/6a2b2030f0689f894432b9c2f0357f2f3286b2a00474827e04b8fe9eea13/skops-0.13.0-py3-none-any.whl", hash = "sha256:55e2cccb18c86f5916e4cfe5acf55ed7b0eecddf08a151906414c092fa5926dc", size = 131200, upload-time = "2025-08-06T09:48:13.356Z" }, +] + +[[package]] +name = "smmap" +version = "5.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/ea/49c993d6dfdd7338c9b1000a0f36817ed7ec84577ae2e52f890d1a4ff909/smmap-5.0.3.tar.gz", hash = "sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c", size = 22506, upload-time = "2026-03-09T03:43:26.1Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl", hash = "sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f", size = 24390, upload-time = "2026-03-09T03:43:24.361Z" }, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.48" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet", marker = "(platform_machine == 'AMD64' and sys_platform == 'linux') or (platform_machine == 'WIN32' and sys_platform == 'linux') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'amd64' and sys_platform == 'linux') or (platform_machine == 'ppc64le' and sys_platform == 'linux') or (platform_machine == 'win32' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1f/73/b4a9737255583b5fa858e0bb8e116eb94b88c910164ed2ed719147bde3de/sqlalchemy-2.0.48.tar.gz", hash = "sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7", size = 9886075, upload-time = "2026-03-02T15:28:51.474Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/52/f75f516a1f3888f027c1cfb5d22d4376f4b46236f2e8669dcb0cddc60275/sqlalchemy-2.0.48-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb", size = 3337020, upload-time = "2026-03-02T15:50:34.547Z" }, + { url = "https://files.pythonhosted.org/packages/37/9a/0c28b6371e0cdcb14f8f1930778cb3123acfcbd2c95bb9cf6b4a2ba0cce3/sqlalchemy-2.0.48-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894", size = 3349983, upload-time = "2026-03-02T15:53:25.542Z" }, + { url = "https://files.pythonhosted.org/packages/1c/46/0aee8f3ff20b1dcbceb46ca2d87fcc3d48b407925a383ff668218509d132/sqlalchemy-2.0.48-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9", size = 3279690, upload-time = "2026-03-02T15:50:36.277Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8c/a957bc91293b49181350bfd55e6dfc6e30b7f7d83dc6792d72043274a390/sqlalchemy-2.0.48-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e", size = 3314738, upload-time = "2026-03-02T15:53:27.519Z" }, + { url = "https://files.pythonhosted.org/packages/46/2c/9664130905f03db57961b8980b05cab624afd114bf2be2576628a9f22da4/sqlalchemy-2.0.48-py3-none-any.whl", hash = "sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096", size = 1940202, upload-time = "2026-03-02T15:52:43.285Z" }, +] + +[[package]] +name = "sqlparse" +version = "0.5.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" }, +] + +[[package]] +name = "stain-normalization" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "albumentations", marker = "sys_platform == 'linux'" }, + { name = "lightning", marker = "sys_platform == 'linux'" }, + { name = "openslide-bin", marker = "sys_platform == 'linux'" }, + { name = "rationai-masks", marker = "sys_platform == 'linux'" }, + { name = "rationai-mlkit", marker = "sys_platform == 'linux'" }, + { name = "rationai-staining", marker = "sys_platform == 'linux'" }, + { name = "rationai-tiling", marker = "sys_platform == 'linux'" }, + { name = "scikit-image", marker = "sys_platform == 'linux'" }, + { name = "torch", marker = "sys_platform == 'linux'" }, + { name = "torchmetrics", marker = "sys_platform == 'linux'" }, + { name = "torchvision", marker = "sys_platform == 'linux'" }, + { name = "tqdm", marker = "sys_platform == 'linux'" }, +] + +[package.metadata] +requires-dist = [ + { name = "albumentations", specifier = ">=1.4.14" }, + { name = "lightning", specifier = ">=2.0.0" }, + { name = "openslide-bin", specifier = ">=4.0.0.6" }, + { name = "rationai-masks", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git" }, + { name = "rationai-mlkit", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/mlkit.git" }, + { name = "rationai-staining", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git" }, + { name = "rationai-tiling", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git" }, + { name = "scikit-image", specifier = ">=0.25.2" }, + { name = "torch", specifier = ">=2.2.2" }, + { name = "torchmetrics", specifier = ">=1.4.14" }, + { name = "torchvision", specifier = ">=0.17.2" }, + { name = "tqdm", specifier = ">=4.66.5" }, +] + +[[package]] +name = "starlette" +version = "0.52.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, +] + +[[package]] +name = "stringzilla" +version = "4.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/61/68/475518f6f4af8273ecd619a5d37d715d36908973f9970faf21571a296821/stringzilla-4.6.0.tar.gz", hash = "sha256:640c0fb5b6a2ad77b7721bff98f00a3c524ca60dc202f552e486831a751d4bbd", size = 646335, upload-time = "2025-12-26T23:44:43.956Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/ca/cb61f293a919fa04bb1b7a2672ea35c69df273fe5aa66410646c50bfc948/stringzilla-4.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83a7545b65c8ebc4182d4a6e494995affd4a43871e7cc89d951aa56854a576fb", size = 689232, upload-time = "2025-12-26T23:43:10.923Z" }, + { url = "https://files.pythonhosted.org/packages/20/e3/ec0e5332975a213ef3456acd18d86de076d86026043b7aa3ccba23cec33d/stringzilla-4.6.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5cff924b1ff38cacd05bb9426675dbbd4ce9c12354978fb33922e316a5be4f8c", size = 657168, upload-time = "2025-12-26T23:43:12.733Z" }, + { url = "https://files.pythonhosted.org/packages/41/70/34af64c767656c23b8d0efdd5a783124f3b1067ff57992640f38f439e109/stringzilla-4.6.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cf83c6c1eabed2704cfb884c8f07ddaebd8d7cddcfef6a4bf76c1a6cad381b2f", size = 640477, upload-time = "2025-12-26T23:43:14.012Z" }, + { url = "https://files.pythonhosted.org/packages/f2/c8/fd7101beb8268d231afe3beb20038b17543ab7ea2a59f56eded1782e420f/stringzilla-4.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d01f2bc4e0ee2d1c444ece06b18016f00e16f7b50491fe81984fa9ac584caa5", size = 2053831, upload-time = "2025-12-26T23:43:15.717Z" }, + { url = "https://files.pythonhosted.org/packages/0f/31/6e3734d402a17077d91c9ff0bebba6168ac98a71baa06c555521e38cc938/stringzilla-4.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78b16030d3c554860ec1738844bc8f06a278c76669957287e42195fe0fd4cfb1", size = 643989, upload-time = "2025-12-26T23:43:17.217Z" }, + { url = "https://files.pythonhosted.org/packages/07/c2/b5951bd07abe255f5e3018b52ccf3a31d166e6c91934fba6d7210b9efa3c/stringzilla-4.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97062003478a73c5263b1c91cc28a2147469fccf3c9d8c042e4394b8af261446", size = 653523, upload-time = "2025-12-26T23:43:18.444Z" }, + { url = "https://files.pythonhosted.org/packages/60/a6/af547d8b2695f93f08b324a9dde6e9bed07a1bbf7f9123427dfc48458a08/stringzilla-4.6.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:07db5994130efc77b93ff44982f37b8cd1bffaa6d51033b0d7d37ceb211446d2", size = 586796, upload-time = "2025-12-26T23:43:19.736Z" }, + { url = "https://files.pythonhosted.org/packages/f4/c0/d9bc41fab3b1261352a80bc8f9ef189c246817bcc66af17b6a6fca27c7fc/stringzilla-4.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f2c706cb6cd522d406874688aaef964eb60e8b24ab027bd7c21fa129f6308a6", size = 626102, upload-time = "2025-12-26T23:43:20.999Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c2/df732da9f8cafebfb58db0d9167211c09143f3858d259d1d7ecb4d66f87b/stringzilla-4.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5b14cca3091fb50f56710b3cc5f95830f6f64a44d72739b3dabff2052722dd45", size = 618774, upload-time = "2025-12-26T23:43:22.528Z" }, + { url = "https://files.pythonhosted.org/packages/f8/a1/5102428c3285d30d8ae647168450749be3ff0309ac34ae71c8eb72ebb420/stringzilla-4.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:88817ba207dc28412ed3e521d0fc4925e86e240f3dc34c5fff62808b01936f1c", size = 613122, upload-time = "2025-12-26T23:43:23.808Z" }, + { url = "https://files.pythonhosted.org/packages/13/9b/45714783635d0b13caf2775d2a7a71f05874077ae08c4cbd3aba56e015c6/stringzilla-4.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d0fef232f918fa93b1b8b64e9c06831cfbe00aef5289c3d4792f219a0f2947a", size = 1909235, upload-time = "2025-12-26T23:43:25.056Z" }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + +[[package]] +name = "threadpoolctl" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, +] + +[[package]] +name = "tifffile" +version = "2026.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/cb/2f6d79c7576e22c116352a801f4c3c8ace5957e9aced862012430b62e14f/tifffile-2026.3.3.tar.gz", hash = "sha256:d9a1266bed6f2ee1dd0abde2018a38b4f8b2935cb843df381d70ac4eac5458b7", size = 388745, upload-time = "2026-03-03T19:14:38.134Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/e4/e804505f87627cd8cdae9c010c47c4485fd8c1ce31a7dd0ab7fcc4707377/tifffile-2026.3.3-py3-none-any.whl", hash = "sha256:e8be15c94273113d31ecb7aa3a39822189dd11c4967e3cc88c178f1ad2fd1170", size = 243960, upload-time = "2026-03-03T19:14:35.808Z" }, +] + +[[package]] +name = "torch" +version = "2.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-bindings", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "filelock", marker = "sys_platform == 'linux'" }, + { name = "fsspec", marker = "sys_platform == 'linux'" }, + { name = "jinja2", marker = "sys_platform == 'linux'" }, + { name = "networkx", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools", marker = "sys_platform == 'linux'" }, + { name = "sympy", marker = "sys_platform == 'linux'" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/af/758e242e9102e9988969b5e621d41f36b8f258bb4a099109b7a4b4b50ea4/torch-2.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5fd4117d89ffd47e3dcc71e71a22efac24828ad781c7e46aaaf56bf7f2796acf", size = 145996088, upload-time = "2026-01-21T16:24:44.171Z" }, + { url = "https://files.pythonhosted.org/packages/23/8e/3c74db5e53bff7ed9e34c8123e6a8bfef718b2450c35eefab85bb4a7e270/torch-2.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:787124e7db3b379d4f1ed54dd12ae7c741c16a4d29b49c0226a89bea50923ffb", size = 915711952, upload-time = "2026-01-21T16:23:53.503Z" }, +] + +[[package]] +name = "torchmetrics" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "lightning-utilities", marker = "sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'linux'" }, + { name = "torch", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/34/39b8b749333db56c0585d7a11fa62a283c087bb1dfc897d69fb8cedbefb1/torchmetrics-1.9.0.tar.gz", hash = "sha256:a488609948600df52d3db4fcdab02e62aab2a85ef34da67037dc3e65b8512faa", size = 581765, upload-time = "2026-03-09T17:41:22.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/a2/c7f6ebf546f8f644edf0f999aa98ece106986a77a7b922316bf6414ff825/torchmetrics-1.9.0-py3-none-any.whl", hash = "sha256:bfdcbff3dd1d96b3374bb2496eb39f23c4b28b8a845b6a18c313688e0d2d9ca1", size = 983384, upload-time = "2026-03-09T17:41:19.756Z" }, +] + +[[package]] +name = "torchvision" +version = "0.25.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "pillow", marker = "sys_platform == 'linux'" }, + { name = "torch", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/f8/c0e1ef27c66e15406fece94930e7d6feee4cb6374bbc02d945a630d6426e/torchvision-0.25.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b75deafa2dfea3e2c2a525559b04783515e3463f6e830cb71de0fb7ea36fe233", size = 2344556, upload-time = "2026-01-21T16:27:40.125Z" }, + { url = "https://files.pythonhosted.org/packages/68/2f/f24b039169db474e8688f649377de082a965fbf85daf4e46c44412f1d15a/torchvision-0.25.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f25aa9e380865b11ea6e9d99d84df86b9cc959f1a007cd966fc6f1ab2ed0e248", size = 8072351, upload-time = "2026-01-21T16:27:21.074Z" }, +] + +[[package]] +name = "tqdm" +version = "4.67.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, +] + +[[package]] +name = "triton" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/a8/cdf8b3e4c98132f965f88c2313a4b493266832ad47fb52f23d14d4f86bb5/triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca", size = 188266850, upload-time = "2026-01-20T16:00:43.041Z" }, +] + +[[package]] +name = "typer" +version = "0.24.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc", marker = "sys_platform == 'linux'" }, + { name = "click", marker = "sys_platform == 'linux'" }, + { name = "rich", marker = "sys_platform == 'linux'" }, + { name = "shellingham", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613, upload-time = "2026-02-21T16:54:40.609Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085, upload-time = "2026-02-21T16:54:41.616Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "tzdata" +version = "2025.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.41.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click", marker = "sys_platform == 'linux'" }, + { name = "h11", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/32/ce/eeb58ae4ac36fe09e3842eb02e0eb676bf2c53ae062b98f1b2531673efdd/uvicorn-0.41.0.tar.gz", hash = "sha256:09d11cf7008da33113824ee5a1c6422d89fbc2ff476540d69a34c87fab8b571a", size = 82633, upload-time = "2026-02-16T23:07:24.1Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/e4/d04a086285c20886c0daad0e026f250869201013d18f81d9ff5eada73a88/uvicorn-0.41.0-py3-none-any.whl", hash = "sha256:29e35b1d2c36a04b9e180d4007ede3bcb32a85fbdfd6c6aeb3f26839de088187", size = 68783, upload-time = "2026-02-16T23:07:22.357Z" }, +] + +[[package]] +name = "wcwidth" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684, upload-time = "2026-02-06T19:19:40.919Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" }, +] + +[[package]] +name = "werkzeug" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/f1/ee81806690a87dab5f5653c1f146c92bc066d7f4cebc603ef88eb9e13957/werkzeug-3.1.6.tar.gz", hash = "sha256:210c6bede5a420a913956b4791a7f4d6843a43b6fcee4dfa08a65e93007d0d25", size = 864736, upload-time = "2026-02-19T15:17:18.884Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/ec/d58832f89ede95652fd01f4f24236af7d32b70cab2196dfcc2d2fd13c5c2/werkzeug-3.1.6-py3-none-any.whl", hash = "sha256:7ddf3357bb9564e407607f988f683d72038551200c704012bb9a4c523d42f131", size = 225166, upload-time = "2026-02-19T15:17:17.475Z" }, +] + +[[package]] +name = "xxhash" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160, upload-time = "2025-10-02T14:37:08.097Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/f2/57eb99aa0f7d98624c0932c5b9a170e1806406cdbcdb510546634a1359e0/xxhash-3.6.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:dc94790144e66b14f67b10ac8ed75b39ca47536bf8800eb7c24b50271ea0c490", size = 194035, upload-time = "2025-10-02T14:34:37.354Z" }, + { url = "https://files.pythonhosted.org/packages/4c/ed/6224ba353690d73af7a3f1c7cdb1fc1b002e38f783cb991ae338e1eb3d79/xxhash-3.6.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93f107c673bccf0d592cdba077dedaf52fe7f42dcd7676eba1f6d6f0c3efffd2", size = 212914, upload-time = "2025-10-02T14:34:38.6Z" }, + { url = "https://files.pythonhosted.org/packages/38/86/fb6b6130d8dd6b8942cc17ab4d90e223653a89aa32ad2776f8af7064ed13/xxhash-3.6.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aa5ee3444c25b69813663c9f8067dcfaa2e126dc55e8dddf40f4d1c25d7effa", size = 212163, upload-time = "2025-10-02T14:34:39.872Z" }, + { url = "https://files.pythonhosted.org/packages/ee/dc/e84875682b0593e884ad73b2d40767b5790d417bde603cceb6878901d647/xxhash-3.6.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7f99123f0e1194fa59cc69ad46dbae2e07becec5df50a0509a808f90a0f03f0", size = 445411, upload-time = "2025-10-02T14:34:41.569Z" }, + { url = "https://files.pythonhosted.org/packages/11/4f/426f91b96701ec2f37bb2b8cec664eff4f658a11f3fa9d94f0a887ea6d2b/xxhash-3.6.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49e03e6fe2cac4a1bc64952dd250cf0dbc5ef4ebb7b8d96bce82e2de163c82a2", size = 193883, upload-time = "2025-10-02T14:34:43.249Z" }, + { url = "https://files.pythonhosted.org/packages/53/5a/ddbb83eee8e28b778eacfc5a85c969673e4023cdeedcfcef61f36731610b/xxhash-3.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bd17fede52a17a4f9a7bc4472a5867cb0b160deeb431795c0e4abe158bc784e9", size = 210392, upload-time = "2025-10-02T14:34:45.042Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c2/ff69efd07c8c074ccdf0a4f36fcdd3d27363665bcdf4ba399abebe643465/xxhash-3.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6fb5f5476bef678f69db04f2bd1efbed3030d2aba305b0fc1773645f187d6a4e", size = 197898, upload-time = "2025-10-02T14:34:46.302Z" }, + { url = "https://files.pythonhosted.org/packages/58/ca/faa05ac19b3b622c7c9317ac3e23954187516298a091eb02c976d0d3dd45/xxhash-3.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:843b52f6d88071f87eba1631b684fcb4b2068cd2180a0224122fe4ef011a9374", size = 210655, upload-time = "2025-10-02T14:34:47.571Z" }, + { url = "https://files.pythonhosted.org/packages/d4/7a/06aa7482345480cc0cb597f5c875b11a82c3953f534394f620b0be2f700c/xxhash-3.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7d14a6cfaf03b1b6f5f9790f76880601ccc7896aff7ab9cd8978a939c1eb7e0d", size = 414001, upload-time = "2025-10-02T14:34:49.273Z" }, + { url = "https://files.pythonhosted.org/packages/23/07/63ffb386cd47029aa2916b3d2f454e6cc5b9f5c5ada3790377d5430084e7/xxhash-3.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:418daf3db71e1413cfe211c2f9a528456936645c17f46b5204705581a45390ae", size = 191431, upload-time = "2025-10-02T14:34:50.798Z" }, +] + +[[package]] +name = "yarl" +version = "1.23.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna", marker = "sys_platform == 'linux'" }, + { name = "multidict", marker = "sys_platform == 'linux'" }, + { name = "propcache", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/30/58260ed98e6ff7f90ba84442c1ddd758c9170d70327394a6227b310cd60f/yarl-1.23.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8", size = 97587, upload-time = "2026-03-01T22:05:17.384Z" }, + { url = "https://files.pythonhosted.org/packages/76/0a/8b08aac08b50682e65759f7f8dde98ae8168f72487e7357a5d684c581ef9/yarl-1.23.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072", size = 92528, upload-time = "2026-03-01T22:05:18.804Z" }, + { url = "https://files.pythonhosted.org/packages/52/07/0b7179101fe5f8385ec6c6bb5d0cb9f76bd9fb4a769591ab6fb5cdbfc69a/yarl-1.23.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8", size = 105339, upload-time = "2026-03-01T22:05:20.235Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8a/36d82869ab5ec829ca8574dfcb92b51286fcfb1e9c7a73659616362dc880/yarl-1.23.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7", size = 105061, upload-time = "2026-03-01T22:05:22.268Z" }, + { url = "https://files.pythonhosted.org/packages/66/3e/868e5c3364b6cee19ff3e1a122194fa4ce51def02c61023970442162859e/yarl-1.23.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51", size = 100132, upload-time = "2026-03-01T22:05:23.638Z" }, + { url = "https://files.pythonhosted.org/packages/cf/26/9c89acf82f08a52cb52d6d39454f8d18af15f9d386a23795389d1d423823/yarl-1.23.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67", size = 99289, upload-time = "2026-03-01T22:05:25.749Z" }, + { url = "https://files.pythonhosted.org/packages/6f/54/5b0db00d2cb056922356104468019c0a132e89c8d3ab67d8ede9f4483d2a/yarl-1.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7", size = 96950, upload-time = "2026-03-01T22:05:27.318Z" }, + { url = "https://files.pythonhosted.org/packages/f6/40/10fa93811fd439341fad7e0718a86aca0de9548023bbb403668d6555acab/yarl-1.23.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d", size = 93960, upload-time = "2026-03-01T22:05:28.738Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d2/8ae2e6cd77d0805f4526e30ec43b6f9a3dfc542d401ac4990d178e4bf0cf/yarl-1.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760", size = 104703, upload-time = "2026-03-01T22:05:30.438Z" }, + { url = "https://files.pythonhosted.org/packages/2f/0c/b3ceacf82c3fe21183ce35fa2acf5320af003d52bc1fcf5915077681142e/yarl-1.23.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2", size = 98325, upload-time = "2026-03-01T22:05:31.835Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e0/12900edd28bdab91a69bd2554b85ad7b151f64e8b521fe16f9ad2f56477a/yarl-1.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86", size = 105067, upload-time = "2026-03-01T22:05:33.358Z" }, + { url = "https://files.pythonhosted.org/packages/15/61/74bb1182cf79c9bbe4eb6b1f14a57a22d7a0be5e9cedf8e2d5c2086474c3/yarl-1.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34", size = 100285, upload-time = "2026-03-01T22:05:35.4Z" }, + { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, +] + +[[package]] +name = "zarr" +version = "3.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "donfig", marker = "sys_platform == 'linux'" }, + { name = "google-crc32c", marker = "sys_platform == 'linux'" }, + { name = "numcodecs", marker = "sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/76/7fa87f57c112c7b9c82f0a730f8b6f333e792574812872e2cd45ab604199/zarr-3.1.5.tar.gz", hash = "sha256:fbe0c79675a40c996de7ca08e80a1c0a20537bd4a9f43418b6d101395c0bba2b", size = 366825, upload-time = "2025-11-21T14:06:01.492Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/15/bb13b4913ef95ad5448490821eee4671d0e67673342e4d4070854e5fe081/zarr-3.1.5-py3-none-any.whl", hash = "sha256:29cd905afb6235b94c09decda4258c888fcb79bb6c862ef7c0b8fe009b5c8563", size = 284067, upload-time = "2025-11-21T14:05:59.235Z" }, +] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, +] From 574a0276201e6f7d668a8223532e9a1fec13e619 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni.cz> Date: Wed, 11 Mar 2026 07:11:14 +0100 Subject: [PATCH 83/85] chore: add mypy to uv --- pyproject.toml | 2 +- uv.lock | 175 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4acf72a..231c39a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ dependencies = [ "rationai-staining @ git+https://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git", ] -[tool.pdm.dev-dependencies] +[dependency-groups] dev = ["mypy", "pre-commit", "ruff"] [tool.pdm.scripts] diff --git a/uv.lock b/uv.lock index b1e9be2..9f50c1d 100644 --- a/uv.lock +++ b/uv.lock @@ -208,6 +208,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, ] +[[package]] +name = "cfgv" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, +] + [[package]] name = "charset-normalizer" version = "3.4.5" @@ -385,6 +394,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668, upload-time = "2025-04-16T00:41:47.671Z" }, ] +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + [[package]] name = "docker" version = "7.1.0" @@ -729,6 +747,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c6/50/e0edd38dcd63fb26a8547f13d28f7a008bc4a3fd4eb4ff030673f22ad41a/hydra_core-1.3.2-py3-none-any.whl", hash = "sha256:fa0238a9e31df3373b35b0bfb672c34cc92718d21f81311d8996a16de1141d8b", size = 154547, upload-time = "2023-02-23T18:33:40.801Z" }, ] +[[package]] +name = "identify" +version = "2.6.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/84/376a3b96e5a8d33a7aa2c5b3b31a4b3c364117184bf0b17418055f6ace66/identify-2.6.17.tar.gz", hash = "sha256:f816b0b596b204c9fdf076ded172322f2723cf958d02f9c3587504834c8ff04d", size = 99579, upload-time = "2026-03-01T20:04:12.702Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl", hash = "sha256:be5f8412d5ed4b20f2bd41a65f920990bdccaa6a4a18a08f1eefdcd0bdd885f0", size = 99382, upload-time = "2026-03-01T20:04:11.439Z" }, +] + [[package]] name = "idna" version = "3.11" @@ -864,6 +891,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl", hash = "sha256:ab0ea149e9c554d4ffeeb21105ac60bed7f3b4fd69b1d2360a4add51b170b005", size = 8044, upload-time = "2026-03-06T15:45:07.668Z" }, ] +[[package]] +name = "librt" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/56/9c/b4b0c54d84da4a94b37bd44151e46d5e583c9534c7e02250b961b1b6d8a8/librt-0.8.1.tar.gz", hash = "sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73", size = 177471, upload-time = "2026-02-17T16:13:06.101Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/33/c510de7f93bf1fa19e13423a606d8189a02624a800710f6e6a0a0f0784b3/librt-0.8.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb", size = 198941, upload-time = "2026-02-17T16:11:44.28Z" }, + { url = "https://files.pythonhosted.org/packages/dd/36/e725903416409a533d92398e88ce665476f275081d0d7d42f9c4951999e5/librt-0.8.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d", size = 209991, upload-time = "2026-02-17T16:11:45.462Z" }, + { url = "https://files.pythonhosted.org/packages/30/7a/8d908a152e1875c9f8eac96c97a480df425e657cdb47854b9efaa4998889/librt-0.8.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7", size = 224476, upload-time = "2026-02-17T16:11:46.542Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b8/a22c34f2c485b8903a06f3fe3315341fe6876ef3599792344669db98fcff/librt-0.8.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440", size = 217518, upload-time = "2026-02-17T16:11:47.746Z" }, + { url = "https://files.pythonhosted.org/packages/79/6f/5c6fea00357e4f82ba44f81dbfb027921f1ab10e320d4a64e1c408d035d9/librt-0.8.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9", size = 225116, upload-time = "2026-02-17T16:11:49.298Z" }, + { url = "https://files.pythonhosted.org/packages/f2/a0/95ced4e7b1267fe1e2720a111685bcddf0e781f7e9e0ce59d751c44dcfe5/librt-0.8.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972", size = 217751, upload-time = "2026-02-17T16:11:50.49Z" }, + { url = "https://files.pythonhosted.org/packages/93/c2/0517281cb4d4101c27ab59472924e67f55e375bc46bedae94ac6dc6e1902/librt-0.8.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921", size = 218378, upload-time = "2026-02-17T16:11:51.783Z" }, + { url = "https://files.pythonhosted.org/packages/43/e8/37b3ac108e8976888e559a7b227d0ceac03c384cfd3e7a1c2ee248dbae79/librt-0.8.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0", size = 241199, upload-time = "2026-02-17T16:11:53.561Z" }, +] + [[package]] name = "lightning" version = "2.6.1" @@ -1102,6 +1145,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6c/28/dd72947e59a6a8c856448a5e74da6201cb5502ddff644fbc790e4bd40b9a/multiprocess-0.70.18-py39-none-any.whl", hash = "sha256:e78ca805a72b1b810c690b6b4cc32579eba34f403094bbbae962b7b5bf9dfcb8", size = 133478, upload-time = "2025-04-17T03:11:26.253Z" }, ] +[[package]] +name = "mypy" +version = "1.19.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "librt", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'linux'" }, + { name = "mypy-extensions", marker = "sys_platform == 'linux'" }, + { name = "pathspec", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, + { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, + { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + [[package]] name = "networkx" version = "3.6.1" @@ -1111,6 +1181,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, ] +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + [[package]] name = "numcodecs" version = "0.16.5" @@ -1400,6 +1479,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, ] +[[package]] +name = "pathspec" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, +] + [[package]] name = "pillow" version = "12.1.1" @@ -1414,6 +1502,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/03/03/31216ec124bb5c3dacd74ce8efff4cc7f52643653bad4825f8f08c697743/pillow-12.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:339ffdcb7cbeaa08221cd401d517d4b1fe7a9ed5d400e4a8039719238620ca35", size = 7166745, upload-time = "2026-02-11T04:20:59.196Z" }, ] +[[package]] +name = "platformdirs" +version = "4.9.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, +] + +[[package]] +name = "pre-commit" +version = "4.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv", marker = "sys_platform == 'linux'" }, + { name = "identify", marker = "sys_platform == 'linux'" }, + { name = "nodeenv", marker = "sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'linux'" }, + { name = "virtualenv", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, +] + [[package]] name = "prettytable" version = "3.17.0" @@ -1596,6 +1709,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] +[[package]] +name = "python-discovery" +version = "1.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock", marker = "sys_platform == 'linux'" }, + { name = "platformdirs", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/7e/9f3b0dd3a074a6c3e1e79f35e465b1f2ee4b262d619de00cfce523cc9b24/python_discovery-1.1.3.tar.gz", hash = "sha256:7acca36e818cd88e9b2ba03e045ad7e93e1713e29c6bbfba5d90202310b7baa5", size = 56945, upload-time = "2026-03-10T15:08:15.038Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/80/73211fc5bfbfc562369b4aa61dc1e4bf07dc7b34df7b317e4539316b809c/python_discovery-1.1.3-py3-none-any.whl", hash = "sha256:90e795f0121bc84572e737c9aa9966311b9fde44ffb88a5953b3ec9b31c6945e", size = 31485, upload-time = "2026-03-10T15:08:13.06Z" }, +] + [[package]] name = "python-dotenv" version = "1.2.2" @@ -1858,6 +1984,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, ] +[[package]] +name = "ruff" +version = "0.15.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/77/9b/840e0039e65fcf12758adf684d2289024d6140cde9268cc59887dc55189c/ruff-0.15.5.tar.gz", hash = "sha256:7c3601d3b6d76dce18c5c824fc8d06f4eef33d6df0c21ec7799510cde0f159a2", size = 4574214, upload-time = "2026-03-05T20:06:34.946Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/20/5369c3ce21588c708bcbe517a8fbe1a8dfdb5dfd5137e14790b1da71612c/ruff-0.15.5-py3-none-linux_armv6l.whl", hash = "sha256:4ae44c42281f42e3b06b988e442d344a5b9b72450ff3c892e30d11b29a96a57c", size = 10478185, upload-time = "2026-03-05T20:06:29.093Z" }, + { url = "https://files.pythonhosted.org/packages/66/0e/ba49e2c3fa0395b3152bad634c7432f7edfc509c133b8f4529053ff024fb/ruff-0.15.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba786a8295c6574c1116704cf0b9e6563de3432ac888d8f83685654fe528fd65", size = 10534857, upload-time = "2026-03-05T20:06:19.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/71/39234440f27a226475a0659561adb0d784b4d247dfe7f43ffc12dd02e288/ruff-0.15.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd4b801e57955fe9f02b31d20375ab3a5c4415f2e5105b79fb94cf2642c91440", size = 10309120, upload-time = "2026-03-05T20:06:00.435Z" }, + { url = "https://files.pythonhosted.org/packages/f5/87/4140aa86a93df032156982b726f4952aaec4a883bb98cb6ef73c347da253/ruff-0.15.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391f7c73388f3d8c11b794dbbc2959a5b5afe66642c142a6effa90b45f6f5204", size = 11047428, upload-time = "2026-03-05T20:05:51.867Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f7/4953e7e3287676f78fbe85e3a0ca414c5ca81237b7575bdadc00229ac240/ruff-0.15.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc18f30302e379fe1e998548b0f5e9f4dff907f52f73ad6da419ea9c19d66c8", size = 11914251, upload-time = "2026-03-05T20:06:22.887Z" }, + { url = "https://files.pythonhosted.org/packages/77/46/0f7c865c10cf896ccf5a939c3e84e1cfaeed608ff5249584799a74d33835/ruff-0.15.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc6e7f90087e2d27f98dc34ed1b3ab7c8f0d273cc5431415454e22c0bd2a681", size = 11333801, upload-time = "2026-03-05T20:05:57.168Z" }, + { url = "https://files.pythonhosted.org/packages/d3/01/a10fe54b653061585e655f5286c2662ebddb68831ed3eaebfb0eb08c0a16/ruff-0.15.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1cb7169f53c1ddb06e71a9aebd7e98fc0fea936b39afb36d8e86d36ecc2636a", size = 11206821, upload-time = "2026-03-05T20:06:03.441Z" }, + { url = "https://files.pythonhosted.org/packages/7a/0d/2132ceaf20c5e8699aa83da2706ecb5c5dcdf78b453f77edca7fb70f8a93/ruff-0.15.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9b037924500a31ee17389b5c8c4d88874cc6ea8e42f12e9c61a3d754ff72f1ca", size = 11133326, upload-time = "2026-03-05T20:06:25.655Z" }, + { url = "https://files.pythonhosted.org/packages/72/cb/2e5259a7eb2a0f87c08c0fe5bf5825a1e4b90883a52685524596bfc93072/ruff-0.15.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:65bb414e5b4eadd95a8c1e4804f6772bbe8995889f203a01f77ddf2d790929dd", size = 10510820, upload-time = "2026-03-05T20:06:37.79Z" }, + { url = "https://files.pythonhosted.org/packages/ff/20/b67ce78f9e6c59ffbdb5b4503d0090e749b5f2d31b599b554698a80d861c/ruff-0.15.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d20aa469ae3b57033519c559e9bc9cd9e782842e39be05b50e852c7c981fa01d", size = 10302395, upload-time = "2026-03-05T20:05:54.504Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e5/719f1acccd31b720d477751558ed74e9c88134adcc377e5e886af89d3072/ruff-0.15.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:15388dd28c9161cdb8eda68993533acc870aa4e646a0a277aa166de9ad5a8752", size = 10754069, upload-time = "2026-03-05T20:06:06.422Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9c/d1db14469e32d98f3ca27079dbd30b7b44dbb5317d06ab36718dee3baf03/ruff-0.15.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b30da330cbd03bed0c21420b6b953158f60c74c54c5f4c1dabbdf3a57bf355d2", size = 11304315, upload-time = "2026-03-05T20:06:10.867Z" }, +] + [[package]] name = "scikit-image" version = "0.26.0" @@ -2035,6 +2181,13 @@ dependencies = [ { name = "tqdm", marker = "sys_platform == 'linux'" }, ] +[package.dev-dependencies] +dev = [ + { name = "mypy", marker = "sys_platform == 'linux'" }, + { name = "pre-commit", marker = "sys_platform == 'linux'" }, + { name = "ruff", marker = "sys_platform == 'linux'" }, +] + [package.metadata] requires-dist = [ { name = "albumentations", specifier = ">=1.4.14" }, @@ -2051,6 +2204,13 @@ requires-dist = [ { name = "tqdm", specifier = ">=4.66.5" }, ] +[package.metadata.requires-dev] +dev = [ + { name = "mypy" }, + { name = "pre-commit" }, + { name = "ruff" }, +] + [[package]] name = "starlette" version = "0.52.1" @@ -2264,6 +2424,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/83/e4/d04a086285c20886c0daad0e026f250869201013d18f81d9ff5eada73a88/uvicorn-0.41.0-py3-none-any.whl", hash = "sha256:29e35b1d2c36a04b9e180d4007ede3bcb32a85fbdfd6c6aeb3f26839de088187", size = 68783, upload-time = "2026-02-16T23:07:22.357Z" }, ] +[[package]] +name = "virtualenv" +version = "21.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib", marker = "sys_platform == 'linux'" }, + { name = "filelock", marker = "sys_platform == 'linux'" }, + { name = "platformdirs", marker = "sys_platform == 'linux'" }, + { name = "python-discovery", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/92/58199fe10049f9703c2666e809c4f686c54ef0a68b0f6afccf518c0b1eb9/virtualenv-21.2.0.tar.gz", hash = "sha256:1720dc3a62ef5b443092e3f499228599045d7fea4c79199770499df8becf9098", size = 5840618, upload-time = "2026-03-09T17:24:38.013Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl", hash = "sha256:1bd755b504931164a5a496d217c014d098426cddc79363ad66ac78125f9d908f", size = 5825084, upload-time = "2026-03-09T17:24:35.378Z" }, +] + [[package]] name = "wcwidth" version = "0.6.0" From 64fb06c789230601f011ad4040c272e8b85b3944 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni.cz> Date: Wed, 11 Mar 2026 07:19:02 +0100 Subject: [PATCH 84/85] fix: remove git@ --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 231c39a..355f258 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ dependencies = [ "rationai-tiling @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git", "scikit-image>=0.25.2", "openslide-bin>=4.0.0.6", - "rationai-staining @ git+https://git@gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git", + "rationai-staining @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git", ] [dependency-groups] From 3e4da7b1297c8eb1abcac10437b3b86095eabf07 Mon Sep 17 00:00:00 2001 From: Adam Lopatka <524733@mail.muni.cz> Date: Wed, 11 Mar 2026 10:47:56 +0100 Subject: [PATCH 85/85] chore: update dataset to PC-A-42 --- configs/data/datasets/stain_normalization/test.yaml | 2 +- configs/data/datasets/stain_normalization/train.yaml | 2 +- configs/data/datasets/stain_normalization/val.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/data/datasets/stain_normalization/test.yaml b/configs/data/datasets/stain_normalization/test.yaml index be01300..8d5b7d5 100644 --- a/configs/data/datasets/stain_normalization/test.yaml +++ b/configs/data/datasets/stain_normalization/test.yaml @@ -3,4 +3,4 @@ defaults: - /data/normalize@normalize: default _target_: stain_normalization.data.datasets.TestDataset -uris: ["mlflow-artifacts:/79/b987e3e7329342fe96906eb3b17bfcc4/artifacts/Stain Normalization - test (updated)"] +uris: ["mlflow-artifacts:/65/c47cd842fbf14bf399cd4aca43af3127/artifacts/MMCI TL Test 512"] diff --git a/configs/data/datasets/stain_normalization/train.yaml b/configs/data/datasets/stain_normalization/train.yaml index 6754a96..6cf01a5 100644 --- a/configs/data/datasets/stain_normalization/train.yaml +++ b/configs/data/datasets/stain_normalization/train.yaml @@ -3,4 +3,4 @@ defaults: - /data/normalize@normalize: default _target_: stain_normalization.data.datasets.TrainDataset -uris: ["mlflow-artifacts:/79/b987e3e7329342fe96906eb3b17bfcc4/artifacts/Stain Normalization - train (updated)"] +uris: ["mlflow-artifacts:/65/b587398845c54e1c84e8b60f11c42b15/artifacts/MMCI TL Train 512"] diff --git a/configs/data/datasets/stain_normalization/val.yaml b/configs/data/datasets/stain_normalization/val.yaml index 4c4c13c..500db12 100644 --- a/configs/data/datasets/stain_normalization/val.yaml +++ b/configs/data/datasets/stain_normalization/val.yaml @@ -3,5 +3,5 @@ defaults: - /data/normalize@normalize: default _target_: stain_normalization.data.datasets.TrainDataset -uris: ["mlflow-artifacts:/79/b987e3e7329342fe96906eb3b17bfcc4/artifacts/Stain Normalization - val (updated)"] +uris: ["mlflow-artifacts:/65/a43da470f89848abb3a32e59c504ee1a/artifacts/MMCI TL Val 512"]

+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