diff --git a/.example.pypirc b/.example.pypirc new file mode 100644 index 0000000..43d915b --- /dev/null +++ b/.example.pypirc @@ -0,0 +1,7 @@ +[distutils] +index-servers = forgejo + +[forgejo] +repository = https://things.com +username = {username} +password = {password} diff --git a/.gitignore b/.gitignore index 0a80269..2e83a31 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ eggs/ lib/ lib64/ parts/ +dist/ sdist/ var/ wheels/ @@ -158,5 +159,5 @@ cython_debug/ # 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/ -.vscode/ \ No newline at end of file +.idea/ +.vscode/ diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..3c27d1b --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +yafti diff --git a/README.md b/README.md index 14eebf2..50d4da7 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ actions: pre: - run: /full/path/to/bin --with --params - run: /another/command run - - yafti.plugin.flatpak: + - yafti.plugins.flatpak: install: org.gnome.Calculator post: - run: /run/these/commands --after --all --screens @@ -83,7 +83,7 @@ screens: values: title: Package Installation show_terminal: true - package_manager: yafti.plugin.flatpak + package_manager: yafti.plugins.flatpak groups: Core: description: All the good stuff @@ -117,8 +117,54 @@ This project uses Poetry and Python 3.11. Make sure you have Python 3.11 and [Po If you're on a Ublue / immutable OS, you'll need to run these and the poetry install in a toolbox. +[cairographics](https://www.cairographics.org/download/) + + +#### distrobox + +the following just commands will create a distro box container + +```shell +just create-distrobox +just enter-distrobox +``` + +After entering the distro box + +```shell +# prints fedora info +fastfetch + +# launches the app +just run +``` + +#### deb based developer prerequisites + +```shell +sudo apt update +sudo apt install build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev curl libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev ibgirepository1.0-dev libcairo2-dev +``` + +#### https://iconduck.com/icons/164466/emblem-system-symbolic + + +#### rhel based developer prerequisites + +``` +sudo dnf install python3-devel cairo-devel gobject-introspection-devel cairo-gobject-devel vte291-gtk4-devel ``` -sudo dnf install python3-devel cairo-devel gobject-introspection-devel cairo-gobject-devel + +#### pyenv installer + +```shell +curl https://pyenv.run | bash +pyenv install 3.11.8 +pyenv virtualenv 3.11.8 yafti +pyenv activate yafti +pip install --upgrade pip +pip install poetry + poetry install ``` diff --git a/distrobox.ini b/distrobox.ini new file mode 100644 index 0000000..3ccea34 --- /dev/null +++ b/distrobox.ini @@ -0,0 +1,9 @@ +[yaftibox] +image=ghcr.io/ublue-os/fedora-toolbox:latest +additional_packages="just fastfetch redhat-lsb-core poetry black python3-isort python3-devel gtk3 gtk4 cairo-devel gobject-introspection-devel cairo-gobject-devel libadwaita-devel vte291-gtk4-devel" +nvidia=false +init=false +pull=true +root=false +replace=true +start_now=true diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..800df87 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,3 @@ +# Yet Another First Time Installer + +TODO add content for generated Github pages. diff --git a/justfile b/justfile new file mode 100644 index 0000000..beac45e --- /dev/null +++ b/justfile @@ -0,0 +1,111 @@ +# justfile docs https://just.systems/man/en/ +# justfile cheatsheet https://cheatography.com/linux-china/cheat-sheets/justfile/ +set dotenv-load := true +set export + +project := "yafti" +host := `uname -a` +python_exe := `which python` +pip_exe := `which pip3` +project_path := invocation_directory() +module_path := join(project_path, 'yafti') + +### +# need to add readme to docs and run a full suite of test etc +# absolute_path parent_directory justfile_directory +# https://github.com/python-poetry/poetry python package and venv manager. +# https://unicode.org/emoji/charts/emoji-list.html#1fae1 emoji's +### +bt := '0' + +export RUST_BACKTRACE_1 := bt +export JUST_LOG := "warn" + +sys-info: + @echo '{{host}}' + +python: + @echo 'Python:' `poetry run python --version` + @echo 'Python Path:' `poetry env info` + @echo 'Python Path:' `which python` +# +@dev deps="yes": + pip install poetry pip --upgrade + poetry install --with docs + +build deps="yes": (dev deps) + @echo "building wheel" + poetry build + +# set-hooks: dev +# pre-commit +# poetry run pre-commit install --install-hooks # uninstall: `pre-commit uninstall` + +unit-test deps="no": (dev deps) + @echo 'unit-tests' + poetry run pytest --cov={{ module_path}} --cov-report=term-missing + +type-checks deps="no": (dev deps) + @echo 'type checking' + poetry run mypy {{ module_path }} + +format deps="no": (dev deps) + @echo 'black isort and ruff being called into action' + poetry run black {{ module_path }} + poetry run isort {{ module_path }} + poetry run ruff check {{ module_path }} + +# TODO: i like flake8 +#lint deps="no": (dev deps) +# @echo "linting code base" +# poetry run flake8 {{ module_path }} \ +# --max-line-length 100 \ +# --ignore=F401,E731,F403,E501,W503 \ +# --count + +coverage-report: + @echo "Building test coverage report" + # poetry run pytest --cov=yafti --cov-report=term-missing + poetry run pytest --cov={{ module_path }} --cov-config=.coveragerc --cov-report html + +sphinx deps="no": (dev deps) + @echo "Building application documentation" + mkdir -p docs/html + touch docs/.nojekyll + cd docs/ && poetry run sphinx-build -b html . _build + cp -r docs/_build/* docs/html + +docs deps="no": (dev deps) + @echo "Run auto build and start http server." + poetry run sphinx-autobuild docs docs/_build/html + +run deps="no": (dev deps) + # not sure why GTK_IM_MODULE is required in the distrobox. I need to test on a physical installation. + export GTK_IM_MODULE=ibus + # poetry run python -m {{ project }}.cli start tests/bazzite.yaml --debug -f + poetry run python -m {{ project }}.cli start tests/bluefin.yml --debug -f + # poetry run python -m {{ project }}.cli start tests/example.yml --debug + +create-distrobox: + distrobox-assemble create --file {{ project_path }}/distrobox.ini + +enter-distrobox: + distrobox-enter yaftibox + +help: + just -l + + +### +# Need to add a just command for glib-compile-schemas +### + +### +# Add just command for running act local github runners +### + +### +# gnome extension mangement. +# https://github.com/essembeh/gnome-extensions-cli/ +### + diff --git a/poetry.lock b/poetry.lock index e61b8a0..58734c7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,34 +1,128 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "alabaster" +version = "0.7.16" +description = "A light, configurable Sphinx theme" +optional = false +python-versions = ">=3.9" +files = [ + {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, + {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, +] + +[[package]] +name = "appnope" +version = "0.1.4" +description = "Disable App Nap on macOS >= 10.9" +optional = false +python-versions = ">=3.6" +files = [ + {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, + {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, +] + +[[package]] +name = "asttokens" +version = "2.4.1" +description = "Annotate AST trees with source code positions" +optional = false +python-versions = "*" +files = [ + {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, + {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, +] + +[package.dependencies] +six = ">=1.12.0" + +[package.extras] +astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] +test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "babel" +version = "2.14.0" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, + {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, +] + +[package.extras] +dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] + +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +description = "Screen-scraping library" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] +html5lib = ["html5lib"] +lxml = ["lxml"] [[package]] name = "black" -version = "23.7.0" +version = "23.12.1" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ - {file = "black-23.7.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587"}, - {file = "black-23.7.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f"}, - {file = "black-23.7.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be"}, - {file = "black-23.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc"}, - {file = "black-23.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995"}, - {file = "black-23.7.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:b5b0ee6d96b345a8b420100b7d71ebfdd19fab5e8301aff48ec270042cd40ac2"}, - {file = "black-23.7.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:893695a76b140881531062d48476ebe4a48f5d1e9388177e175d76234ca247cd"}, - {file = "black-23.7.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:c333286dc3ddca6fdff74670b911cccedacb4ef0a60b34e491b8a67c833b343a"}, - {file = "black-23.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831d8f54c3a8c8cf55f64d0422ee875eecac26f5f649fb6c1df65316b67c8926"}, - {file = "black-23.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:7f3bf2dec7d541b4619b8ce526bda74a6b0bffc480a163fed32eb8b3c9aed8ad"}, - {file = "black-23.7.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:f9062af71c59c004cd519e2fb8f5d25d39e46d3af011b41ab43b9c74e27e236f"}, - {file = "black-23.7.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:01ede61aac8c154b55f35301fac3e730baf0c9cf8120f65a9cd61a81cfb4a0c3"}, - {file = "black-23.7.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:327a8c2550ddc573b51e2c352adb88143464bb9d92c10416feb86b0f5aee5ff6"}, - {file = "black-23.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1c6022b86f83b632d06f2b02774134def5d4d4f1dac8bef16d90cda18ba28a"}, - {file = "black-23.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:27eb7a0c71604d5de083757fbdb245b1a4fae60e9596514c6ec497eb63f95320"}, - {file = "black-23.7.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:8417dbd2f57b5701492cd46edcecc4f9208dc75529bcf76c514864e48da867d9"}, - {file = "black-23.7.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:47e56d83aad53ca140da0af87678fb38e44fd6bc0af71eebab2d1f59b1acf1d3"}, - {file = "black-23.7.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:25cc308838fe71f7065df53aedd20327969d05671bac95b38fdf37ebe70ac087"}, - {file = "black-23.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:642496b675095d423f9b8448243336f8ec71c9d4d57ec17bf795b67f08132a91"}, - {file = "black-23.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:ad0014efc7acf0bd745792bd0d8857413652979200ab924fbf239062adc12491"}, - {file = "black-23.7.0-py3-none-any.whl", hash = "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96"}, - {file = "black-23.7.0.tar.gz", hash = "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb"}, + {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, + {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, + {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, + {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, + {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, + {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, + {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, + {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, + {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, + {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, + {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, + {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, + {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, + {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, + {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, + {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, + {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, + {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, + {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, + {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, + {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, + {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, ] [package.dependencies] @@ -40,19 +134,193 @@ platformdirs = ">=2" [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {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-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + [[package]] name = "click" -version = "8.1.3" +version = "8.1.7" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" files = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] [package.dependencies] @@ -69,6 +337,23 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "comm" +version = "0.2.2" +description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." +optional = false +python-versions = ">=3.8" +files = [ + {file = "comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3"}, + {file = "comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e"}, +] + +[package.dependencies] +traitlets = ">=4" + +[package.extras] +test = ["pytest"] + [[package]] name = "coverage" version = "7.4.3" @@ -134,306 +419,1126 @@ files = [ toml = ["tomli"] [[package]] -name = "gbulb" -version = "0.6.4" -description = "GLib event loop for tulip (PEP 3156)" +name = "debugpy" +version = "1.8.1" +description = "An implementation of the Debug Adapter Protocol for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "gbulb-0.6.4-py3-none-any.whl", hash = "sha256:31d64ede2e7aac8f871ce914f884b949e4891b1faa2ef45800db9eaa9c4b371b"}, - {file = "gbulb-0.6.4.tar.gz", hash = "sha256:4a6dddd9b8f958393e9ace60def7feacee6528136504bea3407e5ffe79f1a89f"}, + {file = "debugpy-1.8.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:3bda0f1e943d386cc7a0e71bfa59f4137909e2ed947fb3946c506e113000f741"}, + {file = "debugpy-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda73bf69ea479c8577a0448f8c707691152e6c4de7f0c4dec5a4bc11dee516e"}, + {file = "debugpy-1.8.1-cp310-cp310-win32.whl", hash = "sha256:3a79c6f62adef994b2dbe9fc2cc9cc3864a23575b6e387339ab739873bea53d0"}, + {file = "debugpy-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:7eb7bd2b56ea3bedb009616d9e2f64aab8fc7000d481faec3cd26c98a964bcdd"}, + {file = "debugpy-1.8.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:016a9fcfc2c6b57f939673c874310d8581d51a0fe0858e7fac4e240c5eb743cb"}, + {file = "debugpy-1.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd97ed11a4c7f6d042d320ce03d83b20c3fb40da892f994bc041bbc415d7a099"}, + {file = "debugpy-1.8.1-cp311-cp311-win32.whl", hash = "sha256:0de56aba8249c28a300bdb0672a9b94785074eb82eb672db66c8144fff673146"}, + {file = "debugpy-1.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:1a9fe0829c2b854757b4fd0a338d93bc17249a3bf69ecf765c61d4c522bb92a8"}, + {file = "debugpy-1.8.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ebb70ba1a6524d19fa7bb122f44b74170c447d5746a503e36adc244a20ac539"}, + {file = "debugpy-1.8.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2e658a9630f27534e63922ebf655a6ab60c370f4d2fc5c02a5b19baf4410ace"}, + {file = "debugpy-1.8.1-cp312-cp312-win32.whl", hash = "sha256:caad2846e21188797a1f17fc09c31b84c7c3c23baf2516fed5b40b378515bbf0"}, + {file = "debugpy-1.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:edcc9f58ec0fd121a25bc950d4578df47428d72e1a0d66c07403b04eb93bcf98"}, + {file = "debugpy-1.8.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:7a3afa222f6fd3d9dfecd52729bc2e12c93e22a7491405a0ecbf9e1d32d45b39"}, + {file = "debugpy-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d915a18f0597ef685e88bb35e5d7ab968964b7befefe1aaea1eb5b2640b586c7"}, + {file = "debugpy-1.8.1-cp38-cp38-win32.whl", hash = "sha256:92116039b5500633cc8d44ecc187abe2dfa9b90f7a82bbf81d079fcdd506bae9"}, + {file = "debugpy-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:e38beb7992b5afd9d5244e96ad5fa9135e94993b0c551ceebf3fe1a5d9beb234"}, + {file = "debugpy-1.8.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:bfb20cb57486c8e4793d41996652e5a6a885b4d9175dd369045dad59eaacea42"}, + {file = "debugpy-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd3fdd3f67a7e576dd869c184c5dd71d9aaa36ded271939da352880c012e703"}, + {file = "debugpy-1.8.1-cp39-cp39-win32.whl", hash = "sha256:58911e8521ca0c785ac7a0539f1e77e0ce2df753f786188f382229278b4cdf23"}, + {file = "debugpy-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:6df9aa9599eb05ca179fb0b810282255202a66835c6efb1d112d21ecb830ddd3"}, + {file = "debugpy-1.8.1-py2.py3-none-any.whl", hash = "sha256:28acbe2241222b87e255260c76741e1fbf04fdc3b6d094fcf57b6c6f75ce1242"}, + {file = "debugpy-1.8.1.zip", hash = "sha256:f696d6be15be87aef621917585f9bb94b1dc9e8aced570db1b8a6fc14e8f9b42"}, ] -[package.dependencies] -pygobject = ">=3.14.0" +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +optional = false +python-versions = ">=3.5" +files = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] [[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" +name = "docutils" +version = "0.20.1" +description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.7" files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, + {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, + {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, ] [[package]] -name = "isort" -version = "5.12.0" -description = "A Python utility / library to sort Python imports." +name = "executing" +version = "2.0.1" +description = "Get the currently executing AST node of a frame, and other information" optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.5" files = [ - {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, - {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, + {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, + {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, ] [package.extras] -colors = ["colorama (>=0.4.3)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] [[package]] -name = "markdown-it-py" -version = "2.2.0" -description = "Python port of markdown-it. Markdown parsing, done right!" +name = "fastjsonschema" +version = "2.19.1" +description = "Fastest Python implementation of JSON schema" optional = false -python-versions = ">=3.7" +python-versions = "*" files = [ - {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, - {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, + {file = "fastjsonschema-2.19.1-py3-none-any.whl", hash = "sha256:3672b47bc94178c9f23dbb654bf47440155d4db9df5f7bc47643315f9c405cd0"}, + {file = "fastjsonschema-2.19.1.tar.gz", hash = "sha256:e3126a94bdc4623d3de4485f8d468a12f02a67921315ddc87836d6e456dc789d"}, ] -[package.dependencies] -mdurl = ">=0.1,<1.0" - [package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark"] -code-style = ["pre-commit (>=3.0,<4.0)"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] -linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins"] -profiling = ["gprof2dot"] -rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] +devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] [[package]] -name = "mdurl" -version = "0.1.2" -description = "Markdown URL utilities" +name = "furo" +version = "2024.1.29" +description = "A clean customisable Sphinx documentation theme." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, + {file = "furo-2024.1.29-py3-none-any.whl", hash = "sha256:3548be2cef45a32f8cdc0272d415fcb3e5fa6a0eb4ddfe21df3ecf1fe45a13cf"}, + {file = "furo-2024.1.29.tar.gz", hash = "sha256:4d6b2fe3f10a6e36eb9cc24c1e7beb38d7a23fc7b3c382867503b7fcac8a1e02"}, ] +[package.dependencies] +beautifulsoup4 = "*" +pygments = ">=2.7" +sphinx = ">=6.0,<8.0" +sphinx-basic-ng = "*" + [[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." +name = "gbulb" +version = "0.6.4" +description = "GLib event loop for tulip (PEP 3156)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" 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"}, + {file = "gbulb-0.6.4-py3-none-any.whl", hash = "sha256:31d64ede2e7aac8f871ce914f884b949e4891b1faa2ef45800db9eaa9c4b371b"}, + {file = "gbulb-0.6.4.tar.gz", hash = "sha256:4a6dddd9b8f958393e9ace60def7feacee6528136504bea3407e5ffe79f1a89f"}, ] +[package.dependencies] +pygobject = ">=3.14.0" + [[package]] -name = "packaging" -version = "23.0" -description = "Core utilities for Python packages" +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, - {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {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-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, ] +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + [[package]] -name = "pathspec" -version = "0.11.1" -description = "Utility library for gitignore style pattern matching of file paths." +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.7" +python-versions = ">=3.5" files = [ - {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, - {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, ] [[package]] -name = "platformdirs" -version = "3.1.1" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +name = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" optional = false -python-versions = ">=3.7" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ - {file = "platformdirs-3.1.1-py3-none-any.whl", hash = "sha256:e5986afb596e4bb5bde29a79ac9061aa955b94fca2399b7aaac4090860920dd8"}, - {file = "platformdirs-3.1.1.tar.gz", hash = "sha256:024996549ee88ec1a9aa99ff7f8fc819bb59e2c3477b410d90a16d32d6e707aa"}, + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] -[package.extras] -docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] - [[package]] -name = "pluggy" -version = "1.4.0" -description = "plugin and hook calling mechanisms for python" +name = "importlib-metadata" +version = "7.0.2" +description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, + {file = "importlib_metadata-7.0.2-py3-none-any.whl", hash = "sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100"}, + {file = "importlib_metadata-7.0.2.tar.gz", hash = "sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792"}, ] +[package.dependencies] +zipp = ">=0.5" + [package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] -name = "pycairo" -version = "1.23.0" -description = "Python interface for cairo" +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" files = [ - {file = "pycairo-1.23.0-cp310-cp310-win32.whl", hash = "sha256:564601e5f528531c6caec1c0177c3d0709081e1a2a5cccc13561f715080ae535"}, - {file = "pycairo-1.23.0-cp310-cp310-win_amd64.whl", hash = "sha256:e7cde633986435d87a86b6118b7b6109c384266fd719ef959883e2729f6eafae"}, - {file = "pycairo-1.23.0-cp311-cp311-win32.whl", hash = "sha256:3a71f758e461180d241e62ef52e85499c843bd2660fd6d87cec99c9833792bfa"}, - {file = "pycairo-1.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:2dec5378133778961993fb59d66df16070e03f4d491b67eb695ca9ad7a696008"}, - {file = "pycairo-1.23.0-cp37-cp37m-win32.whl", hash = "sha256:d6bacff15d688ed135b4567965a4b664d9fb8de7417a7865bb138ad612043c9f"}, - {file = "pycairo-1.23.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ec305fc7f2f0299df78aadec0eaf6eb9accb90eda242b5d3492544d3f2b28027"}, - {file = "pycairo-1.23.0-cp38-cp38-win32.whl", hash = "sha256:1a6d8e0f353062ad92954784e33dbbaf66c880c9c30e947996c542ed9748aaaf"}, - {file = "pycairo-1.23.0-cp38-cp38-win_amd64.whl", hash = "sha256:82e335774a17870bc038e0c2fb106c1e5e7ad0c764662023886dfcfce5bb5a52"}, - {file = "pycairo-1.23.0-cp39-cp39-win32.whl", hash = "sha256:a4b1f525bbdf637c40f4d91378de36c01ec2b7f8ecc585b700a079b9ff83298e"}, - {file = "pycairo-1.23.0-cp39-cp39-win_amd64.whl", hash = "sha256:87efd62a7b7afad9a0a420f05b6008742a6cfc59077697be65afe8dc73ae15ad"}, - {file = "pycairo-1.23.0.tar.gz", hash = "sha256:9b61ac818723adc04367301317eb2e814a83522f07bbd1f409af0dada463c44c"}, + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] -name = "pydantic" -version = "1.10.2" -description = "Data validation and settings management using python type hints" +name = "ipykernel" +version = "6.29.3" +description = "IPython Kernel for Jupyter" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"}, - {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"}, - {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912"}, - {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559"}, - {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"}, - {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c"}, - {file = "pydantic-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644"}, - {file = "pydantic-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f"}, - {file = "pydantic-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a"}, - {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525"}, - {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283"}, - {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42"}, - {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52"}, - {file = "pydantic-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c"}, - {file = "pydantic-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5"}, - {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"}, - {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"}, - {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5"}, - {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d"}, - {file = "pydantic-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2"}, - {file = "pydantic-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13"}, - {file = "pydantic-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116"}, - {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624"}, - {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1"}, - {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"}, - {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965"}, - {file = "pydantic-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e"}, - {file = "pydantic-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488"}, - {file = "pydantic-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41"}, - {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b"}, - {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe"}, - {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d"}, - {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda"}, - {file = "pydantic-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6"}, - {file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"}, - {file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"}, + {file = "ipykernel-6.29.3-py3-none-any.whl", hash = "sha256:5aa086a4175b0229d4eca211e181fb473ea78ffd9869af36ba7694c947302a21"}, + {file = "ipykernel-6.29.3.tar.gz", hash = "sha256:e14c250d1f9ea3989490225cc1a542781b095a18a19447fcf2b5eaf7d0ac5bd2"}, ] [package.dependencies] -typing-extensions = ">=4.1.0" +appnope = {version = "*", markers = "platform_system == \"Darwin\""} +comm = ">=0.1.1" +debugpy = ">=1.6.5" +ipython = ">=7.23.1" +jupyter-client = ">=6.1.12" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +matplotlib-inline = ">=0.1" +nest-asyncio = "*" +packaging = "*" +psutil = "*" +pyzmq = ">=24" +tornado = ">=6.1" +traitlets = ">=5.4.0" [package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] +cov = ["coverage[toml]", "curio", "matplotlib", "pytest-cov", "trio"] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "trio"] +pyqt5 = ["pyqt5"] +pyside6 = ["pyside6"] +test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.23.5)", "pytest-cov", "pytest-timeout"] [[package]] -name = "pygments" -version = "2.15.0" -description = "Pygments is a syntax highlighting package written in Python." +name = "ipython" +version = "8.22.2" +description = "IPython: Productive Interactive Computing" optional = false -python-versions = ">=3.7" +python-versions = ">=3.10" files = [ - {file = "Pygments-2.15.0-py3-none-any.whl", hash = "sha256:77a3299119af881904cd5ecd1ac6a66214b6e9bed1f2db16993b54adede64094"}, - {file = "Pygments-2.15.0.tar.gz", hash = "sha256:f7e36cffc4c517fbc252861b9a6e4644ca0e5abadf9a113c72d1358ad09b9500"}, + {file = "ipython-8.22.2-py3-none-any.whl", hash = "sha256:3c86f284c8f3d8f2b6c662f885c4889a91df7cd52056fd02b7d8d6195d7f56e9"}, + {file = "ipython-8.22.2.tar.gz", hash = "sha256:2dcaad9049f9056f1fef63514f176c7d41f930daa78d05b82a176202818f2c14"}, ] +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} +prompt-toolkit = ">=3.0.41,<3.1.0" +pygments = ">=2.4.0" +stack-data = "*" +traitlets = ">=5.13.0" + [package.extras] -plugins = ["importlib-metadata"] +all = ["ipython[black,doc,kernel,nbconvert,nbformat,notebook,parallel,qtconsole,terminal]", "ipython[test,test-extra]"] +black = ["black"] +doc = ["docrepr", "exceptiongroup", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "stack-data", "typing-extensions"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["pickleshare", "pytest (<8)", "pytest-asyncio (<0.22)", "testpath"] +test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] [[package]] -name = "pygobject" -version = "3.42.2" -description = "Python bindings for GObject Introspection" +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." optional = false -python-versions = ">=3.6, <4" +python-versions = ">=3.8.0" files = [ - {file = "PyGObject-3.42.2.tar.gz", hash = "sha256:21524cef33100c8fd59dc135948b703d79d303e368ce71fa60521cc971cd8aa7"}, + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, ] -[package.dependencies] -pycairo = ">=1.16,<2.0" +[package.extras] +colors = ["colorama (>=0.4.6)"] [[package]] -name = "pytest" -version = "8.1.0" -description = "pytest: simple powerful testing with Python" +name = "jedi" +version = "0.19.1" +description = "An autocompletion tool for Python that can be used for text editors." optional = false -python-versions = ">=3.8" +python-versions = ">=3.6" files = [ - {file = "pytest-8.1.0-py3-none-any.whl", hash = "sha256:ee32db7af8de4629a455806befa90559f307424c07b8413ccfc30bf5b221dd7e"}, - {file = "pytest-8.1.0.tar.gz", hash = "sha256:f8fa04ab8f98d185113ae60ea6d79c22f8143b14bc1caeced44a0ab844928323"}, + {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, + {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, ] [package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=1.4,<2.0" +parso = ">=0.8.3,<0.9.0" [package.extras] -testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] [[package]] -name = "pytest-asyncio" -version = "0.21.1" -description = "Pytest support for asyncio" +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "pytest-asyncio-0.21.1.tar.gz", hash = "sha256:40a7eae6dded22c7b604986855ea48400ab15b069ae38116e8c01238e9eeb64d"}, - {file = "pytest_asyncio-0.21.1-py3-none-any.whl", hash = "sha256:8666c1c8ac02631d7c51ba282e0c69a8a452b211ffedf2599099845da5c5c37b"}, + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, ] [package.dependencies] -pytest = ">=7.0.0" +MarkupSafe = ">=2.0" [package.extras] -docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] -testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] +i18n = ["Babel (>=2.7)"] [[package]] -name = "pytest-cov" -version = "4.1.0" -description = "Pytest plugin for measuring coverage." +name = "jsonschema" +version = "4.21.1" +description = "An implementation of JSON Schema validation for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, - {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, + {file = "jsonschema-4.21.1-py3-none-any.whl", hash = "sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f"}, + {file = "jsonschema-4.21.1.tar.gz", hash = "sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5"}, ] [package.dependencies] -coverage = {version = ">=5.2.1", extras = ["toml"]} -pytest = ">=4.6" +attrs = ">=22.2.0" +jsonschema-specifications = ">=2023.03.6" +referencing = ">=0.28.4" +rpds-py = ">=0.7.1" [package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] [[package]] -name = "pyyaml" -version = "6.0.1" -description = "YAML parser and emitter for Python" +name = "jsonschema-specifications" +version = "2023.12.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" +files = [ + {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, + {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, +] + +[package.dependencies] +referencing = ">=0.31.0" + +[[package]] +name = "jupyter-cache" +version = "1.0.0" +description = "A defined interface for working with a cache of jupyter notebooks." +optional = false +python-versions = ">=3.9" +files = [ + {file = "jupyter_cache-1.0.0-py3-none-any.whl", hash = "sha256:594b1c4e29b488b36547e12477645f489dbdc62cc939b2408df5679f79245078"}, + {file = "jupyter_cache-1.0.0.tar.gz", hash = "sha256:d0fa7d7533cd5798198d8889318269a8c1382ed3b22f622c09a9356521f48687"}, +] + +[package.dependencies] +attrs = "*" +click = "*" +importlib-metadata = "*" +nbclient = ">=0.2" +nbformat = "*" +pyyaml = "*" +sqlalchemy = ">=1.3.12,<3" +tabulate = "*" + +[package.extras] +cli = ["click-log"] +code-style = ["pre-commit (>=2.12)"] +rtd = ["ipykernel", "jupytext", "myst-nb", "nbdime", "sphinx-book-theme", "sphinx-copybutton"] +testing = ["coverage", "ipykernel", "jupytext", "matplotlib", "nbdime", "nbformat (>=5.1)", "numpy", "pandas", "pytest (>=6,<8)", "pytest-cov", "pytest-regressions", "sympy"] + +[[package]] +name = "jupyter-client" +version = "8.6.1" +description = "Jupyter protocol implementation and client libraries" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_client-8.6.1-py3-none-any.whl", hash = "sha256:3b7bd22f058434e3b9a7ea4b1500ed47de2713872288c0d511d19926f99b459f"}, + {file = "jupyter_client-8.6.1.tar.gz", hash = "sha256:e842515e2bab8e19186d89fdfea7abd15e39dd581f94e399f00e2af5a1652d3f"}, +] + +[package.dependencies] +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +python-dateutil = ">=2.8.2" +pyzmq = ">=23.0" +tornado = ">=6.2" +traitlets = ">=5.3" + +[package.extras] +docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] + +[[package]] +name = "jupyter-core" +version = "5.7.2" +description = "Jupyter core package. A base package on which Jupyter projects rely." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409"}, + {file = "jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"}, +] + +[package.dependencies] +platformdirs = ">=2.5" +pywin32 = {version = ">=300", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} +traitlets = ">=5.3" + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] +test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "livereload" +version = "2.6.3" +description = "Python LiveReload is an awesome tool for web developers" +optional = false +python-versions = "*" +files = [ + {file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"}, + {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, +] + +[package.dependencies] +six = "*" +tornado = {version = "*", markers = "python_version > \"2.7\""} + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {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-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.6" +description = "Inline Matplotlib backend for Jupyter" +optional = false +python-versions = ">=3.5" +files = [ + {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, + {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, +] + +[package.dependencies] +traitlets = "*" + +[[package]] +name = "mdit-py-plugins" +version = "0.4.0" +description = "Collection of plugins for markdown-it-py" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"}, + {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"}, +] + +[package.dependencies] +markdown-it-py = ">=1.0.0,<4.0.0" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["myst-parser", "sphinx-book-theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.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"}, +] + +[[package]] +name = "myst-nb" +version = "1.0.0" +description = "A Jupyter Notebook Sphinx reader built on top of the MyST markdown parser." +optional = false +python-versions = ">=3.9" +files = [ + {file = "myst_nb-1.0.0-py3-none-any.whl", hash = "sha256:ee8febc6dd7d9e32bede0c66a9b962b2e2fdab697428ee9fbfd4919d82380911"}, + {file = "myst_nb-1.0.0.tar.gz", hash = "sha256:9077e42a1c6b441ea55078506f83555dda5d6c816ef4930841d71d239e3e0c5e"}, +] + +[package.dependencies] +importlib_metadata = "*" +ipykernel = "*" +ipython = "*" +jupyter-cache = ">=0.5" +myst-parser = ">=1.0.0" +nbclient = "*" +nbformat = ">=5.0" +pyyaml = "*" +sphinx = ">=5" +typing-extensions = "*" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["alabaster", "altair", "bokeh", "coconut (>=1.4.3,<3.1.0)", "ipykernel (>=5.5,<7.0)", "ipywidgets", "jupytext (>=1.11.2,<1.16.0)", "matplotlib", "numpy", "pandas", "plotly", "sphinx-book-theme (>=0.3)", "sphinx-copybutton", "sphinx-design (>=0.4.0,<0.5.0)", "sphinxcontrib-bibtex", "sympy"] +testing = ["beautifulsoup4", "coverage (>=6.4,<8.0)", "ipykernel (>=5.5,<7.0)", "ipython (!=8.1.0,<8.17)", "ipywidgets (>=8)", "jupytext (>=1.11.2,<1.16.0)", "matplotlib (==3.7.*)", "nbdime", "numpy", "pandas", "pytest (>=7.1,<8.0)", "pytest-cov (>=3,<5)", "pytest-param-files (>=0.3.3,<0.4.0)", "pytest-regressions", "sympy (>=1.10.1)"] + +[[package]] +name = "myst-parser" +version = "2.0.0" +description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser," +optional = false +python-versions = ">=3.8" +files = [ + {file = "myst_parser-2.0.0-py3-none-any.whl", hash = "sha256:7c36344ae39c8e740dad7fdabf5aa6fc4897a813083c6cc9990044eb93656b14"}, + {file = "myst_parser-2.0.0.tar.gz", hash = "sha256:ea929a67a6a0b1683cdbe19b8d2e724cd7643f8aa3e7bb18dd65beac3483bead"}, +] + +[package.dependencies] +docutils = ">=0.16,<0.21" +jinja2 = "*" +markdown-it-py = ">=3.0,<4.0" +mdit-py-plugins = ">=0.4,<1.0" +pyyaml = "*" +sphinx = ">=6,<8" + +[package.extras] +code-style = ["pre-commit (>=3.0,<4.0)"] +linkify = ["linkify-it-py (>=2.0,<3.0)"] +rtd = ["ipython", "pydata-sphinx-theme (==v0.13.0rc4)", "sphinx-autodoc2 (>=0.4.2,<0.5.0)", "sphinx-book-theme (==1.0.0rc2)", "sphinx-copybutton", "sphinx-design2", "sphinx-pyscript", "sphinx-tippy (>=0.3.1)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.8.2,<0.9.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"] +testing = ["beautifulsoup4", "coverage[toml]", "pytest (>=7,<8)", "pytest-cov", "pytest-param-files (>=0.3.4,<0.4.0)", "pytest-regressions", "sphinx-pytest"] +testing-docutils = ["pygments", "pytest (>=7,<8)", "pytest-param-files (>=0.3.4,<0.4.0)"] + +[[package]] +name = "nbclient" +version = "0.10.0" +description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "nbclient-0.10.0-py3-none-any.whl", hash = "sha256:f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f"}, + {file = "nbclient-0.10.0.tar.gz", hash = "sha256:4b3f1b7dba531e498449c4db4f53da339c91d449dc11e9af3a43b4eb5c5abb09"}, +] + +[package.dependencies] +jupyter-client = ">=6.1.12" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +nbformat = ">=5.1" +traitlets = ">=5.4" + +[package.extras] +dev = ["pre-commit"] +docs = ["autodoc-traits", "mock", "moto", "myst-parser", "nbclient[test]", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling"] +test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] + +[[package]] +name = "nbformat" +version = "5.10.2" +description = "The Jupyter Notebook format" +optional = false +python-versions = ">=3.8" +files = [ + {file = "nbformat-5.10.2-py3-none-any.whl", hash = "sha256:7381189a0d537586b3f18bae5dbad347d7dd0a7cf0276b09cdcd5c24d38edd99"}, + {file = "nbformat-5.10.2.tar.gz", hash = "sha256:c535b20a0d4310167bf4d12ad31eccfb0dc61e6392d6f8c570ab5b45a06a49a3"}, +] + +[package.dependencies] +fastjsonschema = "*" +jsonschema = ">=2.6" +jupyter-core = "*" +traitlets = ">=5.1" + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["pep440", "pre-commit", "pytest", "testpath"] + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +description = "Patch asyncio to allow nested event loops" +optional = false +python-versions = ">=3.5" +files = [ + {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, + {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, +] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "parso" +version = "0.8.3" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, + {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, +] + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +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 = "pexpect" +version = "4.9.0" +description = "Pexpect allows easy control of interactive console applications." +optional = false +python-versions = "*" +files = [ + {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, + {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, +] + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pockets" +version = "0.9.1" +description = "A collection of helpful Python tools!" +optional = false +python-versions = "*" +files = [ + {file = "pockets-0.9.1-py2.py3-none-any.whl", hash = "sha256:68597934193c08a08eb2bf6a1d85593f627c22f9b065cc727a4f03f669d96d86"}, + {file = "pockets-0.9.1.tar.gz", hash = "sha256:9320f1a3c6f7a9133fe3b571f283bcf3353cd70249025ae8d618e40e9f7e92b3"}, +] + +[package.dependencies] +six = ">=1.5.2" + +[[package]] +name = "prompt-toolkit" +version = "3.0.43" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, + {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "psutil" +version = "5.9.8" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, + {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, + {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, + {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, + {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, + {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, + {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, + {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, + {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, + {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +optional = false +python-versions = "*" +files = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] + +[[package]] +name = "pure-eval" +version = "0.2.2" +description = "Safely evaluate AST nodes without side effects" +optional = false +python-versions = "*" +files = [ + {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, + {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, +] + +[package.extras] +tests = ["pytest"] + +[[package]] +name = "pycairo" +version = "1.26.0" +description = "Python interface for cairo" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycairo-1.26.0-cp310-cp310-win32.whl", hash = "sha256:696ba8024d2827e66e088a6e05a3b0aea30d289476bcb2ca47c9670d40900a50"}, + {file = "pycairo-1.26.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6690a00fb225c19f42d76660e676aba7ae7cb18f3632cb02bce7f0d9b9c3800"}, + {file = "pycairo-1.26.0-cp310-cp310-win_arm64.whl", hash = "sha256:1d54e28170a5e790269d9db4c195cca5152ff018ba7e330d0ed05d86ccc2ea7d"}, + {file = "pycairo-1.26.0-cp311-cp311-win32.whl", hash = "sha256:5986b8da3e7de7ab931d7ad527938df38f75d3a3bdea2b515c786c5ca2c5093c"}, + {file = "pycairo-1.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:d374d9ec6d2f791bf57105d87a9028db1ef2b687848f64a524e447033eae7229"}, + {file = "pycairo-1.26.0-cp311-cp311-win_arm64.whl", hash = "sha256:20a31af89d92ffd5fc60c08e65ff649f16e18621a14a40dbdb049fc74942d7a9"}, + {file = "pycairo-1.26.0-cp312-cp312-win32.whl", hash = "sha256:d63929ab5a2f890a333f2f2f51de9f1c9fe20d1bddc982c2ca577b737448d72f"}, + {file = "pycairo-1.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:8616408ae93de4824a3777ec532ea75643e4bf74e49d601062c0b1788180c962"}, + {file = "pycairo-1.26.0-cp312-cp312-win_arm64.whl", hash = "sha256:a611e4d82ad8470138bb46d465d47e8db826d9d80b6a520ccd83ee007f2073e4"}, + {file = "pycairo-1.26.0-cp38-cp38-win32.whl", hash = "sha256:675578bc6d62d15ff8669f264783efc9c8c73e3a6f564b294a70fb45a2f78667"}, + {file = "pycairo-1.26.0-cp38-cp38-win_amd64.whl", hash = "sha256:aac447b423b33b64119ecdd1ffebf9163b07f5401c5da50c707197efdd1c918a"}, + {file = "pycairo-1.26.0-cp39-cp39-win32.whl", hash = "sha256:9fa51168010e2dfb45499df071fca2d921893f724646f3454951000a7ad0cabb"}, + {file = "pycairo-1.26.0-cp39-cp39-win_amd64.whl", hash = "sha256:3e4e18ea03122e60abe3eb611e2849859cc950083ff85d8369328eadf3df63f5"}, + {file = "pycairo-1.26.0-cp39-cp39-win_arm64.whl", hash = "sha256:a8f3b567ba2ad55624a809823ccf75aff8d768c20216cb5888365f6fc695c1d2"}, + {file = "pycairo-1.26.0.tar.gz", hash = "sha256:2dddd0a874fbddb21e14acd9b955881ee1dc6e63b9c549a192d613a907f9cbeb"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pydantic" +version = "1.10.2" +description = "Data validation and settings management using python type hints" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"}, + {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"}, + {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912"}, + {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559"}, + {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"}, + {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c"}, + {file = "pydantic-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644"}, + {file = "pydantic-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f"}, + {file = "pydantic-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a"}, + {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525"}, + {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283"}, + {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42"}, + {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52"}, + {file = "pydantic-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c"}, + {file = "pydantic-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5"}, + {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"}, + {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"}, + {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5"}, + {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d"}, + {file = "pydantic-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2"}, + {file = "pydantic-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13"}, + {file = "pydantic-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116"}, + {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624"}, + {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1"}, + {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"}, + {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965"}, + {file = "pydantic-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e"}, + {file = "pydantic-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488"}, + {file = "pydantic-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41"}, + {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b"}, + {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe"}, + {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d"}, + {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda"}, + {file = "pydantic-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6"}, + {file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"}, + {file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"}, +] + +[package.dependencies] +typing-extensions = ">=4.1.0" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pygobject" +version = "3.48.1" +description = "Python bindings for GObject Introspection" +optional = false +python-versions = "<4,>=3.8" +files = [ + {file = "pygobject-3.48.1.tar.gz", hash = "sha256:2c4cd7c805ee9e727904172423a7519338017fd7e122b62218ac211627436875"}, +] + +[package.dependencies] +pycairo = ">=1.16" + +[package.extras] +dev = ["flake8", "pytest", "pytest-cov"] +docs = ["sphinx (>=4.0,<5.0)", "sphinx-rtd-theme (>=0.5,<2.0)"] + +[[package]] +name = "pygobject-stubs" +version = "2.10.0" +description = "Typing stubs for PyGObject" +optional = false +python-versions = ">=3.7" +files = [ + {file = "PyGObject-stubs-2.10.0.tar.gz", hash = "sha256:c1842f9bbb91873520376087008c829b060dad1b0ee5aa5b905f0d13ee79fe20"}, +] + +[package.extras] +dev = ["PyGObject", "black", "codespell", "isort", "pre-commit", "ruff"] + +[[package]] +name = "pytest" +version = "8.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.21.1" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-asyncio-0.21.1.tar.gz", hash = "sha256:40a7eae6dded22c7b604986855ea48400ab15b069ae38116e8c01238e9eeb64d"}, + {file = "pytest_asyncio-0.21.1-py3-none-any.whl", hash = "sha256:8666c1c8ac02631d7c51ba282e0c69a8a452b211ffedf2599099845da5c5c37b"}, +] + +[package.dependencies] +pytest = ">=7.0.0" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +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.dependencies] +six = ">=1.5" + +[[package]] +name = "pywin32" +version = "306" +description = "Python for Window Extensions" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, + {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, + {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, + {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, + {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, + {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, + {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, + {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, + {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, + {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, + {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, + {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, + {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, + {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" files = [ {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, @@ -488,15 +1593,156 @@ files = [ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] +[[package]] +name = "pyzmq" +version = "25.1.2" +description = "Python bindings for 0MQ" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyzmq-25.1.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:e624c789359f1a16f83f35e2c705d07663ff2b4d4479bad35621178d8f0f6ea4"}, + {file = "pyzmq-25.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:49151b0efece79f6a79d41a461d78535356136ee70084a1c22532fc6383f4ad0"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9a5f194cf730f2b24d6af1f833c14c10f41023da46a7f736f48b6d35061e76e"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:faf79a302f834d9e8304fafdc11d0d042266667ac45209afa57e5efc998e3872"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f51a7b4ead28d3fca8dda53216314a553b0f7a91ee8fc46a72b402a78c3e43d"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0ddd6d71d4ef17ba5a87becf7ddf01b371eaba553c603477679ae817a8d84d75"}, + {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:246747b88917e4867e2367b005fc8eefbb4a54b7db363d6c92f89d69abfff4b6"}, + {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:00c48ae2fd81e2a50c3485de1b9d5c7c57cd85dc8ec55683eac16846e57ac979"}, + {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5a68d491fc20762b630e5db2191dd07ff89834086740f70e978bb2ef2668be08"}, + {file = "pyzmq-25.1.2-cp310-cp310-win32.whl", hash = "sha256:09dfe949e83087da88c4a76767df04b22304a682d6154de2c572625c62ad6886"}, + {file = "pyzmq-25.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:fa99973d2ed20417744fca0073390ad65ce225b546febb0580358e36aa90dba6"}, + {file = "pyzmq-25.1.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:82544e0e2d0c1811482d37eef297020a040c32e0687c1f6fc23a75b75db8062c"}, + {file = "pyzmq-25.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01171fc48542348cd1a360a4b6c3e7d8f46cdcf53a8d40f84db6707a6768acc1"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc69c96735ab501419c432110016329bf0dea8898ce16fab97c6d9106dc0b348"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e124e6b1dd3dfbeb695435dff0e383256655bb18082e094a8dd1f6293114642"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7598d2ba821caa37a0f9d54c25164a4fa351ce019d64d0b44b45540950458840"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d1299d7e964c13607efd148ca1f07dcbf27c3ab9e125d1d0ae1d580a1682399d"}, + {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4e6f689880d5ad87918430957297c975203a082d9a036cc426648fcbedae769b"}, + {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cc69949484171cc961e6ecd4a8911b9ce7a0d1f738fcae717177c231bf77437b"}, + {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9880078f683466b7f567b8624bfc16cad65077be046b6e8abb53bed4eeb82dd3"}, + {file = "pyzmq-25.1.2-cp311-cp311-win32.whl", hash = "sha256:4e5837af3e5aaa99a091302df5ee001149baff06ad22b722d34e30df5f0d9097"}, + {file = "pyzmq-25.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:25c2dbb97d38b5ac9fd15586e048ec5eb1e38f3d47fe7d92167b0c77bb3584e9"}, + {file = "pyzmq-25.1.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:11e70516688190e9c2db14fcf93c04192b02d457b582a1f6190b154691b4c93a"}, + {file = "pyzmq-25.1.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:313c3794d650d1fccaaab2df942af9f2c01d6217c846177cfcbc693c7410839e"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b3cbba2f47062b85fe0ef9de5b987612140a9ba3a9c6d2543c6dec9f7c2ab27"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc31baa0c32a2ca660784d5af3b9487e13b61b3032cb01a115fce6588e1bed30"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c9087b109070c5ab0b383079fa1b5f797f8d43e9a66c07a4b8b8bdecfd88ee"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f8429b17cbb746c3e043cb986328da023657e79d5ed258b711c06a70c2ea7537"}, + {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5074adeacede5f810b7ef39607ee59d94e948b4fd954495bdb072f8c54558181"}, + {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7ae8f354b895cbd85212da245f1a5ad8159e7840e37d78b476bb4f4c3f32a9fe"}, + {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b264bf2cc96b5bc43ce0e852be995e400376bd87ceb363822e2cb1964fcdc737"}, + {file = "pyzmq-25.1.2-cp312-cp312-win32.whl", hash = "sha256:02bbc1a87b76e04fd780b45e7f695471ae6de747769e540da909173d50ff8e2d"}, + {file = "pyzmq-25.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:ced111c2e81506abd1dc142e6cd7b68dd53747b3b7ae5edbea4578c5eeff96b7"}, + {file = "pyzmq-25.1.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:7b6d09a8962a91151f0976008eb7b29b433a560fde056ec7a3db9ec8f1075438"}, + {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967668420f36878a3c9ecb5ab33c9d0ff8d054f9c0233d995a6d25b0e95e1b6b"}, + {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5edac3f57c7ddaacdb4d40f6ef2f9e299471fc38d112f4bc6d60ab9365445fb0"}, + {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0dabfb10ef897f3b7e101cacba1437bd3a5032ee667b7ead32bbcdd1a8422fe7"}, + {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:2c6441e0398c2baacfe5ba30c937d274cfc2dc5b55e82e3749e333aabffde561"}, + {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:16b726c1f6c2e7625706549f9dbe9b06004dfbec30dbed4bf50cbdfc73e5b32a"}, + {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:a86c2dd76ef71a773e70551a07318b8e52379f58dafa7ae1e0a4be78efd1ff16"}, + {file = "pyzmq-25.1.2-cp36-cp36m-win32.whl", hash = "sha256:359f7f74b5d3c65dae137f33eb2bcfa7ad9ebefd1cab85c935f063f1dbb245cc"}, + {file = "pyzmq-25.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:55875492f820d0eb3417b51d96fea549cde77893ae3790fd25491c5754ea2f68"}, + {file = "pyzmq-25.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b8c8a419dfb02e91b453615c69568442e897aaf77561ee0064d789705ff37a92"}, + {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8807c87fa893527ae8a524c15fc505d9950d5e856f03dae5921b5e9aa3b8783b"}, + {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5e319ed7d6b8f5fad9b76daa0a68497bc6f129858ad956331a5835785761e003"}, + {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3c53687dde4d9d473c587ae80cc328e5b102b517447456184b485587ebd18b62"}, + {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9add2e5b33d2cd765ad96d5eb734a5e795a0755f7fc49aa04f76d7ddda73fd70"}, + {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e690145a8c0c273c28d3b89d6fb32c45e0d9605b2293c10e650265bf5c11cfec"}, + {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:00a06faa7165634f0cac1abb27e54d7a0b3b44eb9994530b8ec73cf52e15353b"}, + {file = "pyzmq-25.1.2-cp37-cp37m-win32.whl", hash = "sha256:0f97bc2f1f13cb16905a5f3e1fbdf100e712d841482b2237484360f8bc4cb3d7"}, + {file = "pyzmq-25.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6cc0020b74b2e410287e5942e1e10886ff81ac77789eb20bec13f7ae681f0fdd"}, + {file = "pyzmq-25.1.2-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:bef02cfcbded83473bdd86dd8d3729cd82b2e569b75844fb4ea08fee3c26ae41"}, + {file = "pyzmq-25.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e10a4b5a4b1192d74853cc71a5e9fd022594573926c2a3a4802020360aa719d8"}, + {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8c5f80e578427d4695adac6fdf4370c14a2feafdc8cb35549c219b90652536ae"}, + {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5dde6751e857910c1339890f3524de74007958557593b9e7e8c5f01cd919f8a7"}, + {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea1608dd169da230a0ad602d5b1ebd39807ac96cae1845c3ceed39af08a5c6df"}, + {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0f513130c4c361201da9bc69df25a086487250e16b5571ead521b31ff6b02220"}, + {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:019744b99da30330798bb37df33549d59d380c78e516e3bab9c9b84f87a9592f"}, + {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2e2713ef44be5d52dd8b8e2023d706bf66cb22072e97fc71b168e01d25192755"}, + {file = "pyzmq-25.1.2-cp38-cp38-win32.whl", hash = "sha256:07cd61a20a535524906595e09344505a9bd46f1da7a07e504b315d41cd42eb07"}, + {file = "pyzmq-25.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb7e49a17fb8c77d3119d41a4523e432eb0c6932187c37deb6fbb00cc3028088"}, + {file = "pyzmq-25.1.2-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:94504ff66f278ab4b7e03e4cba7e7e400cb73bfa9d3d71f58d8972a8dc67e7a6"}, + {file = "pyzmq-25.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6dd0d50bbf9dca1d0bdea219ae6b40f713a3fb477c06ca3714f208fd69e16fd8"}, + {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:004ff469d21e86f0ef0369717351073e0e577428e514c47c8480770d5e24a565"}, + {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c0b5ca88a8928147b7b1e2dfa09f3b6c256bc1135a1338536cbc9ea13d3b7add"}, + {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c9a79f1d2495b167119d02be7448bfba57fad2a4207c4f68abc0bab4b92925b"}, + {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:518efd91c3d8ac9f9b4f7dd0e2b7b8bf1a4fe82a308009016b07eaa48681af82"}, + {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1ec23bd7b3a893ae676d0e54ad47d18064e6c5ae1fadc2f195143fb27373f7f6"}, + {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db36c27baed588a5a8346b971477b718fdc66cf5b80cbfbd914b4d6d355e44e2"}, + {file = "pyzmq-25.1.2-cp39-cp39-win32.whl", hash = "sha256:39b1067f13aba39d794a24761e385e2eddc26295826530a8c7b6c6c341584289"}, + {file = "pyzmq-25.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:8e9f3fabc445d0ce320ea2c59a75fe3ea591fdbdeebec5db6de530dd4b09412e"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a8c1d566344aee826b74e472e16edae0a02e2a044f14f7c24e123002dcff1c05"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:759cfd391a0996345ba94b6a5110fca9c557ad4166d86a6e81ea526c376a01e8"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c61e346ac34b74028ede1c6b4bcecf649d69b707b3ff9dc0fab453821b04d1e"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cb8fc1f8d69b411b8ec0b5f1ffbcaf14c1db95b6bccea21d83610987435f1a4"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3c00c9b7d1ca8165c610437ca0c92e7b5607b2f9076f4eb4b095c85d6e680a1d"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:df0c7a16ebb94452d2909b9a7b3337940e9a87a824c4fc1c7c36bb4404cb0cde"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:45999e7f7ed5c390f2e87ece7f6c56bf979fb213550229e711e45ecc7d42ccb8"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ac170e9e048b40c605358667aca3d94e98f604a18c44bdb4c102e67070f3ac9b"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1b604734bec94f05f81b360a272fc824334267426ae9905ff32dc2be433ab96"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:a793ac733e3d895d96f865f1806f160696422554e46d30105807fdc9841b9f7d"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0806175f2ae5ad4b835ecd87f5f85583316b69f17e97786f7443baaf54b9bb98"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ef12e259e7bc317c7597d4f6ef59b97b913e162d83b421dd0db3d6410f17a244"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea253b368eb41116011add00f8d5726762320b1bda892f744c91997b65754d73"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b9b1f2ad6498445a941d9a4fee096d387fee436e45cc660e72e768d3d8ee611"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8b14c75979ce932c53b79976a395cb2a8cd3aaf14aef75e8c2cb55a330b9b49d"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:889370d5174a741a62566c003ee8ddba4b04c3f09a97b8000092b7ca83ec9c49"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a18fff090441a40ffda8a7f4f18f03dc56ae73f148f1832e109f9bffa85df15"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99a6b36f95c98839ad98f8c553d8507644c880cf1e0a57fe5e3a3f3969040882"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4345c9a27f4310afbb9c01750e9461ff33d6fb74cd2456b107525bbeebcb5be3"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3516e0b6224cf6e43e341d56da15fd33bdc37fa0c06af4f029f7d7dfceceabbc"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:146b9b1f29ead41255387fb07be56dc29639262c0f7344f570eecdcd8d683314"}, + {file = "pyzmq-25.1.2.tar.gz", hash = "sha256:93f1aa311e8bb912e34f004cf186407a4e90eec4f0ecc0efd26056bf7eda0226"}, +] + +[package.dependencies] +cffi = {version = "*", markers = "implementation_name == \"pypy\""} + +[[package]] +name = "referencing" +version = "0.33.0" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "referencing-0.33.0-py3-none-any.whl", hash = "sha256:39240f2ecc770258f28b642dd47fd74bc8b02484de54e1882b74b35ebd779bd5"}, + {file = "referencing-0.33.0.tar.gz", hash = "sha256:c775fedf74bc0f9189c2a3be1c12fd03e8c23f4d371dce795df44e06c5b412f7"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + [[package]] name = "rich" -version = "13.6.0" +version = "13.7.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.6.0-py3-none-any.whl", hash = "sha256:2b38e2fe9ca72c9a00170a1a2d20c63c790d0e10ef1fe35eba76e1e7b1d7d245"}, - {file = "rich-13.6.0.tar.gz", hash = "sha256:5c14d22737e6d5084ef4771b62d5d4363165b403455a30a1c8ca39dc7b644bef"}, + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, ] [package.dependencies] @@ -506,6 +1752,114 @@ pygments = ">=2.13.0,<3.0.0" [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] +[[package]] +name = "rpds-py" +version = "0.18.0" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rpds_py-0.18.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e"}, + {file = "rpds_py-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88"}, + {file = "rpds_py-0.18.0-cp310-none-win32.whl", hash = "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337"}, + {file = "rpds_py-0.18.0-cp310-none-win_amd64.whl", hash = "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836"}, + {file = "rpds_py-0.18.0-cp311-none-win32.whl", hash = "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1"}, + {file = "rpds_py-0.18.0-cp311-none-win_amd64.whl", hash = "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7"}, + {file = "rpds_py-0.18.0-cp312-none-win32.whl", hash = "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98"}, + {file = "rpds_py-0.18.0-cp312-none-win_amd64.whl", hash = "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594"}, + {file = "rpds_py-0.18.0-cp38-none-win32.whl", hash = "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e"}, + {file = "rpds_py-0.18.0-cp38-none-win_amd64.whl", hash = "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20"}, + {file = "rpds_py-0.18.0-cp39-none-win32.whl", hash = "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7"}, + {file = "rpds_py-0.18.0-cp39-none-win_amd64.whl", hash = "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f"}, + {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, +] + [[package]] name = "ruff" version = "0.3.0" @@ -532,6 +1886,462 @@ files = [ {file = "ruff-0.3.0.tar.gz", hash = "sha256:0886184ba2618d815067cf43e005388967b67ab9c80df52b32ec1152ab49f53a"}, ] +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +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 = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "soupsieve" +version = "2.5" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.8" +files = [ + {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, + {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, +] + +[[package]] +name = "sphinx" +version = "7.2.6" +description = "Python documentation generator" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinx-7.2.6-py3-none-any.whl", hash = "sha256:1e09160a40b956dc623c910118fa636da93bd3ca0b9876a7b3df90f07d691560"}, + {file = "sphinx-7.2.6.tar.gz", hash = "sha256:9a5160e1ea90688d5963ba09a2dcd8bdd526620edbb65c328728f1b2228d5ab5"}, +] + +[package.dependencies] +alabaster = ">=0.7,<0.8" +babel = ">=2.9" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.18.1,<0.21" +imagesize = ">=1.3" +Jinja2 = ">=3.0" +packaging = ">=21.0" +Pygments = ">=2.14" +requests = ">=2.25.0" +snowballstemmer = ">=2.0" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.9" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] +test = ["cython (>=3.0)", "filelock", "html5lib", "pytest (>=4.6)", "setuptools (>=67.0)"] + +[[package]] +name = "sphinx-autobuild" +version = "2024.2.4" +description = "Rebuild Sphinx documentation on changes, with live-reload in the browser." +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinx_autobuild-2024.2.4-py3-none-any.whl", hash = "sha256:63fd87ab7505872a89aef468ce6503f65e794a195f4ae62269db3b85b72d4854"}, + {file = "sphinx_autobuild-2024.2.4.tar.gz", hash = "sha256:cb9d2121a176d62d45471624872afc5fad7755ad662738abe400ecf4a7954303"}, +] + +[package.dependencies] +colorama = "*" +livereload = "*" +sphinx = "*" + +[package.extras] +test = ["pytest (>=6.0)", "pytest-cov"] + +[[package]] +name = "sphinx-autodoc-typehints" +version = "2.0.0" +description = "Type hints (PEP 484) support for the Sphinx autodoc extension" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sphinx_autodoc_typehints-2.0.0-py3-none-any.whl", hash = "sha256:12c0e161f6fe191c2cdfd8fa3caea271f5387d9fbc67ebcd6f4f1f24ce880993"}, + {file = "sphinx_autodoc_typehints-2.0.0.tar.gz", hash = "sha256:7f2cdac2e70fd9787926b6e9e541cd4ded1e838d2b46fda2a1bb0a75ec5b7f3a"}, +] + +[package.dependencies] +sphinx = ">=7.1.2" + +[package.extras] +docs = ["furo (>=2023.9.10)"] +numpy = ["nptyping (>=2.5)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "sphobjinv (>=2.3.1)", "typing-extensions (>=4.8)"] + +[[package]] +name = "sphinx-basic-ng" +version = "1.0.0b2" +description = "A modern skeleton for Sphinx themes." +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"}, + {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"}, +] + +[package.dependencies] +sphinx = ">=4.0" + +[package.extras] +docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] + +[[package]] +name = "sphinx-copybutton" +version = "0.5.2" +description = "Add a copy button to each of your code cells." +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd"}, + {file = "sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e"}, +] + +[package.dependencies] +sphinx = ">=1.8" + +[package.extras] +code-style = ["pre-commit (==2.12.1)"] +rtd = ["ipython", "myst-nb", "sphinx", "sphinx-book-theme", "sphinx-examples"] + +[[package]] +name = "sphinx-design" +version = "0.5.0" +description = "A sphinx extension for designing beautiful, view size responsive web components." +optional = false +python-versions = ">=3.8" +files = [ + {file = "sphinx_design-0.5.0-py3-none-any.whl", hash = "sha256:1af1267b4cea2eedd6724614f19dcc88fe2e15aff65d06b2f6252cee9c4f4c1e"}, + {file = "sphinx_design-0.5.0.tar.gz", hash = "sha256:e8e513acea6f92d15c6de3b34e954458f245b8e761b45b63950f65373352ab00"}, +] + +[package.dependencies] +sphinx = ">=5,<8" + +[package.extras] +code-style = ["pre-commit (>=3,<4)"] +rtd = ["myst-parser (>=1,<3)"] +testing = ["myst-parser (>=1,<3)", "pytest (>=7.1,<8.0)", "pytest-cov", "pytest-regressions"] +theme-furo = ["furo (>=2023.7.0,<2023.8.0)"] +theme-pydata = ["pydata-sphinx-theme (>=0.13.0,<0.14.0)"] +theme-rtd = ["sphinx-rtd-theme (>=1.0,<2.0)"] +theme-sbt = ["sphinx-book-theme (>=1.0,<2.0)"] + +[[package]] +name = "sphinx-inline-tabs" +version = "2023.4.21" +description = "Add inline tabbed content to your Sphinx documentation." +optional = false +python-versions = ">=3.8" +files = [ + {file = "sphinx_inline_tabs-2023.4.21-py3-none-any.whl", hash = "sha256:06809ac613f7c48ddd6e2fa588413e3fe92cff2397b56e2ccf0b0218f9ef6a78"}, + {file = "sphinx_inline_tabs-2023.4.21.tar.gz", hash = "sha256:5df2f13f602c158f3f5f6c509e008aeada199a8c76d97ba3aa2822206683bebc"}, +] + +[package.dependencies] +sphinx = ">=3" + +[package.extras] +doc = ["furo", "myst-parser"] +test = ["pytest", "pytest-cov", "pytest-xdist"] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.8" +description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"}, + {file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.6" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"}, + {file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.5" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"}, + {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-mermaid" +version = "0.9.2" +description = "Mermaid diagrams in yours Sphinx powered docs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinxcontrib-mermaid-0.9.2.tar.gz", hash = "sha256:252ef13dd23164b28f16d8b0205cf184b9d8e2b714a302274d9f59eb708e77af"}, + {file = "sphinxcontrib_mermaid-0.9.2-py3-none-any.whl", hash = "sha256:6795a72037ca55e65663d2a2c1a043d636dc3d30d418e56dd6087d1459d98a5d"}, +] + +[[package]] +name = "sphinxcontrib-napoleon" +version = "0.7" +description = "Sphinx \"napoleon\" extension." +optional = false +python-versions = "*" +files = [ + {file = "sphinxcontrib-napoleon-0.7.tar.gz", hash = "sha256:407382beed396e9f2d7f3043fad6afda95719204a1e1a231ac865f40abcbfcf8"}, + {file = "sphinxcontrib_napoleon-0.7-py2.py3-none-any.whl", hash = "sha256:711e41a3974bdf110a484aec4c1a556799eb0b3f3b897521a018ad7e2db13fef"}, +] + +[package.dependencies] +pockets = ">=0.3" +six = ">=1.5.2" + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.7" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"}, + {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.10" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"}, + {file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sqlalchemy" +version = "2.0.28" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "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" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "stack-data" +version = "0.6.3" +description = "Extract data from python stack frames and tracebacks for informative displays" +optional = false +python-versions = "*" +files = [ + {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, + {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, +] + +[package.dependencies] +asttokens = ">=2.1.0" +executing = ">=1.2.0" +pure-eval = "*" + +[package.extras] +tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] + +[[package]] +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, +] + +[package.extras] +widechars = ["wcwidth"] + +[[package]] +name = "tornado" +version = "6.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] + +[[package]] +name = "traitlets" +version = "5.14.2" +description = "Traitlets Python configuration system" +optional = false +python-versions = ">=3.8" +files = [ + {file = "traitlets-5.14.2-py3-none-any.whl", hash = "sha256:fcdf85684a772ddeba87db2f398ce00b40ff550d1528c03c14dbf6a02003cd80"}, + {file = "traitlets-5.14.2.tar.gz", hash = "sha256:8cdd83c040dab7d1dee822678e5f5d100b514f7b72b01615b26fc5718916fdf9"}, +] + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.1)", "pytest-mock", "pytest-mypy-testing"] + [[package]] name = "typer" version = "0.9.0" @@ -555,16 +2365,59 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6. [[package]] name = "typing-extensions" -version = "4.5.0" -description = "Backported and Experimental Type Hints for Python 3.7+" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" files = [ - {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, - {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, ] +[[package]] +name = "zipp" +version = "3.18.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.18.0-py3-none-any.whl", hash = "sha256:c1bb803ed69d2cce2373152797064f7e79bc43f0a3748eb494096a867e0ebf79"}, + {file = "zipp-3.18.0.tar.gz", hash = "sha256:df8d042b02765029a09b157efd8e820451045890acc30f8e37dd2f94a060221f"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "6906ad6794e654ee6e42d299234e0422ae8ace4032c995bf29323a5580d52fa1" +content-hash = "105beed0ea4cb7699e18c95fd924535ee118ade6d651e4069bafcf52c6538479" diff --git a/pyproject.toml b/pyproject.toml index eb0d553..9942061 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,8 @@ [tool.poetry] name = "yafti" -version = "0.8.0" +version = "0.9.0-beta1" description = "Yet another first time installer" -authors = ["Marco Ceppi "] +authors = ["Alex Banna "] license = "Apache 2.0" readme = "README.md" homepage = "https://github.com/ublue-os/yafti" @@ -15,7 +15,7 @@ classifiers = [ ] [tool.poetry.scripts] -yafti = "yafti.__main__:app" +yafti = "yafti.cli.main:app" [tool.poetry.dependencies] python = "^3.11" @@ -26,16 +26,15 @@ gbulb = "^0.6.4" rich = "^13.3.2" typer = ">=0.7,<0.10" -[tool.poetry.plugins."yafti.plugin"] -"yafti.plugin.flatpak" = "yafti.plugin.flatpak:Flatpak" -"yafti.plugin.run" = "yafti.plugin.run:Run" -"run" = "yafti.plugin.run:Run" +[tool.poetry.plugins."yafti.plugins"] +"yafti.plugins.flatpak" = "yafti.plugins.flatpak:Flatpak" +"yafti.plugins.run" = "yafti.plugins.run:Run" +"run" = "yafti.plugins.run:Run" -[tool.poetry.plugins."yafti.screen"] -"yafti.screen.title" = "yafti.screen.title:TitleScreen" -"yafti.screen.package" = "yafti.screen.package:PackageScreen" -"yafti.screen.console" = "yafti.screen.console:ConsoleScreen" -"yafti.screen.consent" = "yafti.screen.consent:ConsentScreen" +#[tool.poetry.plugins."yafti.views"] +#"yafti.views.title" = "yafti.views.title:TitleScreen" +#"yafti.views.console" = "yafti.views.console:YaftiApplyChanges" +#"yafti.views.consent" = "yafti.views.consent:YaftiConsentScreen" [tool.poetry.group.dev.dependencies] black = "^23.1.0" @@ -45,6 +44,23 @@ ruff = ">=0.0.254,<0.3.1" coverage = "^7.2.1" pytest-cov = "^4.0.0" pytest-asyncio = ">=0.20.3,<0.22.0" +pygobject-stubs = "^2.10.0" + +[tool.poetry.group.docs] +optional = true + +[tool.poetry.group.docs.dependencies] +furo = "*" +myst-parser = "*" +myst-nb = "*" +sphinx = "*" +sphinx-autobuild = "*" +sphinx-autodoc-typehints = "*" +sphinx-copybutton = "*" +sphinxcontrib-mermaid = "*" +sphinxcontrib-napoleon = "*" +sphinx-design = "*" +sphinx-inline-tabs = "*" [tool.isort] profile = "black" diff --git a/tests/bazzite.yaml b/tests/bazzite.yaml new file mode 100644 index 0000000..2b1cab1 --- /dev/null +++ b/tests/bazzite.yaml @@ -0,0 +1,255 @@ +title: Bazzite Portal +properties: + mode: "run-once" + save_state: "always" +screens: + first-screen: + source: yafti.screen.title + values: + title: "Welcome to Bazzite (Steam Deck & HTPC Edition)" + icon: "/usr/share/ublue-os/bazzite/logo.svg" + description: | + Configure your system to get started. This utility can be re-opened at any time, so don't feel like you have to get it perfect your first go-through. + configure-bazzite: + source: yafti.screen.package + values: + title: Setting up Bazzite + show_terminal: true + package_manager: yafti.plugins.run + groups: + Add input group to current user: + description: Adds the input group to your current user. Required by certain controller drivers. + default: true + package_manager_defaults: + user: false + packages: + - Set input group: sudo -A ujust add-user-to-input-group + BIOS & Firmware Updates: + description: Enables BIOS & Firmware updates for Steam Deck hardware + default: true + packages: + - Enable Updates: sudo -A ujust enable-deck-bios-firmware-updates + Decky Loader: + description: A plugin loader for the Steam Deck + default: false + packages: + - Retrieve Decky: sudo -A ujust setup-decky install + EmuDeck: + description: | + A utility for installing and configuring emulators on the Steam Deck + default: false + packages: + - Retrieve EmuDeck: ujust get-emudeck + Sunshine: + description: A self-hosted game stream host for Moonlight + default: false + packages: + - Install Sunshine: ujust setup-sunshine install + - Autostart Sunshine: ujust setup-sunshine autostart + Hide GRUB Menu: + description: | + NOTE: Press the escape key before boot to show the menu + default: true + packages: + - Hide GRUB: sudo -A ujust configure-grub hide + Install Game Mode Startup Video: + description: Adds a Bazzite themed startup video for Steam's game mode. + default: true + packages: + - Install Video: ujust install-gamemode-video + Fix Steam Download Speed: + description: Adds a config file that fixes download speed under Steam for Linux. + default: true + packages: + - Fix Download Speed: ujust fix-steam-download-speed + Oversteer: + description: Application to control supported steering wheels + default: false + packages: + - Install Oversteer: ujust install-oversteer + OpenRazer: + description: Enables additional capabilities for Razer Hardware + default: false + packages: + - Install OpenRazer: ujust install-openrazer + Solaar: + description: Application to control Logitech Mice, Keyboards, and Trackpads + default: false + packages: + - Install Solaar: ujust install-solaar + Wootility: + description: A configurator for Wooting keyboards + default: false + packages: + - Retrieve Wootility: ujust install-wootility + OpenTabletDriver: + description: Open source, cross-platform, user-mode tablet driver + default: false + packages: + - Install OpenTabletDriver: ujust install-opentabletdriver + Resilio Sync: + description: A file synchronization utility powered by BitTorrent + default: false + packages: + - Install Resilio Sync: ujust install-resilio-sync + scrcpy: + description: scrcpy provides display and control of Android devices connected on USB (or over TCP/IP) + default: false + packages: + - Install scrcpy: ujust install-scrcpy + SteamCMD: + description: Installs SteamCMD + default: true + packages: + - Install SteamCMD: ujust install-steamcmd + Use EXT4 for SD Cards: + description: Disable BTRFS support for SD cards for direct compatibility with SD cards formatted on stock SteamOS (Not recommended). + default: false + packages: + - Switch SteamOS-BTRFS config to EXT4: sudo -A ujust switch-to-ext4 + applications: + source: yafti.screen.package + values: + title: Application Installation + show_terminal: true + package_manager: yafti.plugins.flatpak + package_manager_defaults: + user: false + system: true + groups: + Web Browsers: + description: Additional browsers to complement Firefox + default: false + packages: + - Brave: com.brave.Browser + - Google Chrome: com.google.Chrome + - LibreWolf: io.gitlab.librewolf-community + - Microsoft Edge: com.microsoft.Edge + - Opera: com.opera.Opera + - Vivaldi: com.vivaldi.Vivaldi + Gaming: + description: "Rock and Stone!" + default: false + packages: + - BoilR: io.github.philipk.boilr + - Bottles: com.usebottles.bottles + - Chiaki4Deck (PlayStation Remote Play): io.github.streetpea.Chiaki4deck + - Discord (Discover Overlay Included): com.discordapp.Discord + - DOSBox Staging: io.github.dosbox-staging + - GeForce NOW Electron: io.github.hmlendea.geforcenow-electron + - Greenlight: io.github.unknownskl.greenlight + - Heroic Games Launcher (GOG & Epic): com.heroicgameslauncher.hgl + - itch: io.itch.itch + - ludusavi (Game Save Backup): com.github.mtkennerly.ludusavi + - Minecraft (Prism Launcher): org.prismlauncher.PrismLauncher + - Minecraft Bedrock Launcher: io.mrarm.mcpelauncher + - Moonlight: com.moonlight_stream.Moonlight + - Mumble: info.mumble.Mumble + - OpenMW: org.openmw.OpenMW + - osu: sh.ppy.osu + - Parsec: com.parsecgaming.parsec + - Space Cadet Pinball: com.github.k4zmu2a.spacecadetpinball + - Sonic Robo Blast 2: org.srb2.SRB2 + - Sonic Robo Blast 2 Kart: org.srb2.SRB2Kart + - Steam Link: com.valvesoftware.SteamLink + - SuperTux: org.supertuxproject.SuperTux + - SuperTuxKart: net.supertuxkart.SuperTuxKart + - TeamSpeak: com.teamspeak.TeamSpeak + - XIV Launcher (Final Fantasy XIV): dev.goats.xivlauncher + Emulation: + description: Play games like it's 1972 (Leave these all unchecked if you're planning to use EmuDeck) + default: false + packages: + - Cemu: info.cemu.Cemu + - Dolphin: org.DolphinEmu.dolphin-emu + - DuckStation: org.duckstation.DuckStation + - MAME: org.mamedev.MAME + - melonDS: net.kuribo64.melonDS + - mGBA: io.mgba.mGBA + - PCSX2: net.pcsx2.PCSX2 + - Parallel Launcher: ca.parallel_launcher.ParallelLauncher + - Pegasus: org.pegasus_frontend.Pegasus + - PPSSPP: org.ppsspp.PPSSPP + - RetroArch: org.libretro.RetroArch + - RetroDECK: net.retrodeck.retrodeck + - Rosalie's Mupen GUI: com.github.Rosalie241.RMG + - RPCS3: net.rpcs3.RPCS3 + - Ryujinx: org.ryujinx.Ryujinx + - ScummVM: org.scummvm.ScummVM + - Snes9x: com.snes9x.Snes9x + - Stella: io.github.stella_emu.Stella + - xemu: app.xemu.xemu + Streaming: + description: Stream to the Internet + default: false + packages: + - OBS Studio: com.obsproject.Studio + - Boatswain for Streamdeck: com.feaneron.Boatswain + Music: + description: "Rock and Roll!" + default: false + packages: + - Cider (Apple Music Client): sh.cider.Cider + - Spotify: com.spotify.Client + - Strawberry Music Player: org.strawberrymusicplayer.strawberry + - Tidal-hifi: com.mastermindzh.tidal-hifi + Office and Productivity: + description: Bow to Capitalism + default: false + packages: + - Ardour: org.ardour.Ardour + - Blender: org.blender.Blender + - darktable: org.darktable.Darktable + - GIMP: org.gimp.GIMP + - Inkscape: org.inkscape.Inkscape + - Joplin: net.cozic.joplin_desktop + - Kdenlive: org.kde.kdenlive + - Krita: org.kde.krita + - LibreOffice: org.libreoffice.LibreOffice + - Obsidian: md.obsidian.Obsidian + - OnlyOffice: org.onlyoffice.desktopeditors + - Planify: io.github.alainm23.planify + - Slack: com.slack.Slack + - Standard Notes: org.standardnotes.standardnotes + - Tenacity: org.tenacityaudio.Tenacity + - Thunderbird Email: org.mozilla.Thunderbird + - Xournal++: com.github.xournalpp.xournalpp + Utilities and System Tools: + description: Helpful tools + default: false + packages: + - AppImage Pool: io.github.prateekmedia.appimagepool + - Barrier: com.github.debauchee.barrier + - Bitwarden: com.bitwarden.desktop + - Calibre: com.calibre_ebook.calibre + - DejaDup: org.gnome.DejaDup + - Fedora Media Writer: org.fedoraproject.MediaWriter + - Gradience: com.github.GradienceTeam.Gradience + - KeePassXC: org.keepassxc.KeePassXC + - Metadata Cleaner: fr.romainvigier.MetadataCleaner + - OpenRGB: org.openrgb.OpenRGB + - Pika Backup: org.gnome.World.PikaBackup + - PinApp: io.github.fabrialberio.pinapp + - qBittorrent: org.qbittorrent.qBittorrent + - Resources: net.nokyan.Resources + - SaveDesktop: io.github.vikdevelop.SaveDesktop + - Syncthing: com.github.zocker_160.SyncThingy + - VLC: org.videolan.VLC + final-screen: + source: yafti.screen.title + values: + title: "All done!" + icon: "/usr/share/ublue-os/bazzite/logo.svg" + links: + - "Install More Applications": + run: /usr/bin/plasma-discover + - "Documentation": + run: /usr/bin/xdg-open https://universal-blue.discourse.group/docs?category=5 + - "Forums": + run: /usr/bin/xdg-open https://universal-blue.discourse.group/c/bazzite/5 + - "Join the Discord Community": + run: /usr/bin/xdg-open https://discord.gg/XjG48C7VHx + - "Reboot now": + run: systemctl reboot + description: | + Thank you for trying Bazzite (Steam Deck Edition). Please reboot to apply changes made by this setup utility. diff --git a/tests/bluefin.yml b/tests/bluefin.yml new file mode 100644 index 0000000..47d342c --- /dev/null +++ b/tests/bluefin.yml @@ -0,0 +1,92 @@ +title: Welcome to Bluefin +properties: + mode: "run-once" +screens: + first-screen: + source: yafti.screen.title + values: + title: "Welcome to Bluefin (Beta)" + icon: "/path/to/icon" + description: | + Applications are also installing in the background, the system will notify you when it is finished. + applications: + source: yafti.screen.package + values: + title: Application Installation + show_terminal: true + package_manager: yafti.plugins.flatpak + groups: + Communication: + default: false + description: Tools to communicate and collaborate + packages: + - Discord: com.discordapp.Discord + - Slack: com.slack.Slack + Development Tools: + description: Start your cloud-native journey here! + default: false + packages: + - GNOME Boxes: org.gnome.Boxes + - Podman Desktop: io.podman_desktop.PodmanDesktop + Gaming: + description: "Rock and Stone!" + default: false + packages: + - Bottles: com.usebottles.bottles + - Heroic Games Launcher: com.heroicgameslauncher.hgl + - Lutris: net.lutris.Lutris + - MangoHUD: org.freedesktop.Platform.VulkanLayer.MangoHud/x86_64/23.08 + - Steam: com.valvesoftware.Steam + - Proton Plus for Steam: com.vysp3r.ProtonPlus + Office: + description: Bow to Capitalism + default: false + packages: + - OnlyOffice: org.onlyoffice.desktopeditors + - LibreOffice: org.libreoffice.LibreOffice + - Obsidian: md.obsidian.Obsidian + - Standard Notes: org.standardnotes.standardnotes + - Thunderbird Email: org.mozilla.Thunderbird + Other Web Browsers: + description: Additional browsers to complement Firefox + default: false + packages: + - Brave: com.brave.Browser + - Google Chrome: com.google.Chrome + - Microsoft Edge: com.microsoft.Edge + - Opera: com.opera.Opera + Streaming: + description: Stream to the Internet + default: false + packages: + - OBS Studio: com.obsproject.Studio + - VkCapture for OBS: com.obsproject.Studio.OBSVkCapture + - Gstreamer for OBS: com.obsproject.Studio.Plugin.Gstreamer + - Gstreamer VAAPI for OBS: com.obsproject.Studio.Plugin.GStreamerVaapi + - Boatswain for Streamdeck: com.feaneron.Boatswain + Utilities: + description: Useful Utilities + default: true + packages: + - Font Downloader: org.gustavoperedo.FontDownloader + - PinApp Menu Editor: io.github.fabrialberio.pinapp + - Backup: org.gnome.DejaDup + - LocalSend: org.localsend.localsend_app + - Syncthing: com.github.zocker_160.SyncThingy + - Cockpit Client: org.cockpit_project.CockpitClient + final-screen: + source: yafti.screen.title + values: + title: "All done!" + icon: "/path/to/icon" + links: + - "Install More Applications": + run: /usr/bin/gnome-software + - "Documentation": + run: /usr/bin/xdg-open https://universal-blue.discourse.group/t/introduction-to-bluefin/41 + - "Discussions and Announcements": + run: /usr/bin/xdg-open https://universal-blue.discourse.group/c/bluefin/6 + - "Join the Discord Community": + run: /usr/bin/xdg-open https://discord.gg/XjG48C7VHx + description: | + Thanks for trying Bluefin, we hope you enjoy it! diff --git a/tests/example-multi-package.yml b/tests/example-multi-package.yml index 677cd28..38e515b 100644 --- a/tests/example-multi-package.yml +++ b/tests/example-multi-package.yml @@ -5,7 +5,7 @@ actions: pre: - run: /full/path/to/bin --with --params - run: /another/command run - - yafti.plugin.flatpak: + - yafti.plugins.flatpak: install: org.gnome.Calculator post: - run: /run/these/commands --after --all --screens @@ -34,7 +34,7 @@ screens: values: title: Install more flatpaks show_terminal: true - package_manager: yafti.plugin.flatpak + package_manager: yafti.plugins.flatpak packages: - Steam: com.valvesoftware.Steam - Games: org.gnome.Games @@ -43,7 +43,7 @@ screens: values: title: Install flatpaks show_terminal: true - package_manager: yafti.plugin.flatpak + package_manager: yafti.plugins.flatpak groups: Core: description: All the good stuff @@ -55,7 +55,7 @@ screens: values: title: Install more flatpaks show_terminal: true - package_manager: yafti.plugin.flatpak + package_manager: yafti.plugins.flatpak groups: Office: description: All the work stuff diff --git a/tests/example-save-state.yml b/tests/example-save-state.yml index 26470f9..bbf1f96 100644 --- a/tests/example-save-state.yml +++ b/tests/example-save-state.yml @@ -6,7 +6,7 @@ actions: pre: - run: /full/path/to/bin --with --params - run: /another/command run - - yafti.plugin.flatpak: + - yafti.plugins.flatpak: install: org.gnome.Calculator post: - run: /run/these/commands --after --all --screens @@ -35,7 +35,7 @@ screens: values: title: Install flatpaks show_terminal: true - package_manager: yafti.plugin.flatpak + package_manager: yafti.plugins.flatpak groups: Core: description: All the good stuff diff --git a/tests/example-system.yml b/tests/example-system.yml index f204af3..fd7cfd7 100644 --- a/tests/example-system.yml +++ b/tests/example-system.yml @@ -5,7 +5,7 @@ actions: pre: - run: /full/path/to/bin --with --params - run: /another/command run - - yafti.plugin.flatpak: + - yafti.plugins.flatpak: install: org.gnome.Calculator post: - run: /run/these/commands --after --all --screens @@ -34,7 +34,7 @@ screens: values: title: Package Installation show_terminal: true - package_manager: yafti.plugin.flatpak + package_manager: yafti.plugins.flatpak package_manager_defaults: user: false system: true diff --git a/tests/example.yml b/tests/example.yml index df47621..9ac434c 100644 --- a/tests/example.yml +++ b/tests/example.yml @@ -5,7 +5,7 @@ actions: pre: - run: /full/path/to/bin --with --params - run: /another/command run - - yafti.plugin.flatpak: + - yafti.plugins.flatpak: install: org.gnome.Calculator post: - run: /run/these/commands --after --all --screens @@ -34,7 +34,7 @@ screens: values: title: Install flatpaks show_terminal: true - package_manager: yafti.plugin.flatpak + package_manager: yafti.plugins.flatpak groups: Core: description: All the good stuff diff --git a/tests/test_plugin_run.py b/tests/test_plugin_run.py index 662b31e..3fb0fd3 100644 --- a/tests/test_plugin_run.py +++ b/tests/test_plugin_run.py @@ -1,7 +1,7 @@ import pytest from unittest.mock import patch from subprocess import CompletedProcess -from yafti.plugin.run import Run +from yafti.plugins.run import Run from pydantic import ValidationError diff --git a/tests/test_screen_package_state.py b/tests/test_screen_package_state.py deleted file mode 100644 index 5b7edbd..0000000 --- a/tests/test_screen_package_state.py +++ /dev/null @@ -1,82 +0,0 @@ -import pytest -from yafti.screen.package.state import PackageScreenState -from pydantic import ValidationError - - -def test_state_set(): - state = PackageScreenState("test_state_set") - state.set("hello", True) - assert state.get("hello") is True - - -def test_state_set_fail(): - state = PackageScreenState("test_state_set_fail") - with pytest.raises(ValidationError): - state.set("hello", "world") - - -def test_state_load(): - input = {"hello": True, "world": False} - state = PackageScreenState("test_state_load") - state.load(input) - assert state.get("hello") is True - assert state.get("world") is False - - -def test_state_remove(): - state = PackageScreenState("test_state_remove") - state.set("kenobi", False) - state.set("general", True) - assert state.get("kenobi") is False - assert state.get("general") is True - state.remove("kenobi") - assert state.get("kenobi") is None - assert state.get("general") is True - - -def test_state_on_off(): - state = PackageScreenState("test_state_on_off") - state.on("grievous") - assert state.get("grievous") is True - state.off("grievous") - assert state.get("grievous") is False - - state.off("ani") - assert state.get("ani") is False - state.on("ani") - assert state.get("ani") is True - - -def test_state_toggle(): - state = PackageScreenState("test_state_toggle") - state.on("chewy") - assert state.get("chewy") is True - state.toggle("chewy") - assert state.get("chewy") is False - state.toggle("chewy") - assert state.get("chewy") is True - - -def test_state_toggle_error(): - state = PackageScreenState("test_state_toggle_error") - with pytest.raises(KeyError): - state.toggle("barf") - - -def test_state_get_on(): - state = PackageScreenState("test_state_get_on") - state.on("chewy") - state.on("han") - state.off("greedo") - - assert state.get_on() == ["chewy", "han"] - assert state.get_on("ch") == ["chewy"] - - -def test_state_keys(): - state = PackageScreenState("test_state_keys") - state.on("AA") - state.on("BB") - state.off("CC") - - assert state.keys() == ["AA", "BB", "CC"] diff --git a/tests/test_screen_package_utils.py b/tests/test_screen_package_utils.py deleted file mode 100644 index 1147388..0000000 --- a/tests/test_screen_package_utils.py +++ /dev/null @@ -1,56 +0,0 @@ -from yafti.screen.package.utils import parse_packages - - -def test_parse_packages_groups(): - cfg = { - "Core": { - "description": "hello world", - "packages": [ - {"Calculator": "org.gnome.Calculator"}, - { - "Firefox": { - "package": "org.mozilla.firefox", - "system": True, - "user": False, - }, - }, - ], - }, - "Gaming": { - "description": "hello games", - "default": False, - "packages": [ - {"Steam": "com.valvesoftware.Steam"}, - {"Games": "org.gnome.Games"}, - ], - }, - } - - expected = { - "group:Core": True, - "pkg:org.gnome.Calculator": True, - 'pkg:{"package": "org.mozilla.firefox", "system": true, "user": false}': True, - "group:Gaming": True, - "pkg:com.valvesoftware.Steam": True, - "pkg:org.gnome.Games": True, - } - - assert expected == parse_packages(cfg) - - -def test_parse_packages_list(): - cfg = [ - {"Calculator": "org.gnome.Calculator"}, - {"Firefox": "org.mozilla.firefox"}, - {"Steam": "com.valvesoftware.Steam"}, - {"Games": "org.gnome.Games"}, - ] - - expected = { - "pkg:org.gnome.Calculator": True, - "pkg:org.mozilla.firefox": True, - "pkg:com.valvesoftware.Steam": True, - "pkg:org.gnome.Games": True, - } - - assert expected == parse_packages(cfg) diff --git a/yafti/__init__.py b/yafti/__init__.py index 16c7447..9151284 100644 --- a/yafti/__init__.py +++ b/yafti/__init__.py @@ -1,5 +1,5 @@ """ -Copyright 2023 Marco Ceppi +Copyright 2024 Alex Banna Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,3 +13,5 @@ See the License for the specific language governing permissions and limitations under the License. """ +__version__: str = "0.9.0-beta1" +__name__: str = "yafti" diff --git a/yafti/__main__.py b/yafti/__main__.py deleted file mode 100644 index e5ba677..0000000 --- a/yafti/__main__.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -Copyright 2023 Marco Ceppi - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import logging -from typing import Annotated - -import typer -import yaml - -import yafti.setup # noqa -from yafti import log -from yafti.app import Yafti -from yafti.parser import Config - - -def run( - config: typer.FileText = typer.Argument("/etc/yafti.yml"), - debug: bool = False, - force_run: Annotated[ - bool, typer.Option("-f", "--force", help="Ignore run mode and force run") - ] = False, -): - log.set_level(logging.DEBUG if debug else logging.INFO) - log.debug("starting up", config=config, debug=debug) - config = Config.parse_obj(yaml.safe_load(config)) - app = Yafti(config) - app.run(None, force_run=force_run) - - -def app(): - typer.run(run) - - -if __name__ == "__main__": - app() diff --git a/yafti/abc.py b/yafti/abc.py index e681487..ab13a3e 100644 --- a/yafti/abc.py +++ b/yafti/abc.py @@ -57,6 +57,7 @@ async def from_config(cls, cfg: Any): def activate(self): self.active = True + if hasattr(self, "on_activate"): if iscoroutinefunction(self.on_activate): asyncio.ensure_future(self.on_activate()) @@ -65,6 +66,7 @@ def activate(self): def deactivate(self): self.active = False + if hasattr(self, "on_deactivate"): if iscoroutinefunction(self.on_deactivate): asyncio.ensure_future(self.on_deactivate()) diff --git a/yafti/app.py b/yafti/app.py index 105539f..51c53a7 100644 --- a/yafti/app.py +++ b/yafti/app.py @@ -13,36 +13,62 @@ See the License for the specific language governing permissions and limitations under the License. """ - import hashlib +import os +from pathlib import Path import gbulb import yaml -from gi.repository import Adw -from pathlib import Path +from gi.repository import Adw, Gdk, Gio, Gtk -from yafti.parser import Config, YaftiRunModes, YaftSaveState -from yafti.screen.window import Window +from yafti import log +from yafti.parser import YaftiRunModes +from yafti.settings import Config +from yafti.views.content import Content +from yafti.views.sidebar import Sidebar +from yafti.windows.window import ApplicationWindow class Yafti(Adw.Application): - def __init__(self, cfg: Config = None, loop=None): - super().__init__(application_id="it.ublue.Yafti") - self.config = cfg + """ + Yafti main application + https://developer.gnome.org/documentation/tutorials/menus.html + """ + + def __init__(self, cfg: str = None, loop=None): + self.config = Config(cfg) + super().__init__(application_id=self.config.APP_ID) + + resource = Gio.Resource.load( + os.path.join(self.config.APP_ROOT, "gresource.gresource") + ) + + resource._register() + self.loop = loop or gbulb.get_event_loop() + @property + def temp_config(self) -> Config: + return self.config.system_config + def run(self, *args, force_run: bool = False, **kwargs): - configured_mode = self.config.properties.mode - _p: Path = self.config.properties.path.expanduser() + configured_mode = self.config.system_config.properties.mode + _p: Path = self.config.system_config.properties.path.expanduser() + + # TODO we need to maintain state of all installed applications and this hash should be moved to dconf/GSettings. + # this still needs to be cleaned up and moved to above mentioned. # TODO(GH-#103): Remove this prior to 1.0 release. Start. _old_p = Path("~/.config/yafti-last-run").expanduser() + if _old_p.exists() and _old_p.resolve() != _p.resolve(): if not _p.parent.is_dir(): _p.parent.mkdir(mode=0o755, parents=True, exist_ok=True) if _p.is_file(): _p.unlink() + _old_p.rename(_p) # TODO(GH-#103): End. + if not force_run: if configured_mode == YaftiRunModes.disable: return @@ -57,8 +83,57 @@ def run(self, *args, force_run: bool = False, **kwargs): super().run(*args, **kwargs) def do_activate(self): - self._win = Window(application=self) - self._win.present() + # self._win = Window(application=self) + # this needs to be set in the constructor + + log.debug("@@@@@@@@@@@@@@@ Application Entrypoint @@@@@@@@@@@@@@@@@@@@") + self.window = ApplicationWindow( + application=self, width_request=280, height_request=200 + ) + + ## GTK UI CSS Provider + self.css = Gtk.CssProvider() + + # pull from resources + self.css.load_from_path("yafti/assets/css/application.css") + Gtk.StyleContext.add_provider_for_display( + Gdk.Display.get_default(), self.css, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + ) + + # Establish the navigation split view object & properties + self.split_view = Adw.NavigationSplitView() + self.split_view.set_vexpand(True) + + # The breakpoint is used to hide the sidebar when the window is resized + self.breakpoint = Adw.Breakpoint.new( + condition=Adw.BreakpointCondition.parse("max-width: 450px") + ) + self.breakpoint.add_setter(self.split_view, property="collapsed", value=True) + self.window.add_breakpoint(self.breakpoint) + # In order to ge the split header bar view in the navigation split view, + # an invisible header bar is added to the window + # TODO: Check if there is a way to avoid this + self.dummy_header_bar = Adw.HeaderBar() + self.dummy_header_bar.set_visible(False) + + # The main window box is the container for the navigation split view + self.main_window_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) + self.main_window_box.set_vexpand(True) + self.main_window_box.append(self.dummy_header_bar) + self.main_window_box.append(self.split_view) + self.window.set_content(self.main_window_box) + + self.window.__setattr__("bundle_list", self.config.bundles) + # Set the sidebar and content panes + self.split_view.set_sidebar(Sidebar(self.window)) + self.split_view.set_content(Content()) + + on_deactivate = Gio.SimpleAction(name="quit") + on_deactivate.connect("activate", self.quit) + self.add_action(on_deactivate) + + self.window.connect("destroy", self.quit) + self.window.present() self.loop.run() @property @@ -66,21 +141,27 @@ def config_sha(self): return hashlib.sha256(yaml.dump(self.config.dict()).encode()).hexdigest() def sync_last_run(self): + """ """ p = self.config.properties.path.expanduser() if not p.parent.is_dir(): p.parent.mkdir(mode=0o755, parents=True, exist_ok=True) + p.write_text(self.config_sha) def quit(self, *args, **kwargs): + """ + https://python-gtk-3-tutorial.readthedocs.io/en/latest/builder.html#gtk-template + """ self.loop.stop() - if self.config.properties.save_state == YaftSaveState.always: - self.sync_last_run() - - if ( - self.config.properties.save_state == YaftSaveState.end - and self._win - and self._win.is_last_page - ): - self.sync_last_run() - super().quit() + # TODO: this was once used. will be deleted soon. + # This has been commented out during development + # if self.config.properties.save_state == YaftiSaveState.always: + # self.sync_last_run() + # + # if ( + # self.config.properties.save_state == YaftiSaveState.end + # and self._win + # and self._win.is_last_page + # ): + # self.sync_last_run() diff --git a/yafti/assets/bazzite.gif b/yafti/assets/bazzite.gif new file mode 100644 index 0000000..35bd419 Binary files /dev/null and b/yafti/assets/bazzite.gif differ diff --git a/yafti/assets/bazzite.webm b/yafti/assets/bazzite.webm new file mode 100644 index 0000000..ffadd4e Binary files /dev/null and b/yafti/assets/bazzite.webm differ diff --git a/yafti/assets/bluefin-small.png b/yafti/assets/bluefin-small.png new file mode 100644 index 0000000..58bf99a Binary files /dev/null and b/yafti/assets/bluefin-small.png differ diff --git a/yafti/assets/bluefin.svg b/yafti/assets/bluefin.svg new file mode 100644 index 0000000..ff9b6ce --- /dev/null +++ b/yafti/assets/bluefin.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + diff --git a/yafti/assets/css/application.css b/yafti/assets/css/application.css new file mode 100644 index 0000000..4ec9bee --- /dev/null +++ b/yafti/assets/css/application.css @@ -0,0 +1,117 @@ +/* + Contains the CSS for the Adw.ApplicationWindow which is an Adw.NavigationSplitView + + NavigationSplitView is the main widget used in the Adw library. It is a container + that allows you to have a sidebar and a content area. The sidebar can be collapsed + and expanded by the user. + + https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.NavigationSplitView.html#css-nodes + + When collapsed, the split view has the following CSS nodes: + + navigation-split-view + ╰── navigation-view + ├── [sidebar child] + ╰── [content child] + + When expanded, the split view has the following CSS nodes: + + navigation-split-view + ├── widget.sidebar-pane + │ ╰── [sidebar child] + ╰── widget.content-pane + ╰── [content child] +*/ + +/* ### Sidebar ### */ + +/* Expanded sidebar window focused */ +navigation-split-view widget.sidebar-pane navigation-view-page { + /* background-color: @headerbar_bg_color; */ /* Hides the sidebar separator */ + border-right-width: 5px; +} +navigation-split-view widget.sidebar-pane toolbarview list { + background-color: @headerbar_bg_color; + border-right-width: 1px; + padding-bottom: 0px; + padding-top: 0px; +} +navigation-split-view widget.sidebar-pane list { + padding-bottom: 6px; + padding-top: 6px; +} + +/* Expanded sidebar window deselected */ +navigation-split-view widget.sidebar-pane navigation-view-page:backdrop { + background-color: @sidebar_backdrop_color; + border-right-width: 1px; +} +navigation-split-view widget.sidebar-pane toolbarview list:backdrop { + background-color: @sidebar_backdrop_color; + border-right-width: 1px; + padding-bottom: 0px; + padding-top: 0px; +} + +/* Collapsed sidebar */ +navigation-split-view navigation-view-page toolbarview list { + background-color: @theme_bg_color; + border-right-width: 1px; + padding-bottom: 6px; + padding-top: 6px; +} + +/* Sidebar Action row button settings */ + +.action-row-rounded box box { + margin-top: 2px; + margin-bottom: 2px; +} +.action-row-rounded { + border-top-left-radius: 6px; + border-top-right-radius: 6px; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + min-height: 36px; + font-weight: 400; + line-height: 36px; + padding-bottom: 0px; + padding-top: 0px; + margin-bottom: 2px; + margin-top: 2px; +} +.action-row-rounded box { + min-height: 36px; + line-height: 36px; + padding-bottom: 0px; + padding-top: 0px; +} +.action-row-rounded:selected { + background-color: alpha(@theme_selected_fg_color, 0.1); + outline-color: alpha(@theme_selected_bg_color, 0.3); + outline-width: 1px; + outline-offset: -1px; + min-height: 36px; + font-weight: 400; + line-height: 36px; +} + + +/* ### Content Pane ### */ + +navigation-split-view widget.content-pane label.title { + font-weight: 500; +} + +navigation-split-view widget.sidebar-pane revealer.top-bar { + font-weight: 700; +} + +/* ### Named Colors ### */ +.cc_card_fg_color { + color: @theme_text_color; +} + +.cc_theme_selected_bg_color { + color: @theme_selected_bg_color; +} diff --git a/yafti/assets/devpod.svg b/yafti/assets/devpod.svg new file mode 100644 index 0000000..24f2acd --- /dev/null +++ b/yafti/assets/devpod.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/yafti/assets/distrobox.svg b/yafti/assets/distrobox.svg new file mode 100644 index 0000000..ef4cd35 --- /dev/null +++ b/yafti/assets/distrobox.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/yafti/assets/docker.svg b/yafti/assets/docker.svg new file mode 100644 index 0000000..7082d26 --- /dev/null +++ b/yafti/assets/docker.svg @@ -0,0 +1,41 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/yafti/assets/fedora.svg b/yafti/assets/fedora.svg new file mode 100644 index 0000000..ef5c3e3 --- /dev/null +++ b/yafti/assets/fedora.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/yafti/assets/flatpak_logo.svg b/yafti/assets/flatpak_logo.svg new file mode 100644 index 0000000..62b6719 --- /dev/null +++ b/yafti/assets/flatpak_logo.svg @@ -0,0 +1,130 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/yafti/assets/nest.png b/yafti/assets/nest.png new file mode 100644 index 0000000..a1d7682 Binary files /dev/null and b/yafti/assets/nest.png differ diff --git a/yafti/assets/pivot_raptor.png b/yafti/assets/pivot_raptor.png new file mode 100644 index 0000000..62527fb Binary files /dev/null and b/yafti/assets/pivot_raptor.png differ diff --git a/yafti/assets/podman.svg b/yafti/assets/podman.svg new file mode 100644 index 0000000..f3e1457 --- /dev/null +++ b/yafti/assets/podman.svg @@ -0,0 +1,519 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/yafti/assets/small_logo.png b/yafti/assets/small_logo.png new file mode 100644 index 0000000..ef045d2 Binary files /dev/null and b/yafti/assets/small_logo.png differ diff --git a/yafti/cli/__init__.py b/yafti/cli/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/yafti/cli/__main__.py b/yafti/cli/__main__.py new file mode 100644 index 0000000..ecc674c --- /dev/null +++ b/yafti/cli/__main__.py @@ -0,0 +1,3 @@ +from yafti.cli.main import app + +app() diff --git a/yafti/cli/main.py b/yafti/cli/main.py new file mode 100644 index 0000000..94c2507 --- /dev/null +++ b/yafti/cli/main.py @@ -0,0 +1,121 @@ +import logging +import subprocess +import sys +from pprint import pprint +from typing import Annotated + +import typer + +import yafti.setup # noqa +from yafti import __version__, log +from yafti.app import Yafti + +CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"]) +app = typer.Typer(context_settings=CONTEXT_SETTINGS, help="yafti command line tool") + + +@app.callback() +def callback(ctx: typer.Context) -> None: + """callback""" + pass + + +@app.command() +def version(ctx: typer.Context) -> None: + """ + Show yafti version + """ + typer.echo(f"Platform: {sys.platform}") + typer.secho(f"Python Version: {sys.version}") + typer.secho(f"Yafti Version: {__version__}") + + +@app.command(name="start") +def start( + config: typer.FileText = typer.Argument("/etc/yafti.yml"), + debug: bool = False, + force_run: Annotated[ + bool, typer.Option("-f", "--force", help="Ignore run mode and force run") + ] = False, +) -> None: + """ + Run yafti using the config file + """ + log.set_level(logging.DEBUG if debug else logging.INFO) + log.debug("starting up", config=config, debug=debug) + try: + attempt = Yafti(config) + attempt.run(None, force_run=force_run) + except Exception as e: + log.error("unable to run yafti: ", e) + sys.exit(1) + + +# TODO: this needs to be cleaned up etc. +@app.command(name="fp-info") +def flatpak_info(fp_path: str = "/usr/bin/flatpak"): + """ + Print flatpak info as json + """ + results = __exec_cmd(fp_path) + + decoded_current = results.decode("utf-8").replace("current packages: ", "") + headers = ["ref", "name", "runtime", "installation", "version", "options"] + + pkg_list = [] + for p in decoded_current.splitlines(): + turds = p.split("\t") + + with_headers = {headers[i]: t for i, t in enumerate(turds)} + pkg_list.append(with_headers.copy()) + + for i in pkg_list: + cares = None + opts = i.get("options") + if "," in opts: + chunks = opts.split(",") + prefix = chunks[1].strip() + if prefix == "current": + prefix = "app" + + cares = f"{prefix}/{i.get('ref')}" + + if cares is not None: + info = __exec_remote_info(fp_path, i.get("ref").strip()) + info["cares"] = cares + pprint(info) + + +def __exec_cmd(fp_path: str) -> str: + results = subprocess.run( + [fp_path, "list", "--columns=ref,name,runtime,installation,version,options"], + capture_output=True, + ) + + return results.stdout if results.returncode == 0 else "" + + +def __exec_remote_info(fp_path: str, ref: str): + results = subprocess.run( + [fp_path, "remote-info", "flathub", ref], capture_output=True + ) + + decoded_current = results.stdout.decode("utf-8").replace(ref, "") + pkg_info = {} + for p in decoded_current.splitlines(): + lines = p.strip().split("\t") + for li in lines: + if li: + chunks = li.split(":") + if len(chunks) == 2: + k, v = li.split(":") + pkg_info[k.strip().lower()] = v.strip().lower() + else: + # likely a date, skipping can clean up later + continue + + return pkg_info + + +if __name__ == "__main__": + typer.run(app) diff --git a/yafti/core/__init__.py b/yafti/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/yafti/core/apply.py b/yafti/core/apply.py new file mode 100644 index 0000000..3bee6b6 --- /dev/null +++ b/yafti/core/apply.py @@ -0,0 +1,301 @@ +import base64 +import json +import time + +# for localization +from gettext import gettext as _ # noqa +from typing import Optional + +from gi.repository import Adw, Gdk, Gio, GLib, Gtk, Pango, Vte + +from yafti import log +from yafti.abc import YaftiScreen, YaftiScreenConfig +from yafti.core.models import PackageConfig, PackageGroupConfig +from yafti.core.tasks import YaftiTasks +from yafti.views.tour import YaftiTour + + +@Gtk.Template(filename="yafti/gtk/progress.ui") +class YaftiProgress(YaftiScreen, Gtk.Box): + __gtype_name__ = "YaftiProgress" + + carousel_tour = Gtk.Template.Child() + tour_button = Gtk.Template.Child() + toasts = Gtk.Template.Child() + tour_box = Gtk.Template.Child() + tour_btn_back = Gtk.Template.Child() + tour_btn_next = Gtk.Template.Child() + progressbar = Gtk.Template.Child() + console_button = Gtk.Template.Child() + console_box = Gtk.Template.Child() + console_output = Gtk.Template.Child() + install_button = Gtk.Template.Child() + + class Config(YaftiScreenConfig): + title: str + show_terminal: bool = True + package_manager: str + groups: Optional[PackageGroupConfig] = None + packages: Optional[list[PackageConfig]] = None + package_manager_defaults: Optional[dict] = None + + def __init__(self, title, window, package_list, **kwargs): + """ + Installing + """ + super().__init__(**kwargs) + self.__window = window + + # TODO: needs to be retrieved from yafti config + self.__tour = json.loads( + b""" + { + "apply": { + "resource": "/org/ublue-os/yafti/assets/bluefin-small.png", + "title": "Apply Changes to initialize!", + "description": "Your scientists were so preoccupied with whether or not they could, they did\'t stop to think if they should." + }, + "control": { + "resource": "/org/ublue-os/yafti/assets/pivot_raptor.png", + "title": "Evaluation", + "description": "Featuring automatic image-based updates and a simple graphical application store, Bluefin is designed to get out of your way. Get what you want without sacrificing system stability. The Linux client has evolved." + }, + "containerized": { + "resource": "/org/ublue-os/yafti/assets/flatpak_logo.svg", + "title": "Flatpaks", + "description": "we got you!" + }, + "completed": { + "resource": "/org/ublue-os/yafti/assets/bluefin.svg", + "title": "Our Mission", + "description": "Bluefin is designed to be the tool you depend on to do what you do best. Bluefin is about sustainability, encompassing the software, the hardware, and the people." + } + } + """ + ) + + self.__app = Gio.Application.get_default() + self.__title = title + self.__package_list = package_list or [] + self.__terminal = Vte.Terminal() + self.__pty = Vte.Pty().new_sync(Vte.PtyFlags.DEFAULT) + self.__terminal.set_pty(self.__pty) + self.__font = Pango.FontDescription() + self.__font.set_family("Monospace") + self.__font.set_size(13 * Pango.SCALE) + self.__font.set_weight(Pango.Weight.NORMAL) + self.__font.set_stretch(Pango.Stretch.NORMAL) + self.style_manager = self.__window.style_manager + self.__build_ui() + self.__on_setup_terminal_colors() + self.style_manager.connect("notify::dark", self.__on_setup_terminal_colors) + self.tour_button.connect("clicked", self.__on_tour_button) + self.tour_btn_back.connect("clicked", self.__on_tour_back) + self.tour_btn_next.connect("clicked", self.__on_tour_next) + self.install_button.connect("clicked", self.__apply) + self.carousel_tour.connect("page-changed", self.__on_page_changed) + self.console_button.connect("clicked", self.__on_console_button) + + def __on_setup_terminal_colors(self, *args): + """ """ + is_dark: bool = self.style_manager.get_dark() + + palette = [ + "#363636", + "#c01c28", + "#26a269", + "#a2734c", + "#12488b", + "#a347ba", + "#2aa1b3", + "#cfcfcf", + "#5d5d5d", + "#f66151", + "#33d17a", + "#e9ad0c", + "#2a7bde", + "#c061cb", + "#33c7de", + "#ffffff", + ] + + FOREGROUND = palette[0] + BACKGROUND = palette[15] + FOREGROUND_DARK = palette[15] + BACKGROUND_DARK = palette[0] + + self.fg = Gdk.RGBA() + self.bg = Gdk.RGBA() + + self.colors = [Gdk.RGBA() for c in palette] + [color.parse(s) for (color, s) in zip(self.colors, palette)] + + if is_dark: + self.fg.parse(FOREGROUND_DARK) + self.bg.parse(BACKGROUND_DARK) + else: + self.fg.parse(FOREGROUND) + self.bg.parse(BACKGROUND) + + self.__terminal.set_colors(self.fg, self.bg, self.colors) + + def __on_tour_button(self, *args): + self.tour_box.set_visible(True) + self.console_box.set_visible(False) + self.tour_button.set_visible(False) + self.console_button.set_visible(True) + + def __on_tour_back(self, *args): + cur_index = self.carousel_tour.get_position() + page = self.carousel_tour.get_nth_page(cur_index - 1) + self.carousel_tour.scroll_to(page, True) + + def __on_tour_next(self, *args): + cur_index = self.carousel_tour.get_position() + page = self.carousel_tour.get_nth_page(cur_index + 1) + self.carousel_tour.scroll_to(page, True) + + def __on_page_changed(self, *args): + position = self.carousel_tour.get_position() + pages = self.carousel_tour.get_n_pages() + + self.tour_btn_back.set_visible(position < pages and position > 0) + self.tour_btn_next.set_visible(position < pages - 1) + + def __on_console_button(self, *args): + self.tour_box.set_visible(False) + self.console_box.set_visible(True) + self.tour_button.set_visible(True) + self.console_button.set_visible(False) + + def __build_ui(self): + self.__terminal.set_cursor_blink_mode(Vte.CursorBlinkMode.ON) + self.__terminal.set_font(self.__font) + self.__terminal.set_mouse_autohide(True) + self.__terminal.set_input_enabled(False) + self.console_output.append(self.__terminal) + self.__terminal.connect("child-exited", self.on_vte_child_exited) + + for k, tour in self.__tour.items(): + self.carousel_tour.append(YaftiTour(self.__window, tour)) + + self.__start_tour() + + def __switch_tour(self, *args): + cur_index = self.carousel_tour.get_position() + 1 + if cur_index == self.carousel_tour.get_n_pages(): + cur_index = 0 + + page = self.carousel_tour.get_nth_page(cur_index) + self.carousel_tour.scroll_to(page, True) + + def __start_tour(self): + log.debug("@@@@@ Tour Starting @@@@@") + + def squirrel(): + while True: + GLib.idle_add(self.progressbar.pulse) + GLib.idle_add(self.__switch_tour) + time.sleep(10) + + # TODO: add callback + YaftiTasks(squirrel, None) + + def on_vte_child_exited(self, terminal, status, *args): + terminal.get_parent().remove(terminal) + # to invert the status to get the correct result. + status = not bool(status) + # TODO: set results + + def start(self, cmd, *fn_args): + log.debug("@@@@@@ Starting to apply changes @@@@@@") + if not cmd: + self.__window.set_installation_result(False, None) + return None + + self.__terminal.pty_new_sync(Vte.PtyFlags.DEFAULT) + self.__pty.new_sync(Vte.PtyFlags.DEFAULT, None) + try: + self.__pty.spawn_async( + None, + cmd, + None, + GLib.SpawnFlags.DO_NOT_REAP_CHILD, + None, + None, + -1, + None, + self.ready, + ) + + except AttributeError: + # See issue #1. + self.__terminal.spawn_sync( + Vte.PtyFlags.DEFAULT, + None, + cmd, + None, + GLib.SpawnFlags.CLOEXEC_PIPES, + None, + self.ready, + ) + + def __apply(self, button): + log.debug("Apply Changes, somethings...") + self.progressbar.set_visible(True) + self.console_button.set_visible(True) + button.set_visible(False) + # application = Gio.Application.get_default() + # package_list = application.config.settings.included_packages + + included_packages = self.__app.config.settings.get_value("included-packages") + bpkg_data = included_packages.get_data_as_bytes().get_data().decode("utf-8") + try: + pkg_data = json.loads(base64.b64decode(bpkg_data)) + except json.JSONDecodeError: + pkg_data = {} + pass + + self.toast(_("{} Launched. Applying changes...").format("YAFTI Installer")) + # plug_and_play = PLUGINS.get(self.__app.config.package_manager) + real_stupid = [b"/usr/bin/flatpak", "install", "--system", "-y", "--or-update"] + real_stupid_remove = [b"/usr/bin/flatpak", "remove", "-y", "--system"] + for k, v in pkg_data.items(): + action = v.get("action") + if action is None: + log.debug(f"noop --- {k}") + continue + + elif action == "install": + log.debug(f"install ----- {k}") + real_stupid.append(k) + + elif action == "uninstall": + log.debug(f"uninstall ----- {k}") + real_stupid_remove.append(k) + else: + raise RuntimeError(f"Unknown action: {action}") + + if len(real_stupid_remove) > 4: + self.start(real_stupid_remove) + elif len(real_stupid) > 5: + self.start(real_stupid) + else: + pass + + def set_content(self, button): + self.progressbar.set_visible(False) + self.console_button.set_visible(False) + # button.set_visible(True) + # button.set_text("Apply Changes") + content = Gio.Application.get_default().split_view.get_content() + content.pane.set_content(self) + content.set_title("Installation") + + def toast(self, message, timeout=2): + toast = Adw.Toast.new(message) + toast.props.timeout = timeout + self.toasts.add_toast(toast) + + def ready(self, pty, task): + pty.spawn_finish(task) diff --git a/yafti/core/collections.py b/yafti/core/collections.py new file mode 100644 index 0000000..f997567 --- /dev/null +++ b/yafti/core/collections.py @@ -0,0 +1,17 @@ +from typing import Dict, List + + +class CollectionsMapper: + def __init__(self, **kwargs): + self.title: str + self.tasks: Dict + + super().__init__(self, **kwargs) + + @classmethod + def convert_to_collection(self, **kwargs): + return CollectionsMapper(**kwargs) + + @classmethod + def from_collection(cls, collections: List): + return {} diff --git a/yafti/core/finished.py b/yafti/core/finished.py new file mode 100644 index 0000000..c1bc5f3 --- /dev/null +++ b/yafti/core/finished.py @@ -0,0 +1,18 @@ +from gi.repository import Adw, Gtk + +# TODO: MAKE WORK + + +@Gtk.Template(filename="yafti/screen/assets/finished.ui") +class YaftiFinished(Adw.Bin): + __gtype_name__ = "YaftiFinished" + + status_page = Gtk.Template.Child() + btn_reboot = Gtk.Template.Child() + btn_close = Gtk.Template.Child() + btn_log = Gtk.Template.Child() + + def __init__(self, window, **kwargs): + super().__init__(**kwargs) + self.__window = window + self.__log = None diff --git a/yafti/screen/package/models.py b/yafti/core/models.py similarity index 100% rename from yafti/screen/package/models.py rename to yafti/core/models.py diff --git a/yafti/screen/package/state.py b/yafti/core/state.py similarity index 98% rename from yafti/screen/package/state.py rename to yafti/core/state.py index 4b465a1..5bc7493 100644 --- a/yafti/screen/package/state.py +++ b/yafti/core/state.py @@ -1,5 +1,7 @@ from pydantic import validate_arguments +# TODO remove or use this + class PackageScreenState: __slots__ = ["state"] @@ -9,6 +11,7 @@ def __new__(cls, id: str): cls.instances = {} if id not in cls.instances: cls.instances[id] = super(PackageScreenState, cls).__new__(cls) + return cls.instances[id] def __init__(self, id: str): diff --git a/yafti/core/tasks.py b/yafti/core/tasks.py new file mode 100644 index 0000000..f7cb8c9 --- /dev/null +++ b/yafti/core/tasks.py @@ -0,0 +1,80 @@ +import asyncio +import itertools +import sys +import threading +import time +import traceback + +from gi.repository import GLib + +from yafti import log + + +class YaftiTasks(threading.Thread): + """ + Task that will run in the background + """ + + def __init__(self, task_func, callback=None, *args, **kwargs): + self.source_id = None + super().__init__(target=self.__target, args=args, kwargs=kwargs) + self.task_func = task_func + self.callback = callback if callback else lambda r, e: None + self.daemon = kwargs.pop("daemon", True) + asyncio.new_event_loop() + + self.start() + + def __target(self, *args, **kwargs): + result = None + error = None + + log.debug(f"Task running: {self.task_func}.") + try: + result = self.task_func(*args, **kwargs) + except Exception as exception: + log.error( + f"Error while running Task: {self.task_func}\nException: {exception}" + ) + + error = exception + _ex_type, _ex_value, trace = sys.exc_info() + traceback.print_tb(trace) + + self.source_id = GLib.idle_add(self.callback, result, error) + + return self.source_id + + def silly(self, callback, *args, **kwargs): + loop = asyncio.new_event_loop() + try: + results = loop.run_until_complete(self.__async_callback(callback)) + return results + finally: + loop.close() + + async def __async_callback(self, callback): + """ + This will run the callback function asynchronously + """ + retry_count = itertools.count() + while True: + try: + loop = asyncio.get_running_loop() + + if loop.is_running(): + async with asyncio.TaskGroup() as tg: + task = tg.create_task(callback) + + self.__for_justice = task.result() + except RuntimeError as e: + if next(retry_count) >= 5: + time.sleep(30) + continue + else: + print("failed on max retries.") + raise e + except Exception as e: + print("unrecoverable error") + raise e + break diff --git a/yafti/events.py b/yafti/events.py index b61b3ed..08e61e3 100644 --- a/yafti/events.py +++ b/yafti/events.py @@ -36,6 +36,7 @@ def register(event_name): raise EventAlreadyRegisteredError( "event is already registered", event=event_name ) + _listeners[event_name] = [] @@ -44,12 +45,14 @@ def on(event_name: str, fn: Any): raise EventNotRegisteredError("event name not registered", event=event_name) if fn in _listeners[event_name]: return + _listeners[event_name].insert(0, fn) def detach(event_name: str, fn: Any): if event_name not in _listeners: raise EventNotRegisteredError("event name not registered", event=event_name) + _listeners[event_name].remove(fn) diff --git a/yafti/gresource.gresource b/yafti/gresource.gresource new file mode 100644 index 0000000..78a4e0f Binary files /dev/null and b/yafti/gresource.gresource differ diff --git a/yafti/gresource.xml b/yafti/gresource.xml new file mode 100644 index 0000000..fb2e5cc --- /dev/null +++ b/yafti/gresource.xml @@ -0,0 +1,19 @@ + + + + gtk/shortcuts.ui + gtk/window.ui + gtk/progress.ui + gtk/tour.ui + + assets/pivot_raptor.png + assets/bluefin.svg + assets/bluefin-small.png + assets/nest.png + assets/small_logo.png + assets/flatpak_logo.svg + assets/podman.svg + assets/bazzite.gif + assets/css/application.css + + diff --git a/yafti/gschemas.compiled b/yafti/gschemas.compiled new file mode 100644 index 0000000..c71886a Binary files /dev/null and b/yafti/gschemas.compiled differ diff --git a/yafti/gtk/confirm.ui b/yafti/gtk/confirm.ui new file mode 100644 index 0000000..01b20ac --- /dev/null +++ b/yafti/gtk/confirm.ui @@ -0,0 +1,60 @@ + + + + + + diff --git a/yafti/gtk/consent.ui b/yafti/gtk/consent.ui new file mode 100644 index 0000000..46996b8 --- /dev/null +++ b/yafti/gtk/consent.ui @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/yafti/gtk/console.ui b/yafti/gtk/console.ui new file mode 100644 index 0000000..7014170 --- /dev/null +++ b/yafti/gtk/console.ui @@ -0,0 +1,23 @@ + + + + + + diff --git a/yafti/screen/dialog.py b/yafti/gtk/dialog.ui similarity index 68% rename from yafti/screen/dialog.py rename to yafti/gtk/dialog.ui index 8b0766a..b1e3a07 100644 --- a/yafti/screen/dialog.py +++ b/yafti/gtk/dialog.ui @@ -1,6 +1,3 @@ -from gi.repository import Adw, Gtk - -_xml = """\ @@ -31,24 +28,4 @@ - -""" - - -@Gtk.Template(string=_xml) -class DialogBox(Adw.Window): - __gtype_name__ = "YaftiDialog" - - def __init__(self, parent=None, **kwargs): - super().__init__(**kwargs) - if parent: - self.set_transient_for(parent) - - sc = Gtk.ShortcutController.new() - sc.add_shortcut( - Gtk.Shortcut.new( - Gtk.ShortcutTrigger.parse_string("Escape"), - Gtk.CallbackAction.new(lambda x: self.hide()), - ) - ) - self.add_controller(sc) + \ No newline at end of file diff --git a/yafti/gtk/packages.ui b/yafti/gtk/packages.ui new file mode 100644 index 0000000..180de58 --- /dev/null +++ b/yafti/gtk/packages.ui @@ -0,0 +1,32 @@ + + + + + + diff --git a/yafti/gtk/progress.ui b/yafti/gtk/progress.ui new file mode 100644 index 0000000..289bbcb --- /dev/null +++ b/yafti/gtk/progress.ui @@ -0,0 +1,185 @@ + + + + + + \ No newline at end of file diff --git a/yafti/gtk/shortcuts.ui b/yafti/gtk/shortcuts.ui new file mode 100644 index 0000000..b92dc7b --- /dev/null +++ b/yafti/gtk/shortcuts.ui @@ -0,0 +1,41 @@ + + + + True + + + shortcuts + 10 + + + General + + + Show shortcuts + win.show-help-overlay + + + + + Filter to show all provided packages + win.filter('All') + + + + + Filter to show only outdated or uninstalled packages + win.filter('Open') + + + + + Filter to show only completed installed and out of date packages + win.filter('Done') + + + + + + + + diff --git a/yafti/gtk/test.ui b/yafti/gtk/test.ui new file mode 100644 index 0000000..026031b --- /dev/null +++ b/yafti/gtk/test.ui @@ -0,0 +1,49 @@ + + + + + diff --git a/yafti/gtk/title.ui b/yafti/gtk/title.ui new file mode 100644 index 0000000..93f3fa8 --- /dev/null +++ b/yafti/gtk/title.ui @@ -0,0 +1,19 @@ + + + + + + diff --git a/yafti/gtk/tour.ui b/yafti/gtk/tour.ui new file mode 100644 index 0000000..a986889 --- /dev/null +++ b/yafti/gtk/tour.ui @@ -0,0 +1,22 @@ + + + + + + diff --git a/yafti/gtk/window.ui b/yafti/gtk/window.ui new file mode 100644 index 0000000..9a843ee --- /dev/null +++ b/yafti/gtk/window.ui @@ -0,0 +1,183 @@ + + + + + Filter + + All + win.filter + All + + + Open + win.filter + Open + + + Done + win.filter + Done + + + + Done Tasks + win.remove-done-tasks + + + Shortcuts + win.show-help-overlay + + + + diff --git a/yafti/log.py b/yafti/log.py index 24f2f6d..41dffdf 100644 --- a/yafti/log.py +++ b/yafti/log.py @@ -4,6 +4,8 @@ _l = logging.getLogger("yafti") +# TODO: this generally needs an update to fix the _fmt issues and support async + def _fmt(msg: dict) -> str: return " ".join([f"{k}={v}" for k, v in msg.items()]) @@ -22,7 +24,7 @@ def info(message, **kwargs): def warn(message, *kwargs): - _l.warn(f"{message} {_fmt(kwargs)}") + _l.warning(f"{message} {_fmt(kwargs)}") def error(message, *kwargs): diff --git a/yafti/org.ublue-os.yafti.gschema.xml b/yafti/org.ublue-os.yafti.gschema.xml new file mode 100644 index 0000000..a1d3ee4 --- /dev/null +++ b/yafti/org.ublue-os.yafti.gschema.xml @@ -0,0 +1,79 @@ + + + + + 800 + + + 800 + + + false + + + false + + + false + + + + + 1.0 + + + false + + + false + + + + "" + The check sum of the yafti configuration file. + + System defined configuration via the yafti.yaml config file found /etc/yafti.yaml. + + + + + "" + Apps that the user included during installation. + + A list applications that the user installed when using yafti or independently installed + by the user and defined in the yafti.yaml config file found /etc/yafti.yaml. + + + + + "" + Apps the user has installed on the system. + + A list applications that the user installed using yafti or independently. + + + + + false + + + + + + + + 'json' + + + + + + + + + + 'All' + Filtering for Packages + + + diff --git a/yafti/parser.py b/yafti/parser.py index 0ec6754..27c7e98 100644 --- a/yafti/parser.py +++ b/yafti/parser.py @@ -21,6 +21,8 @@ import yaml from pydantic import BaseModel +# TODO: need to update or remove pydantic + class ActionConfig(BaseModel): pre: Optional[list[dict[str, str | dict]]] @@ -38,7 +40,7 @@ class YaftiRunModes(str, Enum): disable = "disabled" -class YaftSaveState(str, Enum): +class YaftiSaveState(str, Enum): always = "always" end = "last-screen" @@ -46,7 +48,7 @@ class YaftSaveState(str, Enum): class YaftiProperties(BaseModel): path: Optional[Path] = Path("~/.config/yafti/last-run") mode: YaftiRunModes = YaftiRunModes.changed - save_state: YaftSaveState = YaftSaveState.always + save_state: YaftiSaveState = YaftiSaveState.always class Config(BaseModel): @@ -60,4 +62,5 @@ def parse(config_file: str) -> Config: """Parse the YAML or JSON file passed and return a rendered Config object""" with open(config_file) as f: cfg = yaml.safe_load(f) + return Config.parse_obj(cfg) diff --git a/yafti/plugins/__init__.py b/yafti/plugins/__init__.py new file mode 100644 index 0000000..6a076b6 --- /dev/null +++ b/yafti/plugins/__init__.py @@ -0,0 +1 @@ +# TODO: turn this into proper plugins and make the interface easy for others to add new packages mangers. diff --git a/yafti/plugin/flatpak.py b/yafti/plugins/flatpak.py similarity index 59% rename from yafti/plugin/flatpak.py rename to yafti/plugins/flatpak.py index a6a5580..3e8f17d 100644 --- a/yafti/plugin/flatpak.py +++ b/yafti/plugins/flatpak.py @@ -22,28 +22,28 @@ commands: pre: # Install a Flatpak package - - yafti.plugin.flatpak: com.github.marcoceppi.PackageName + - yafti.plugins.flatpak: com.github.marcoceppi.PackageName # Explicit install - - yafti.plugin.flatpak: + - yafti.plugins.flatpak: install: com.github.marcoceppi.PackageName # Install with options - - yafti.plugin.flatpak: + - yafti.plugins.flatpak: install: pkg: com.github.marcoceppi.PackageName update: true user: true # Remove a flatpak package - - yafti.plugin.flatpak: + - yafti.plugins.flatpak: remove: com.github.marcoceppi.PackageName # Remove with options - - yafti.plugin.flatpak: + - yafti.plugins.flatpak: remove: pkg: com.github.marcoceppi.PackageName force: true Programmatic usage example: - from yafti.plugin.flatpak import Flatpak + from yafti.plugins.flatpak import Flatpak f = Flatpak() f.install("com.github.marcoceppi.PackageName") f.install(pkg="com.github.marcoceppi.PackageName", reinstall=True) @@ -60,12 +60,15 @@ """ import asyncio -from typing import Any, Optional +from typing import Any, List, Optional from pydantic import BaseModel, ValidationError, root_validator +from yafti import log from yafti.abc import YaftiPluginReturn -from yafti.plugin.run import Run +from yafti.plugins.run import Run + +# TODO: refactor class ApplicationDetail(BaseModel): @@ -78,6 +81,19 @@ class ApplicationDetail(BaseModel): installation: str +class FlatpakListItem(BaseModel): + """Flatpak application list results""" + + application: str + ref: str + name: str + runtime: str + installation: str + options: str + origin: str + version: str + + class FlatpakException(Exception): """Flatpak binary encountered a problem""" @@ -155,7 +171,7 @@ async def install( system: bool = False, assumeyes: bool = True, reinstall: bool = False, - noninteractive: bool = True, + noninteractive: bool = False, update: bool = True, ) -> YaftiPluginReturn: """Install flatpak package on the host system @@ -180,9 +196,10 @@ async def install( update=update, noninteractive=noninteractive, ) - cmd = [self.bin, "install"] + cmd = [self.bin, "install", "-y"] cmd.extend(args) cmd.append(package) + return await self.exec(" ".join(cmd)) async def remove( @@ -191,33 +208,172 @@ async def remove( user: bool = False, system: bool = True, force: bool = False, - noninteractive: bool = True, + noninteractive: bool = False, ) -> YaftiPluginReturn: """Remove flatpak package on the host system""" args = self._parse_args( user=user, system=system, force=force, noninteractive=noninteractive ) - cmd = [self.bin, "remove"] + cmd = [self.bin, "remove", "-y"] cmd.extend(args) cmd.append(package) - return self.exec(cmd) - def ls(self) -> list[ApplicationDetail]: + return await self.exec(" ".join(cmd)) + + async def list(self) -> list[FlatpakListItem]: + """ + list flatpak packages: https://flatpak.readthedocs.io/en + + supported columns: + Name + Description + Application + ID + Version + Branch + Arch + Origin + Installation + Ref + Active + commit + Latest + commit + Installed + size + Options + + :return: list of Flatpack list + + """ + headers = [ + "application", + "ref", + "name", + "runtime", + "installation", + "version", + "options", + "origin", + ] + cmd = [ + self.bin, + "list", + f" --columns={','.join(headers)}", + ] + + results = await self.exec(" ".join(cmd)) + if results.returncode != 0: + raise FlatpakException( + f"{cmd} returned non-zero." + f"return code: {results.returncode}" + f"stdout: {results.stdout}" + f"stderr: {results}" + ) + + decoded = results.stdout.decode("utf-8").replace("current packages: ", "") + package_list = await self.__parse_package_list(headers, decoded) + + return package_list + + async def ls(self): + """ + ls flatpak packages: https://flatpak.readthedocs.io/en + + supported columns: + Name + Description + Application + ID + Version + Branch + Arch + Origin + Installation + Ref + Active + commit + Latest + commit + Installed + size + Options + """ + headers = [ + "application", + "ref", + "name", + "runtime", + "installation", + "version", + "options", + ] + cmd = [ + self.bin, + "list", + f" --columns={','.join(headers)}", + ] + + return await self.exec(" ".join(cmd)) + + async def __parse_stdout(self, line: str): pass + async def __parse_package_list( + self, headers: List[str], response: str + ) -> List[FlatpakListItem]: + _package_list = [] + for p in response.splitlines(): + chunks = p.split("\t") + with_headers = {headers[i]: t for i, t in enumerate(chunks)} + + _package_list.append(with_headers.copy()) + + _results = None + if isinstance(_package_list, list) and len(_package_list) > 0: + _results = [FlatpakListItem(**p) for p in _package_list] + else: + log.info("No flat packages found") + _package_list = [] + + return _results + def __call__(self, options) -> YaftiPluginReturn: try: params = self.validate(options) except ValidationError as e: + log.error(f"plugin:flatpak:__call__ {str(e)}") return YaftiPluginReturn(errors=str(e), code=1) # TODO: when a string is passed, make sure it maps to the "pkg" key. if params.install: if isinstance(params.install, str): params.install = {"package": params.install} + r = asyncio.ensure_future(self.install(**params.install)) else: if isinstance(params.remove, str): params.remove = {"package": params.remove} + r = asyncio.ensure_future(self.remove(**params.install)) + + # log.debug(**params.install) return YaftiPluginReturn(output=r.stdout, errors=r.stderr, code=r.returncode) + + # TODO: this should be the way... + async def build_command( + self, pkg: str, auto_approve: bool = False, force: bool = False, **kwargs + ): + """ + Build a flat pak package on the host system + """ + # build rules + # install = { + # "base": "install", + # } + # + # user = kwargs.get("user", False) + # system = kwargs.get("system", False) + # noninteractive = kwargs.get("noninteractive", False) + + raise NotImplementedError diff --git a/yafti/plugin/run.py b/yafti/plugins/run.py similarity index 87% rename from yafti/plugin/run.py rename to yafti/plugins/run.py index 473bbe6..01b4a42 100644 --- a/yafti/plugin/run.py +++ b/yafti/plugins/run.py @@ -24,7 +24,7 @@ # Simple config - run: /usr/bin/whoami # Explicit full path plugin - - yafti.plugin.run: /usr/bin/whoami + - yafti.plugins.run: /usr/bin/whoami # Run with parameters - run: /bin/ls -lah - run: ["/bin/ls", "-lah"] @@ -35,7 +35,7 @@ Programmatic usage example: - from yafti.plugin.run import Run + from yafti.plugins.run import Run r = Run() r.exec(["/usr/bin/whoami"]) f.exec(pkg="com.github.marcoceppi.PackageName", reinstall=True) @@ -57,6 +57,8 @@ from yafti import log from yafti.abc import YaftiPlugin, YaftiPluginReturn +# TODO refactor whole + class Run(YaftiPlugin): async def exec(self, cmd: str) -> subprocess.CompletedProcess: @@ -67,6 +69,7 @@ async def exec(self, cmd: str) -> subprocess.CompletedProcess: if not isfile(cmd) and is_container: if which("distrobox-host-exec"): cmd = f"distrobox-host-exec {cmd}" + elif which("flatpak-spawn"): cmd = f"flatpak-spawn --host {cmd}" @@ -76,14 +79,14 @@ async def exec(self, cmd: str) -> subprocess.CompletedProcess: stdout, stderr = await proc.communicate() - log.info("command complete", cmd=cmd, code=proc.returncode) - log.debug( - "command complete", - cmd=cmd, - code=proc.returncode, - stdout=stdout, - stderr=stderr, - ) + # log.info("command complete", cmd=cmd, code=proc.returncode) + # log.debug( + # "command complete", + # cmd=cmd, + # code=proc.returncode, + # stdout=stdout, + # stderr=stderr, + # ) return subprocess.CompletedProcess( cmd, returncode=proc.returncode, stdout=stdout, stderr=stderr @@ -98,6 +101,7 @@ async def install(self, package: str) -> YaftiPluginReturn: Returns: An object containing the stdout and stderr from the command """ + # TODO: ... return await self.exec(package) @validate_arguments @@ -107,4 +111,5 @@ async def __call__(self, cmd: list[str] | str) -> YaftiPluginReturn: cmd = shlex.join(cmd) r = await self.exec(cmd) + return YaftiPluginReturn(output=r.stdout, errors=r.stderr, code=r.returncode) diff --git a/yafti/registry.py b/yafti/registry.py index d86397c..ba522c1 100644 --- a/yafti/registry.py +++ b/yafti/registry.py @@ -1,10 +1,10 @@ # Copyright 2023 Marco Ceppi # SPDX-License-Identifier: Apache-2.0 - from importlib.metadata import entry_points -_plugins = entry_points(group="yafti.plugin") +# TODO: update to support proper plugins +_plugins = entry_points(group="yafti.plugins") PLUGINS = {s.name: s.load()() for s in _plugins} -_screens = entry_points(group="yafti.screen") +_screens = entry_points(group="yafti.views") SCREENS = {s.name: s.load() for s in _screens} diff --git a/yafti/screen/console.py b/yafti/screen/console.py deleted file mode 100644 index aa6f738..0000000 --- a/yafti/screen/console.py +++ /dev/null @@ -1,75 +0,0 @@ -from gi.repository import Gtk - -from yafti.abc import YaftiScreen - -_xml = """\ - - - - - -""" - - -@Gtk.Template(string=_xml) -class ConsoleScreen(YaftiScreen, Gtk.ScrolledWindow): - __gtype_name__ = "YaftiConsoleScreen" - - console_output = Gtk.Template.Child() - - def stdout(self, text): - if isinstance(text, bytes): - t = text.decode() - for line in t.split("\n"): - if not line: - continue - self.stdout(Gtk.Text(text=line)) - else: - self.console_output.append(text) - self.scroll_to_bottom() - - def stderr(self, text): - if isinstance(text, bytes): - t = text.decode() - for line in t.split("\n"): - if not line: - continue - self.stderr(Gtk.Text(text=line)) - else: - self.console_output.append(text) - self.scroll_to_bottom() - - def scroll_to_bottom(self): - adj = self.get_vadjustment() - adj.set_value(adj.get_upper()) - self.set_vadjustment(adj) - - def scroll_to_top(self): - adj = self.get_vadjustment() - adj.set_value(adj.get_lower()) - - def hide(self): - self.set_visible(False) - - def show(self): - self.set_visible(True) - - def toggle_visible(self): - self.set_visible(self.get_visible() is False) diff --git a/yafti/screen/package/__init__.py b/yafti/screen/package/__init__.py deleted file mode 100644 index adc4878..0000000 --- a/yafti/screen/package/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .screen.package import PackageScreen # noqa diff --git a/yafti/screen/package/screen/__init__.py b/yafti/screen/package/screen/__init__.py deleted file mode 100644 index c1f4d3c..0000000 --- a/yafti/screen/package/screen/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .install import PackageInstallScreen # noqa -from .picker import PackagePickerScreen # noqa diff --git a/yafti/screen/package/screen/install.py b/yafti/screen/package/screen/install.py deleted file mode 100644 index 8a82671..0000000 --- a/yafti/screen/package/screen/install.py +++ /dev/null @@ -1,151 +0,0 @@ -import asyncio -import json - -from gi.repository import Gtk -from typing import Optional - -import yafti.share -from yafti import events -from yafti import log -from yafti.abc import YaftiScreen -from yafti.screen.console import ConsoleScreen -from yafti.screen.package.state import PackageScreenState - -_xml = """\ - - - - - -""" - - -@Gtk.Template(string=_xml) -class PackageInstallScreen(YaftiScreen, Gtk.Box): - __gtype_name__ = "YaftiPackageInstallScreen" - - status_page = Gtk.Template.Child() - pkg_progress = Gtk.Template.Child() - btn_console = Gtk.Template.Child() - started = False - already_run = False - pulse = True - - def __init__( - self, - state: PackageScreenState, - title: str = "Package Installation", - package_manager: str = "yafti.plugin.flatpak", - package_manager_defaults: Optional[dict] = None, - **kwargs, - ): - super().__init__(**kwargs) - from yafti.registry import PLUGINS - - self.status_page.set_title(title) - self.package_manager = PLUGINS.get(package_manager) - self.package_manager_defaults = package_manager_defaults or {} - self.btn_console.connect("clicked", self.toggle_console) - self.state = state - - async def on_activate(self): - if self.started or self.already_run: - return - self.console = ConsoleScreen() - self.started = True - events.on("btn_next", self.next) - await self.draw() - - async def next(self, _): - return self.started - - def toggle_console(self, btn): - btn.set_label("Show Console" if self.console.get_visible() else "Hide Console") - self.console.toggle_visible() - - async def do_pulse(self): - self.pkg_progress.set_pulse_step(1.0) - while self.pulse: - self.pkg_progress.pulse() - await asyncio.sleep(0.5) - - def draw(self): - self.console.hide() - self.append(self.console) - packages = [item.replace("pkg:", "") for item in self.state.get_on("pkg:")] - asyncio.create_task(self.do_pulse()) - return self.install(packages) - - def run_package_manager(self, packge_config): - try: - config = json.loads(packge_config) - except json.decoder.JSONDecodeError as e: - log.debug("could not parse", config=packge_config, e=e) - config = {"package": packge_config} - - log.debug("parsed packages config", config=config) - opts = self.package_manager_defaults.copy() - opts.update(config) - return self.package_manager.install(**opts) - - async def install(self, packages: list): - total = len(packages) - yafti.share.BTN_NEXT.set_label("Installing...") - yafti.share.BTN_BACK.set_visible(False) - for idx, pkg in enumerate(packages): - results = await self.run_package_manager(pkg) - self.console.stdout(results.stdout) - self.console.stderr(results.stderr) - self.pulse = False - self.pkg_progress.set_fraction((idx + 1) / total) - - self.console.stdout(b"Installation Complete!") - - self.started = False - self.already_run = True - yafti.share.BTN_NEXT.set_label("Next") - yafti.share.BTN_BACK.set_visible(True) diff --git a/yafti/screen/package/screen/package.py b/yafti/screen/package/screen/package.py deleted file mode 100644 index cce670f..0000000 --- a/yafti/screen/package/screen/package.py +++ /dev/null @@ -1,133 +0,0 @@ -from typing import Optional - -from gi.repository import Adw, Gtk - -import yafti.share -from yafti import events -from yafti.abc import YaftiScreen, YaftiScreenConfig -from yafti.screen.package.models import PackageConfig, PackageGroupConfig -from yafti.screen.package.screen import PackageInstallScreen, PackagePickerScreen -from yafti.screen.package.state import PackageScreenState -from yafti.screen.package.utils import parse_packages, generate_fingerprint - -_xml = """\ - - - - - -""" - - -@Gtk.Template(string=_xml) -class PackageScreen(YaftiScreen, Adw.Bin): - __gtype_name__ = "YaftiPackageScreen" - - pkg_carousel = Gtk.Template.Child() - - class Config(YaftiScreenConfig): - title: str - show_terminal: bool = True - package_manager: str - groups: Optional[PackageGroupConfig] = None - packages: Optional[list[PackageConfig]] = None - package_manager_defaults: Optional[dict] = None - - def __init__( - self, - title: str = "Package Installation", - package_manager: str = "yafti.plugin.flatpak", - packages: list[PackageConfig] = None, - groups: PackageGroupConfig = None, - show_terminal: bool = True, - package_manager_defaults: Optional[dict] = None, - **kwargs, - ): - super().__init__(**kwargs) - self.title = title - self.packages = groups or packages - self.show_terminal = show_terminal - self.package_manager = package_manager - self.package_manager_defaults = package_manager_defaults - self.fingerprint = generate_fingerprint(self.packages) - self.state = PackageScreenState(self.fingerprint) - self.state.load(parse_packages(self.packages)) - self.pkg_carousel.connect("page-changed", self.changed) - self.draw() - - def draw(self): - self.pkg_carousel.append( - PackagePickerScreen( - state=self.state, title=self.title, packages=self.packages - ) - ) - self.pkg_carousel.append( - PackageInstallScreen( - title=self.title, - state=self.state, - package_manager=self.package_manager, - package_manager_defaults=self.package_manager_defaults, - ) - ) - - def on_activate(self): - events.on("btn_next", self.next) - events.on("btn_back", self.back) - yafti.share.BTN_NEXT.set_label("Install") - - def on_deactivate(self): - events.detach("btn_next", self.next) - events.detach("btn_back", self.back) - - @property - def idx(self): - return self.pkg_carousel.get_position() - - @property - def total(self): - return self.pkg_carousel.get_n_pages() - - def goto(self, page: int, animate: bool = True): - if page < 0: - page = 0 - - if page >= self.pkg_carousel.get_n_pages(): - page = self.pkg_carousel.get_n_pages() - - current_screen = self.pkg_carousel.get_nth_page(self.idx) - next_screen = self.pkg_carousel.get_nth_page(page) - - current_screen.deactivate() - self.pkg_carousel.scroll_to(next_screen, animate) - - def changed(self, *args): - current_screen = self.pkg_carousel.get_nth_page(self.idx) - current_screen.activate() - - async def next(self, _): - if not self.active: - return False - if self.idx + 1 == self.total: - return False - self.goto(self.idx + 1) - - async def back(self, _): - if not self.active: - return False - - if self.idx - 1 < 0: - return False - self.goto(self.idx - 1) diff --git a/yafti/screen/package/screen/picker.py b/yafti/screen/package/screen/picker.py deleted file mode 100644 index 40f79b7..0000000 --- a/yafti/screen/package/screen/picker.py +++ /dev/null @@ -1,164 +0,0 @@ -from functools import partial -import json - -from gi.repository import Adw, Gtk -from pydantic import BaseModel - -from yafti import log -from yafti.abc import YaftiScreen -from yafti.screen.dialog import DialogBox -from yafti.screen.package.state import PackageScreenState -from yafti.screen.utils import find_parent - -_xml = """\ - - - - - -""" - - -@Gtk.Template(string=_xml) -class PackagePickerScreen(YaftiScreen, Adw.Bin): - __gtype_name__ = "YaftiPackagePickerScreen" - - status_page = Gtk.Template.Child() - package_list = Gtk.Template.Child() - - class Config(BaseModel): - title: str = "Package Installation" - packages: list | dict - - def __init__( - self, - state: PackageScreenState, - title: str, - packages: list | dict, - **kwargs, - ): - super().__init__(**kwargs) - self.status_page.set_title(title) - self.packages = packages - self.state = state - self.draw() - - def draw(self): - if isinstance(self.packages, list): - for item in self._build_apps(self.packages): - self.package_list.add(item) - return - - for name, details in self.packages.items(): - action_row = Adw.ActionRow(title=name, subtitle=details.get("description")) - - def state_set(group, _, value): - self.state.set(f"group:{group}", value) - d = self.packages.get(group) - for pkg in d.get("packages", []): - for pkg_name in pkg.values(): - if isinstance(pkg_name, dict): - pkg_name = json.dumps(pkg_name) - self.state.set(f"pkg:{pkg_name}", value) - - state_set(name, None, details.get("default", True)) - _switcher = Gtk.Switch() - _switcher.set_active(self.state.get(f"group:{name}")) - _switcher.set_valign(Gtk.Align.CENTER) - - state_set_fn = partial(state_set, name) - _switcher.connect("state-set", state_set_fn) - action_row.add_suffix(_switcher) - - _customize = Gtk.Button() - _customize.set_icon_name("go-next-symbolic") - _customize.set_valign(Gtk.Align.CENTER) - _customize.add_css_class("flat") - action_row.add_suffix(_customize) - picker_fn = partial(self._build_picker, details.get("packages", [])) - _customize.connect("clicked", picker_fn) - self.package_list.add(action_row) - - def _build_picker(self, packages: list, *args): - dialog = DialogBox(find_parent(self, Gtk.Window)) - - btn_cancel = Gtk.Button() - btn_save = Gtk.Button() - btn_cancel.set_label("Cancel") - btn_save.set_label("Save") - btn_save.add_css_class("suggested-action") - - header = Adw.HeaderBar() - header.pack_start(btn_cancel) - header.pack_end(btn_save) - header.set_show_end_title_buttons(False) - header.set_show_start_title_buttons(False) - - item_list = Adw.PreferencesGroup() - item_list.set_description( - "The following list includes only applications available in your preferred " - "package manager." - ) - page = Adw.PreferencesPage() - page.add(item_list) - - box = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0) - box.append(header) - box.append(page) - - dialog.set_content(box) - dialog.set_default_size(500, 600) - - for item in self._build_apps(packages): - item_list.add(item) - - btn_cancel.connect("clicked", lambda x: dialog.close()) - btn_save.connect("clicked", lambda x: dialog.close()) - dialog.show() - - def _build_apps(self, packages: list): - for item in packages: - for name, pkg in item.items(): - _apps_action_row = Adw.ActionRow( - title=name, - ) - _app_switcher = Gtk.Switch() - if isinstance(pkg, dict): - pkg = json.dumps(pkg) - _app_switcher.set_active(self.state.get(f"pkg:{pkg}")) - _app_switcher.set_valign(Gtk.Align.CENTER) - - def set_state(pkg, btn, value): - log.debug("state-set", pkg=pkg, value=value) - self.state.set(f"pkg:{pkg}", value) - - set_state_func = partial(set_state, pkg) - _app_switcher.connect("state-set", set_state_func) - _apps_action_row.add_suffix(_app_switcher) - yield _apps_action_row diff --git a/yafti/screen/package/utils.py b/yafti/screen/package/utils.py deleted file mode 100644 index 0d6a6df..0000000 --- a/yafti/screen/package/utils.py +++ /dev/null @@ -1,24 +0,0 @@ -import json -from typing import Any -from hashlib import sha256 - - -def generate_fingerprint(obj: Any): - return sha256(json.dumps(obj).encode()).hexdigest() - - -def parse_packages(packages: dict | list) -> dict: - output = {} - - if isinstance(packages, dict): - for group, value in packages.items(): - output[f"group:{group}"] = True - output.update(parse_packages(value["packages"])) - return output - - for pkgcfg in packages: - for package in pkgcfg.values(): - if isinstance(package, dict): - package = json.dumps(package) - output[f"pkg:{package}"] = True - return output diff --git a/yafti/screen/utils.py b/yafti/screen/utils.py deleted file mode 100644 index 1c5d448..0000000 --- a/yafti/screen/utils.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -Copyright 2023 Marco Ceppi - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - - -class NoParentFound(Exception): - """No parent matched""" - - -def find_parent(obj, cls=None): - """Traverse to the parent of a GTK4 component - - Args: - obj: A GTK4 derived component - cls: Parent component to find - - Returns: - The instance of the parent component - - Raises: - NoParentFound: if a cls is passed and all parents are traversed without a match - """ - - p = obj.get_parent() - if cls: - if isinstance(p, cls): - return p - if p is None: - raise NoParentFound(f"no matching parent found for {cls}") - - if p is None: - return obj - - return find_parent(p, cls) diff --git a/yafti/screen/window.py b/yafti/screen/window.py deleted file mode 100644 index 2a78649..0000000 --- a/yafti/screen/window.py +++ /dev/null @@ -1,160 +0,0 @@ -import asyncio -from functools import partial - -from gi.repository import Adw, Gtk - -import yafti.share -from yafti import events -from yafti.registry import SCREENS - -_xml = """\ - - - - - - -""" - - -@Gtk.Template(string=_xml) -class Window(Adw.ApplicationWindow): - __gtype_name__ = "YaftiWindow" - - carousel_indicator = Gtk.Template.Child() - carousel = Gtk.Template.Child() - headerbar = Gtk.Template.Child() - btn_back = Gtk.Template.Child() - btn_next = Gtk.Template.Child() - toasts = Gtk.Template.Child() - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - self.app = kwargs.get("application") - events.register("btn_next") - events.register("btn_back") - events.on("btn_next", self.next) - events.on("btn_back", self.back) - - # TODO(GH-2): not a huge fan of this - yafti.share.BTN_BACK = self.btn_back - yafti.share.BTN_NEXT = self.btn_next - - def do_emit(*args, **kwargs): - asyncio.create_task(events.emit(*args, **kwargs)) - - _next = partial(do_emit, "btn_next") - _back = partial(do_emit, "btn_back") - - self.connect("show", self.draw) - self.connect("close-request", self.app.quit) - self.btn_next.connect("clicked", _next) - self.btn_back.connect("clicked", _back) - self.carousel.connect("page-changed", self.changed) - - def draw(self, _) -> None: - asyncio.ensure_future(self.build_screens()) - - async def build_screens(self): - screens = self.app.config.screens - for name, details in screens.items(): - if details.source not in SCREENS: - continue - screen = SCREENS.get(details.source) - s = await screen.from_config(details.values) - if s is None: - continue - self.carousel.append(s) - - @property - def idx(self) -> float: - return self.carousel.get_position() - - def goto(self, page: int, animate: bool = True) -> None: - if page < 0: - page = 0 - - if page >= self.carousel.get_n_pages(): - page = self.carousel.get_n_pages() - - current_screen = self.carousel.get_nth_page(self.idx) - next_screen = self.carousel.get_nth_page(page) - - current_screen.deactivate() - self.carousel.scroll_to(next_screen, animate) - - @property - def is_last_page(self): - return self.idx + 1 >= self.carousel.get_n_pages() - - async def next(self, _) -> None: - if self.idx + 1 >= self.carousel.get_n_pages(): - self.app.quit() - else: - self.goto(self.idx + 1) - - async def back(self, _) -> None: - self.goto(self.idx - 1) - - def changed(self, *args) -> None: - self.btn_back.set_visible(self.idx > 0) - current_screen = self.carousel.get_nth_page(self.idx) - if self.idx + 1 >= self.carousel.get_n_pages(): - self.btn_next.set_label("Done") - self.btn_back.set_visible(False) - else: - self.btn_next.set_label("Next") - current_screen.activate() diff --git a/yafti/settings.py b/yafti/settings.py new file mode 100644 index 0000000..add472d --- /dev/null +++ b/yafti/settings.py @@ -0,0 +1,394 @@ +# -*- coding: utf-8 -*- +import asyncio +import hashlib +import io +import itertools +import os +import time +from enum import Enum +from functools import lru_cache + +import yaml +from gi.repository import Gio, GLib +from sphinx.errors import ExtensionError + +import yafti.parser +from yafti import __name__ as app_name +from yafti import __version__ as app_version +from yafti import log +from yafti.registry import PLUGINS + +# from dotenv import load_dotenv +# from pydantic import BaseSettings +# from pydantic.fields import Field + + +class BaseConfig: + """ + Base configuration + + TODO: + get user arch version kind + """ + + APP_NAME: str = app_name + PROJECT_ROOT: str = os.path.realpath( + os.path.join( + os.path.dirname(os.path.dirname(os.path.realpath(__file__))), + os.pardir, + APP_NAME, + ) + ) + + APP_ROOT: str = os.path.realpath(os.path.join(PROJECT_ROOT, APP_NAME)) + VERSION: str = app_version + # not local all the time... + ENVIRONMENT: str = "local" + + # load_dotenv() + APP_ID: str = "org.ublue-os.yafti" + + # pydantic configuration + class Config: + env_file = ".env" + env_file_encoding = "utf-8" + + @classmethod + @lru_cache() + def get_settings(cls, **overrides): + """ + Get a settings object from YAML file. + This is not working as intended. + """ + # TODO: override settings that are passed and look up env vars to override values. + return cls(**overrides) + + # TODO: The Config class presents persistent application settings stored in dconf/GSettings + # and exposes them in the Application class The schema for this application contains + # nested elements to be addressed independently. org.ublue-os.yafti. + + +class YaftiRunMode(str, Enum): + ENABLED = 0 + NONCE = 1 + ON_BOOT = 2 + DAEMON = 3 + + +class Config(BaseConfig): + """ + Configuration class for the application. + """ + + # TODO: support more than just flatpak please + package_manager: str = "yafti.plugins.flatpak" + + def __init__(self, config_path: str = "/etc/yafti.yml") -> None: + schema_dir = f"{self.APP_ROOT}" + schema_source = Gio.SettingsSchemaSource.new_from_directory( + schema_dir, + Gio.SettingsSchemaSource.get_default(), + True, + ) + # TODO: add filter and other currently non imported schemas + schema = schema_source.lookup("org.ublue-os.yafti", True) + self.filter = Gio.Settings.new_full(schema, None) + + # Load Window config schema + schema = schema_source.lookup("org.ublue-os.yafti.windows", True) + self.window = Gio.Settings.new_full(schema, None, None) + # Load Settings config schema + schema = schema_source.lookup("org.ublue-os.yafti.settings", True) + self.settings = Gio.Settings.new_full(schema, None, None) + self.__yafti_config_path: str = os.environ.get("YAFTI_CONFIG", config_path) + self.__yafti_config = None + + _config_sum = self.settings.get_value("config-sum") + _decoded_config_sum = _config_sum.get_data_as_bytes().get_data().decode("utf-8") + self.__yafti_config_sum = _decoded_config_sum + self.__system_state = None + self.__screens = None + self.__bundles: list = [] + self.__title: str = "yafti" + self.__mode: YaftiRunMode = YaftiRunMode.NONCE + self.__enabled: bool = True + self.__for_justice: list = [] + + asyncio.ensure_future( + self.__async_callback(PLUGINS.get(self.package_manager).list()) + ) + + self.yafti_config_path = self.__yafti_config_path + + ### + # private methods + ### + async def __async_callback(self, callback): + # TODO: check for is awaitable + # self._all_packages = task.result() + # if inspect.isawaitable(callback): + # results = await callback() + # self._all_packages = results + # else: + # results = callback() + # + # self._all_packages = results + + retry_count = itertools.count() + while True: + try: + loop = asyncio.get_running_loop() + if loop.is_running(): + async with asyncio.TaskGroup() as tg: + task = tg.create_task(callback) + + self.__for_justice = task.result() + except RuntimeError as e: + if next(retry_count) >= 10: + time.sleep(30) + continue + else: + print("failed on max retries.") + raise e + except Exception as e: + print("unrecoverable error") + raise e + break + + def __parse_yafti_config(self): + """ + Parse the yafti config file and return a dictionary of the parsed data. + + example config: + screens: + first-screen: + source: yafti.screen.title + values: + title: "Welcome to Bluefin (Beta)" + icon: "/path/to/icon" + description: | + Applications are also installing in the background, the system will notify you when it is finished. + applications: + source: yafti.screen.package + values: + title: Application Installation + show_terminal: true + package_manager: yafti.plugin.flatpak + groups: + Communication: + default: false + description: Tools to communicate and collaborate + packages: + - Discord: com.discordapp.Discord + - Slack: com.slack.Slack + Cloud Native Development Tools: + description: Start your cloud-native journey here! + default: false + packages: + - Podman Desktop: io.podman_desktop.PodmanDesktop + - Headlamp: io.kinvolk.Headlamp + - Cockpit Client: org.cockpit_project.CockpitClient + Gaming: + description: "Rock and Stone!" + default: false + packages: + - Bottles: com.usebottles.bottles + - Heroic Games Launcher: com.heroicgameslauncher.hgl + - Lutris: net.lutris.Lutris + - MangoHUD: org.freedesktop.Platform.VulkanLayer.MangoHud//22.08 + - Steam: com.valvesoftware.Steam + - Proton Plus for Steam: com.vysp3r.ProtonPlus + Office: + description: Work apps, Bow to Capitalism + default: false + packages: + - OnlyOffice: org.onlyoffice.desktopeditors + - LibreOffice: org.libreoffice.LibreOffice + - LogSeq: com.logseq.Logseq + - Obsidian: md.obsidian.Obsidian + - Standard Notes: org.standardnotes.standardnotes + - Todoist: com.todoist.Todoist + Other Web Browsers: + description: Additional browsers to complement Firefox + default: false + packages: + - Brave: com.brave.Browser + - Google Chrome: com.google.Chrome + - Microsoft Edge: com.microsoft.Edge + - Opera: com.opera.Opera + - Vivaldi: com.vivaldi.Vivaldi + Streaming: + description: Stream to the Internet + default: false + packages: + - OBS Studio: com.obsproject.Studio + - VkCapture for OBS: com.obsproject.Studio.OBSVkCapture + - Gstreamer for OBS: com.obsproject.Studio.Plugin.Gstreamer + - Gstreamer VAAPI for OBS: com.obsproject.Studio.Plugin.GStreamerVaapi + - Boatswain for Streamdeck: com.feaneron.Boatswain + Utilities: + description: Useful Utilities + default: true + packages: + - Font Downloader: org.gustavoperedo.FontDownloader + - PinApp Menu Editor: io.github.fabrialberio.pinapp + - Backup: org.gnome.DejaDup + - LocalSend (Easily send files across your network): org.localsend.localsend_app + - Peek (Simple Screen Recorder): com.uploadedlobster.peek + - Syncthing: com.github.zocker_160.SyncThingy + final-screen: + source: yafti.screen.title + values: + title: "All done!" + icon: "/path/to/icon" + links: + - "Install More Applications": + run: /usr/bin/gnome-software + - "Documentation": + run: /usr/bin/xdg-open https://universal-blue.discourse.group/t/introduction-to-bluefin/41 + - "Discussions and Announcements": + run: /usr/bin/xdg-open https://universal-blue.discourse.group/c/bluefin/6 + - "Join the Discord Community": + run: /usr/bin/xdg-open https://discord.gg/XjG48C7VHx + description: | + Thanks for trying Bluefin, we hope you enjoy it! + """ + try: + with open(self.__yafti_config_path, "r") as f: + _cfg = yaml.safe_load(f) + except FileNotFoundError as e: + log.debug(e) + raise FileNotFoundError(e) + + def __verify_file_extension(self, path: str) -> bool: + __valid_extensions = [".yml", ".yaml"] + path, f = os.path.split(path) + _, ext = os.path.splitext(f) + + return ext.lower() in __valid_extensions + + ### + # public methods + ### + def apps_by_screen(self, name: str): + log.debug(f"APPS BY SCREEN {name}") + # return all apps based on bundle + + return NotImplementedError + + @property + @lru_cache(None) + def for_justice(self): + return self.__for_justice + + @property + def installed(self): + return self.__for_justice + + def is_installed(self, application_id: str) -> bool: + """ + Check if an application is installed + """ + for i in self.__for_justice: + if i.application == application_id: + # log.debug(f"Is Installed ==> {application_id} : {i.application}") + return True + + # log.debug(f"Is Not Installed ==> {application_id}") + return False + + @property + @lru_cache(None) + def yafti_config_path(self): + return self.__yafti_config_path + + @yafti_config_path.setter + def yafti_config_path(self, value: str): + """ + Sets the path to the yafti configuration file. + """ + _path = None + if isinstance(value, io.TextIOWrapper): + _path = value.name + else: + _path = value + + full_path = os.path.realpath(_path) + if not os.path.isfile(full_path): + raise FileNotFoundError() + elif not self.__verify_file_extension(full_path): + raise ExtensionError + else: + with open(full_path, "r") as f: + try: + cfg = yaml.safe_load(f) + self.__yafti_config = yafti.parser.Config.parse_obj(cfg) + + config_sum = hashlib.sha256(f.buffer.read()).hexdigest() + if config_sum != self.__yafti_config_sum: + # when config is updated should likely ask user stuff. + self.__yafti_config_sum = config_sum + self.settings.set_value( + "included-packages", GLib.Variant.new_string("") + ) + self.settings.set_value( + "config-sum", GLib.Variant.new_string(config_sum) + ) + except yaml.YAMLError as e: + log.error(e) + raise e + + @property + @lru_cache(None) + def bundles(self): + # fix me + test = self.screens.get("applications", None).values + for k, v in test.items(): + log.debug(f"@@@@@ bundles @@@@@\n" f"Name: {k} \n" f"Value: {v} \n") + if k == "groups": + self.__bundles.append(v) + + return self.__bundles + + @property + @lru_cache(None) + def screens(self): + return self.system_config.screens + + @property + def system_state(self): + return self.__system_state + + @property + @lru_cache(None) + def system_config(self): + return self.__yafti_config + + @system_config.setter + def system_config(self, value: str): + raise NotImplementedError + + @classmethod + @lru_cache() + def system_settings(cls): + return cls.system_config + + +class Screens: + pass + + +class Bundles: + pass + + +class Packages: + pass + + +class ExcludedPackages: + pass + + +class IncludedPackages: + pass diff --git a/yafti/setup.py b/yafti/setup.py index ca2d5d2..8f0a86e 100644 --- a/yafti/setup.py +++ b/yafti/setup.py @@ -4,6 +4,16 @@ import gi from rich.logging import RichHandler +# TODO: We should support OSTree as a first class citation. This however could be an issue if we wanted to support +# non ostree based operating systems. +# gi.require_version('OSTree', '1.0') +# gi.require_version('Flatpak', '1.0') +gi.require_version("Adw", "1") +gi.require_version("GIRepository", "2.0") +gi.require_version("Gtk", "4.0") +gi.require_version("Rsvg", "2.0") +gi.require_version("Vte", "3.91") + logging.basicConfig( level="NOTSET", format="%(message)s", @@ -18,7 +28,4 @@ ], ) -gi.require_version("Gtk", "4.0") -gi.require_version("Adw", "1") - gbulb.install(gtk=True) diff --git a/yafti/share.py b/yafti/share.py deleted file mode 100644 index 4651718..0000000 --- a/yafti/share.py +++ /dev/null @@ -1,2 +0,0 @@ -BTN_NEXT = None -BTN_BACK = None diff --git a/yafti/utils.py b/yafti/utils.py new file mode 100644 index 0000000..afe0972 --- /dev/null +++ b/yafti/utils.py @@ -0,0 +1,106 @@ +import csv +import json +from hashlib import sha256 +from io import StringIO +from typing import Any, List + + +def generate_fingerprint(obj: Any): + return sha256(json.dumps(obj).encode()).hexdigest() + + +def parse_packages(packages: dict | list) -> dict: + output = {} + + if isinstance(packages, dict): + for group, value in packages.items(): + output[f"group:{group}"] = True + output.update(parse_packages(value["packages"])) + + return output + + for pkg_cfg in packages: + for package in pkg_cfg.values(): + if isinstance(package, dict): + package = json.dumps(package) + output[f"pkg:{package}"] = True + + return output + + +def _format_complex(record: dict) -> dict: + # don't want to get rid of non-csv values, so we will simply write them as a JSON string instead + json_opts = (",", ":") + cast_types = (list, tuple, dict) + + return { + k: (v if not isinstance(v, cast_types) else json.dumps(v, separators=json_opts)) + for k, v in record.items() + } + + +def to_json(records: List[dict]) -> str: + return json.dumps(records, indent=4) + + +def formatter(data: List[dict], output_type: str) -> str: + formats = {"json": to_json, "csv": to_csv} + + return formats[output_type](data) + + +def fs(data: str, filename: str) -> None: + with open(filename, "w") as f: + f.write(data) + + +def to_csv(records: List[dict]) -> str: + if not records: + return "" + + defaults = { + "delimiter": ",", + "quoting": csv.QUOTE_MINIMAL, + "dialect": "excel", + "lineterminator": "\n", + } + + handler = StringIO() + header = records[0].keys() + writer = csv.DictWriter(handler, fieldnames=header, **defaults) + writer.writeheader() + for record in records: + writer.writerow(_format_complex(record)) + + return handler.getvalue() + + +class NoParentFound(Exception): + """No parent matched""" + + +def find_parent(obj, cls=None): + """Traverse to the parent of a GTK4 component + + Args: + obj: A GTK4 derived component + cls: Parent component to find + + Returns: + The instance of the parent component + + Raises: + NoParentFound: if a cls is passed and all parents are traversed without a match + """ + + p = obj.get_parent() + if cls: + if isinstance(p, cls): + return p + if p is None: + raise NoParentFound(f"no matching parent found for {cls}") + + if p is None: + return obj + + return find_parent(p, cls) diff --git a/yafti/views/__init__.py b/yafti/views/__init__.py new file mode 100644 index 0000000..55d9a80 --- /dev/null +++ b/yafti/views/__init__.py @@ -0,0 +1,3 @@ +# TODO: remove during cleanup +# from .install import PackageInstallScreen # noqa +# from .picker import PackagePickerScreen # noqa diff --git a/yafti/views/about.py b/yafti/views/about.py new file mode 100644 index 0000000..8e2a021 --- /dev/null +++ b/yafti/views/about.py @@ -0,0 +1,73 @@ +from gi.repository import Gio, Gtk, Rsvg + + +class About: + """ + About class defines the About content pane + + It generates the structure in memory to apply to the navigation split view + """ + + def __init__(self): + self.scrolled_window = Gtk.ScrolledWindow() + self.scrolled_window.set_vexpand(True) + self.content_box = Gtk.Box() + self.content_box.set_halign(Gtk.Align.CENTER) + self.content_box.set_valign(Gtk.Align.CENTER) + + # Resolve the theme named colours to RGBA strings + self.accent_fg_color = self.get_color("theme_selected_fg_color") + self.accent_bg_color = self.get_color("theme_selected_bg_color") + + # Load the SVG file using librsvg + # TODO get from config + self.svg_data = Rsvg.Handle.new_from_file("yafti/assets/bluefin.svg") + + # Set the SVG colours to the theme colours + # Check: print(self.accent_fg_color, self.accent_bg_color) + # maybe this is something that folks might want to modify?? + self.svg_style_sheet = f""" + #fg1, #fg2, #fg3, #fg4 {{ fill: {self.accent_fg_color} ; }} + #bg1, #bg2, #bg3 {{ fill: {self.accent_bg_color} ; }} + #svg5 {{ width: 256px; height: 256px;}} + """ + self.svg_data.set_stylesheet(self.svg_style_sheet.encode()) + + # Draw the Gtk.Image from the SVG pixel buffer + self.logo = Gtk.Image(height_request=250, width_request=250) + self.logo.set_from_pixbuf(self.svg_data.get_pixbuf()) # TODO below + self.logo.set_margin_end(40) + + self.content_box.append(self.logo) + + # checkbox-checked-symbolic + # self.content_box.append(Gtk.Label(label='')) + # self.content_box.append(Gtk.Text(""" + # Installs Flatpaks and system packages on first boot after a user finishes installation. Yafti is idempotent. + # This allows users to run GUI as may times as they would like + # """)) + + self.scrolled_window.set_child(self.content_box) + + # TODO: + # Deprecated since version 4.12: Use [ctor`Gtk`.Image.new_from_paintable] and [ctor`Gdk`.Texture.new_for_pixbuf] instead + # https://github.com/python-pillow/Pillow + + def set_content(self, button): + """ + set_content sets the About pane content in the navigation split view + """ + content = Gio.Application.get_default().split_view.get_content() + content.set_title("About") + content.pane.set_content(self.scrolled_window) + content.pane.set_reveal_bottom_bars(False) + + def get_color(self, named_color): + """ + Return the libadwaita named color in RGBA from CSS provider + """ + label = Gtk.Label(label="Coloured Text") + label.set_css_classes([f"cc_{named_color}"]) + rgba_color = label.get_color() + + return rgba_color.to_string() diff --git a/yafti/views/confirm.py b/yafti/views/confirm.py new file mode 100644 index 0000000..13d4480 --- /dev/null +++ b/yafti/views/confirm.py @@ -0,0 +1,60 @@ +from gi.repository import Adw, Gtk + +from yafti.windows.dialog import YaftiDialog + + +# TODO: not working please make work. +@Gtk.Template(resource_path="/org/ublue-os/yafti/confirm.ui") +class YaftiConfirm(Adw.Bin): + __gtype_name__ = "YaftiConfirm" + + status_page = Gtk.Template.Child() + reject_btn = Gtk.Template.Child() + accept_btn = Gtk.Template.Child() + info_btn = Gtk.Template.Child() + + def __init__(self, window, distro_info, key, step, **kwargs): + super().__init__(**kwargs) + self.__window = window + self.__distro_info = distro_info + self.__key = key + self.__step = step + self.__response = False + self.__build_ui() + + # signals + self.accept_btn.connect("clicked", self.__on_response, True) + self.reject_btn.connect("clicked", self.__on_response, False) + self.info_btn.connect("clicked", self.__on_info) + + def __build_ui(self): + self.status_page.set_icon_name(self.__step["icon"]) + self.status_page.set_title(self.__step["title"]) + self.status_page.set_description(self.__step["description"]) + + self.accept_btn.set_label(self.__step["buttons"]["yes"]) + self.reject_btn.set_label(self.__step["buttons"]["no"]) + + if "info" in self.__step["buttons"]: + self.info_btn.set_visible(True) + + def __on_response(self, _, response): + self.__response = response + self.__window.next() + + def __on_info(self, _): + if "info" not in self.__step["buttons"]: + return + + dialog = YaftiDialog( + self.__window, + self.__step["buttons"]["info"]["title"], + self.__step["buttons"]["info"]["text"], + ) + dialog.show() + + def get_finals(self): + return { + "vars": {self.__key: self.__response}, + "funcs": [x for x in self.__step["final"]], + } diff --git a/yafti/screen/consent.py b/yafti/views/consent.py similarity index 59% rename from yafti/screen/consent.py rename to yafti/views/consent.py index b22f466..d836b45 100644 --- a/yafti/screen/consent.py +++ b/yafti/views/consent.py @@ -1,4 +1,4 @@ -# Copyright 2023 Marco Ceppi +# Copyright 2024 abanna # SPDX-License-Identifier: Apache-2.0 """ @@ -34,36 +34,16 @@ import asyncio from typing import Optional -from gi.repository import Adw, Gtk +from gi.repository import Adw, Gio, GLib, Gtk -import yafti.share from yafti import events from yafti.abc import YaftiScreen, YaftiScreenConfig from yafti.registry import PLUGINS -_xml = """\ - - - - - - -""" +# TODO: refactor this asap -@Gtk.Template(string=_xml) +@Gtk.Template(filename="yafti/gtk/consent.ui") class ConsentScreen(YaftiScreen, Adw.Bin): __gtype_name__ = "YaftiConsentScreen" @@ -83,15 +63,30 @@ def __init__( **kwargs ): super().__init__(**kwargs) - self.status_page.set_title(title) - self.status_page.set_description(description) + + events.register("accept") + events.on("accept", self.next) + self.status_page.set_title("Accept Consent") + self.status_page.set_description( + "This Allows us to add and/or modify flatpaks on this system." + ) self.actions = actions self.condition = condition self.already_run = False + def set_content(self, button, content=None): + if content is None: + content = Gio.Application.get_default().split_view.get_content() + content.set_title("Welcome Travelers") + + button.set_label("Accept") + content.pane.set_content(self) + content.pane.set_reveal_bottom_bars(True) + button.connect("clicked", lambda x: asyncio.ensure_future(self.next(x))) + async def on_activate(self): events.on("btn_next", self.next) - yafti.share.BTN_NEXT.set_label("Accept") + # yafti.share.BTN_NEXT.set_label("Accept") async def on_deactivate(self): events.detach("btn_next", self.next) @@ -100,11 +95,21 @@ async def next(self, _): if self.already_run: return False + # Connect to root application to get config object + application = Gio.Application.get_default() + application.config.settings.set_value( + "consent-accepted", GLib.Variant.new_boolean(True) + ) + to_run = [] - for action in self.actions: - plugin_name = list(action.keys())[0] - plugin = PLUGINS.get(plugin_name) - to_run.append(plugin(action[plugin_name])) - await asyncio.gather(*to_run) + if self.actions is not None: + for action in self.actions: + plugin_name = list(action.keys())[0] + plugin = PLUGINS.get(plugin_name) + to_run.append(plugin(action[plugin_name])) + + await asyncio.gather(*to_run) + self.already_run = True + return False diff --git a/yafti/views/console.py b/yafti/views/console.py new file mode 100644 index 0000000..3bd02d5 --- /dev/null +++ b/yafti/views/console.py @@ -0,0 +1,395 @@ +import asyncio +from typing import Optional + +from gi.repository import Adw, Gdk, Gio, GObject, Gtk, Pango, Vte + +from yafti import log +from yafti.abc import YaftiScreen, YaftiScreenConfig +from yafti.core.models import PackageConfig, PackageGroupConfig + +# TODO: move to .ui file this might be able to just be removed. +__xml = """\ + + + + + + +""" + + +# @Gtk.Template(filename="yafti/screen/assets/console.ui") +@Gtk.Template(string=__xml) +class YaftiApplyChanges(YaftiScreen, Adw.Bin): + __gtype_name__ = "YaftiApplyChanges" + + # carousel_tour = Gtk.Template.Child() + tour_button = Gtk.Template.Child() + tour_box = Gtk.Template.Child() + progressbar = Gtk.Template.Child() + console_button = Gtk.Template.Child() + console_box = Gtk.Template.Child() + console_output = Gtk.Template.Child() + install_button = Gtk.Template.Child() + # installer_state = Gtk.Template.Child() + status_page = Gtk.Template.Child() + + class Config(YaftiScreenConfig): + title: str + show_terminal: bool = True + package_manager: str + groups: Optional[PackageGroupConfig] = None + packages: Optional[list[PackageConfig]] = None + package_manager_defaults: Optional[dict] = None + + def __init__(self, title, window, package_list, **kwargs): + """ """ + + log.debug("YaftiApplyChanges") + super().__init__(**kwargs) + + self.__window = window + self.title: str = title + log.debug(GObject.signal_list_names(self)) + self.set_property("visible", True) + self.set_vexpand(True) + self.__packages = package_list or [] + # self.__success_fn = None + # self.__terminal = Vte.Terminal() + # self.__font = Pango.FontDescription() + # self.__font.set_family("Source Code Pro 10") + # self.__font.set_size(13 * Pango.SCALE) + # self.__font.set_weight(Pango.Weight.NORMAL) + # self.__font.set_stretch(Pango.Stretch.NORMAL) + # self.__style_manager = self.__window.style_manager + # self.__build_ui() + # self.__on_setup_terminal_colors() + # asyncio.ensure_future(self.crap(PLUGINS.get(package_manager).list())) + asyncio.ensure_future(self.build_screens(package_list)) + # self.__style_manager.connect("notify::dark", self.__on_setup_terminal_colors) + # self.tour_button.connect("clicked", self.__on_tour_button) + # self.console_button.connect("clicked", self.__on_console_button) + # self.install_button.connect("clicked", self.__install_packages) + # self.scrolled_window = Gtk.ScrolledWindow() + # self.scrolled_window.set_vexpand(True) + # self.content_box = Gtk.Box() + # self.content_box.set_halign(Gtk.Align.CENTER) + # self.content_box.set_valign(Gtk.Align.CENTER) + # # Connect to root application to get config object + # application = Gio.Application.get_default() + # + # self.scrolled_window.set_child(self.content_box) + + async def build_screens(self, package_list=None): + """ + This entire function needs to be refactored + theming - cappica mocca + """ + # package_manager: str = "yafti.plugins.flatpak" + self.__success_fn = None + self.__terminal = Vte.Terminal() + self.__font = Pango.FontDescription() + self.__font.set_family("Source Code Pro 10") + self.__font.set_size(13 * Pango.SCALE) + self.__font.set_weight(Pango.Weight.NORMAL) + self.__font.set_stretch(Pango.Stretch.NORMAL) + self.__style_manager = self.__window.style_manager + self.__build_ui() + + self.__on_setup_terminal_colors() + self.__style_manager.connect("notify::dark", self.__on_setup_terminal_colors) + self.tour_button.connect("clicked", self.__on_tour_button) + self.console_button.connect("clicked", self.__on_console_button) + self.install_button.connect("clicked", self.__install_packages) + + self.scrolled_window = Gtk.ScrolledWindow() + self.scrolled_window.set_vexpand(True) + # self.content_box = Gtk.Box() + # self.content_box.set_halign(Gtk.Align.CENTER) + # self.content_box.set_valign(Gtk.Align.CENTER) + self.status_page.set_title(self.title) + # Connect to root application to get config object + # application = Gio.Application.get_default() + self.status_page.set_child(self.console_box) + self.scrolled_window.set_child(self.content_box) + + def __on_setup_terminal_colors(self, *args): + is_dark: bool = self.__style_manager.get_dark() + + # scheme: "Catppuccin Mocha" + # author: "https://github.com/catppuccin/catppuccin" + # base00: "1e1e2e" # base + # base01: "181825" # mantle + # base02: "313244" # surface0 + # base03: "45475a" # surface1 + # base04: "585b70" # surface2 + # base05: "cdd6f4" # text + # base06: "f5e0dc" # rosewater + # base07: "b4befe" # lavender + # base08: "f38ba8" # red + # base09: "fab387" # peach + # base0A: "f9e2af" # yellow + # base0B: "a6e3a1" # green + # base0C: "94e2d5" # teal + # base0D: "89b4fa" # blue + # base0E: "cba6f7" # mauve + # base0F: "f2cdcd" # flamingo + + palette = [ + "#1e1e2e", # base + "#181825", # mantle + "#313244", # surface0 + "#45475a", # surface1 + "#585b70", # surface2 + "#cdd6f4", # text + "#f5e0dc", # rosewater + "#b4befe", # lavender + "#f38ba8", # red + "#fab387", # peach + "#f9e2af", # yellow + "#a6e3a1", # green + "#94e2d5", # teal + "#89b4fa", # blue + "#cba6f7", # mauve + "#f2cdcd" # flamingo + ] + + FOREGROUND = palette[0] + BACKGROUND = palette[15] + FOREGROUND_DARK = palette[15] + BACKGROUND_DARK = palette[0] + + self.fg = Gdk.RGBA() + self.bg = Gdk.RGBA() + + self.colors = [Gdk.RGBA() for c in palette] + # TODO come back to this later + [color.parse(s) for (color, s) in zip(self.colors, palette)] + + if is_dark: + self.fg.parse(FOREGROUND_DARK) + self.bg.parse(BACKGROUND_DARK) + else: + self.fg.parse(FOREGROUND) + self.bg.parse(BACKGROUND) + + self.__terminal.set_colors(self.fg, self.bg, self.colors) + + def __on_console_button(self, *args): + self.tour_box.set_visible(False) + self.console_box.set_visible(True) + self.tour_button.set_visible(True) + self.console_button.set_visible(False) + + def __on_tour_button(self, *args): + self.tour_box.set_visible(True) + self.console_box.set_visible(False) + self.tour_button.set_visible(False) + self.console_button.set_visible(True) + + def __build_ui(self): + self.__terminal.set_cursor_blink_mode(Vte.CursorBlinkMode.ON) + self.__terminal.set_font(self.__font) + self.__terminal.set_mouse_autohide(True) + self.__terminal.set_input_enabled(False) + + if self.console_output is None: + self.console_output = [] + + self.console_output.append(self.__terminal) + self.__terminal.connect("child-exited", self.on_vte_child_exited) + + def __packages(self): + """ """ + print("PACKAGES ACTIVATED") + + raise NotImplementedError + + def __install_packages(self, content): + """ """ + print("INSTALLED PACKAGES ACTIVATED") + + raise NotImplementedError + + def on_vte_child_exited(self, terminal, status, *args): + terminal.get_parent().remove(terminal) + status = not bool(status) + if self.__success_fn is not None and status: + self.__success_fn(*self.__success_fn_args) + + self.__window.set_installation_result(status, self.__terminal) + + def stdout(self, text): + if isinstance(text, bytes): + t = text.decode() + for line in t.split("\n"): + if not line: + continue + + self.stdout(Gtk.Text(text=line)) + else: + self.console_output.append(text) + + self.scroll_to_bottom() + + def stderr(self, text): + if isinstance(text, bytes): + t = text.decode() + for line in t.split("\n"): + if not line: + continue + + self.stderr(Gtk.Text(text=line)) + else: + self.console_output.append(text) + + self.scroll_to_bottom() + + def scroll_to_bottom(self): + adj = self.get_vadjustment() + adj.set_value(adj.get_upper()) + self.set_vadjustment(adj) + + def scroll_to_top(self): + adj = self.get_vadjustment() + adj.set_value(adj.get_lower()) + + def hide(self): + self.set_visible(False) + + def show(self): + self.set_visible(True) + + def toggle_visible(self): + self.set_visible(self.get_visible() is False) + + def set_content(self, button): + content = Gio.Application.get_default().split_view.get_content() + self.__build_ui() + content.pane.set_content(self) + content.set_title("Installation") + content.set_visible(True) + content.pane.set_content(self.scrolled_window) + content.pane.set_reveal_bottom_bars(False) diff --git a/yafti/views/content.py b/yafti/views/content.py new file mode 100644 index 0000000..8fbe327 --- /dev/null +++ b/yafti/views/content.py @@ -0,0 +1,620 @@ +import asyncio +import base64 +import hashlib +import json +from pprint import pformat + +from gi.repository import Adw, Gio, GLib, GObject, Gtk + +from yafti import log +from yafti.views.consent import ConsentScreen +from yafti.views.dialog import DialogBox + +# The content pane is a shell to swap panes in and out of the content area of the navigation split view +# The content pane must be a subclass of Adw.NavigationPage +# The content pane consists of a top_bar, content area, and bottom_bar +# For specific content panes, common bottom toolbar is used to control +# the state of the data factory updating for linked panes class +# In other content panes, the common bottom toolbar can be hidden + +# When a button is clicked in the sidebar, it should update the toolbar content and the bottom toolbar visibility +# It should be able to remove the bottom toolbar from the content pane using an empty widget + + +# This would typically be its own file +MENU_XML = """ + + + +
+ Application Menu + + win.change_label + + String 1 + + + win.change_label + + String 2 + + + win.change_label + + String 3 + +
+
+ + win.maximize + Dark Mode + +
+
+ + app.about + _About + + + app.settings + Settings + + + app.quit + _Quit + <Primary>q + +
+
+
+""" + + +class Content(Adw.NavigationPage): + """ + Content class creates the content pane structure + https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/style-classes.html#toolbars + Other pane classes update the content pane with their content + """ + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.set_visible(True) + self.pane = Adw.ToolbarView(top_bar_style=Adw.ToolbarStyle.FLAT) + self.header = Adw.HeaderBar() + + # https://python-gtk-3-tutorial.readthedocs.io/en/latest/application.html + self.header.set_decoration_layout("menu:false") + self.menu = Gio.Menu() + builder = Gtk.Builder.new_from_string(MENU_XML, -1) + + # self.menu.append("Dark Mode", "app.dark_mode") + # self.menu_button = Gtk.MenuButton( + # icon_name="open-menu-symbolic", menu_model=self.menu + # ) + + self.end_menu_button = Gtk.MenuButton( + icon_name="system-shutdown-symbolic", + menu_model=builder.get_object("app-menu"), + ) + + # self.header.pack_start(self.menu_button) + self.header.pack_end(self.end_menu_button) + + # self.pane.replace_data + self.pane.add_top_bar(self.header) + self.set_child(self.pane) + + # next button + next_button = Gtk.Button() + next_button.set_label("Next") + next_button.set_halign(Gtk.Align.CENTER) + next_button.set_css_classes(["suggested-action"]) + next_button.connect("clicked", lambda x: self.next()) + # bottom bar + bottom_bar = Gtk.Box() + bottom_bar.set_margin_top(12) + bottom_bar.set_margin_bottom(12) + bottom_bar.set_margin_end(12) + bottom_bar.set_halign(Gtk.Align.END) + bottom_bar.append(next_button) + bottom_bar.set_css_classes([".toolbar"]) + # setting pane + self.pane.add_bottom_bar(bottom_bar) + self.pane.set_reveal_bottom_bars(True) + + # Packages is unable to resolve the Application object before the content pane is first created + # So the content pane is passed as an argument to the set_content method + # Connect to root application to get config object + # need to properly send user to next page with content + application = Gio.Application.get_default() + consent_accepted = application.config.settings.get_value( + "consent-accepted" + ).get_boolean() + log.info(f"consent has been set to: {consent_accepted}") + + if consent_accepted is True: + first_screen = application.config.screens.get('first-screen') + if first_screen is None: + self.set_title("Welcome Travelers") + pkg = Packages("Welcome", self, []) + else: + self.set_title("Welcome Travelers") + self.set_description = first_screen.values['description'] + self.set_icon = first_screen.values['icon'] + pkg = Packages(first_screen.values['title'], self, []) + + pkg.set_content(None, self) + + else: + # TODO: get from config file and load as view + self.set_title("Welcome Travelers") + welcome = ConsentScreen() + welcome.set_content(next_button, self) + + def _closed(self, obj, pspec): + log.debug(f"Closing {obj}") + + def next(self, **kwargs): + """ + Next screen this currently doesn't work + """ + log.debug("Next screen") + application = Gio.Application.get_default() + current = self.pane.set_content(application.split_view.get_content()) + pkg = Packages( + self, package_list=[], window=application.get_default().split_view + ) + pkg.set_content(current, self) + + +# TODO: move to .ui file. +_xml2 = """\ + + + + + +""" + + +class Package(GObject.GObject): + __gtype_name__ = "Package" + + def __init__(self, application, name, ref, installed): + super().__init__() + self.__app = Gtk.Application.get_default() + self._application = application + self._action = None + self._checksum: str = "" + self._name = name + self._ref = ref + self._installed = installed or False + self._modified: bool = False + self._install_button = Gtk.Button() + self._window = Gtk.Window() + self._dialog_box = DialogBox(self._window) + self._apps_page = Adw.PreferencesPage() + self._box = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0) + self._box.append(self._apps_page) + self._dialog_box.set_content(self._box) + self._dialog_box.set_default_size(500, 600) + self._action_row = Adw.ActionRow(title=self._ref) + self._install_button.set_name("install") + self._install_button.set_label("install") + self._install_button.set_label("install") + self._install_button.set_valign(Gtk.Align.CENTER) + self._install_button.add_css_class("boxed_list") + self._action_row.add_suffix(self._install_button) + + if self._installed is True and self._modified is False: + log.debug( + f"package was installed and not modified setting initial icon {self._name}" + ) + self._install_button.set_icon_name("process-completed-symbolic") + + def to_dict(self): + return { + "action": self._action, + "application": self._application, + "name": self._name, + "ref": self._ref, + "installed": self._installed, + } + + def to_json(self): + return json.dumps(self.to_dict()) + + def __base64_encode(self): + encoded_json = self.to_json().encode("utf-8") + + return base64.b64encode(encoded_json).decode("utf-8") + + @property + def action(self): + return self._action + + @action.setter + def action(self, value: str or None): + if value not in ["install", "uninstall", None]: + raise ValueError("Action must be either 'install' or 'uninstall'") + + self._action = value + + @property + def checksum(self): + sha256 = hashlib.sha256(self.to_json().encode("utf-8")) + self._checksum = sha256.hexdigest() + + return self._checksum + + def __get_from_settings(self): + pkg_settings = self.__app.config.settings.get_value("included-packages") + encoded_pkgs = pkg_settings.get_data_as_bytes().get_data().decode("utf-8") + + all_pkgs = "" + try: + all_pkgs = json.loads(base64.b64decode(encoded_pkgs)) + except Exception: + # attempt to rebuild full included package settings + pass + + return all_pkgs + + def __get_installed(self): + pass + + def __update_settings(self): + included_packages = self.__app.config.settings.get_value("included-packages") + pkg_data = included_packages.get_data_as_bytes().get_data().decode("utf-8") + + try: + pkgs_loaded = json.loads(base64.b64decode(pkg_data)) + except json.JSONDecodeError as e: + log.debug( + f"failed to decode and json load package data updating settings: {pkg_data} -- {e}" + ) + pkgs_loaded = {} + pass + + exists = pkgs_loaded.get(self.application) + if exists is None or exists.get("checksum") != self.checksum: + self_dict = self.to_dict() + self_dict["checksum"] = self.checksum + pkgs_loaded[self.application] = self_dict + pkgs_encoded = json.dumps(pkgs_loaded).encode("utf-8") + b64 = base64.b64encode(pkgs_encoded).decode("utf-8") + self.__app.config.settings.set_value( + "included-packages", GLib.Variant.new_string(b64) + ) + + def update_state(self): + self._modified = True + self.__update_settings() + + @GObject.Property(type=str) + def application(self): + return self._application + + @GObject.Property(type=str) + def name(self): + return self._name + + @GObject.Property(type=str) + def ref(self): + return self._ref + + @GObject.Property(type=bool, default=False) + def installed(self): + if self._installed is True and self._modified is False: + self._install_button.set_icon_name("process-completed-symbolic") + + return self._installed + + @installed.setter + def installed(self, value: bool): + if value is True and self._modified is False: + self._install_button.set_icon_name("process-completed-symbolic") + + self._installed = value + + @property + def install_button(self): + return self._install_button + + @property + def dialog_box(self): + return self._dialog_box + + @property + def action_row(self): + return self._action_row + + def __repr__(self): + return f""" + Package( + action={self._action} + application={self._application}, + name={self._name}, + ref={self._ref}, + installed{self._installed} + ) + """ # noqa + + +# @Gtk.Template(filename="yafti/screen/assets/packages.ui") +# class Packages(Gtk.ScrolledWindow): +@Gtk.Template(string=_xml2) +class Packages(Adw.Bin): + """ + Packages class defines the Packages content panes + + It generates the structure in memory to apply to the navigation split view + """ + + __gtype_name__ = "YaftiPackages" + # scrolled_window = Gtk.ScrolledWindow() + # package_list = Gtk.Template.Child() + # packages_overview = Gtk.Template.Child() + status_page = Gtk.Template.Child() + bundle_list = Gtk.Template.Child() + # bundle = Gtk.Template.Child() + # btn_next = Gtk.Template.Child() + + def __init__(self, title, window, package_list, **kwargs): + super().__init__(**kwargs) + + self.__window = window + # self.btn_next.connect("clicked", self.__next_step) + self.title: str = title + self.set_property("visible", True) + self.package_list = package_list or [] + self._all_packages = [] + # self.scrolled_window = Gtk.ScrolledWindow() + self.set_vexpand(True) + self.__register_widgets = [] + self.__app = Gtk.Application.get_default() + # TODO get from config + # self.bundle_list.activate_action("clicked", self.on_selection_button_clicked) + self.list_store = Gio.ListStore.new(Package) + self._currently_installed = [] + self.__loaded = [] + + asyncio.ensure_future(self.build_screens(package_list)) + + def currently_installed(self, force=False): + if not self._currently_installed or force is True: + self._currently_installed = [] + for pkg in self.__app.config.installed: + self._currently_installed.append(pkg) + + return self._currently_installed + + def __next_step(self, *args): + """ + This still does not work + """ + log.debug(f"next_step: {args}") + self.__window.next() + + def language_filter_func(self, model, iter, data): + """ + Not working + """ + if self.current_filter is None or self.current_filter == "None": + return True + else: + return model[iter][2] == self.current_filter + + def on_ready_callback(self, source_object, result, user_data): + """ + Not working + """ + log.debug("on_ready_callback") + + # @Gtk.Template.Callback() + def on_selection_button_clicked(self, widget, dialog, item): + """ """ + log.debug("on_selection_button_clicked") + dialog.set_visible(False) + # dialog.connect() + # we set the current language filter to the button's label + + state = widget.get_icon_name() + log.debug(f"package action state: {state}") + if state is None: + widget.set_icon_name("selection-checked-symbolic") + item.action = "install" + if state == "process-completed-symbolic": + widget.set_icon_name("process-error-symbolic") + item.install_button.set_icon_name("process-error-symbolic") + item.action = "uninstall" + elif state == "selection-checked-symbolic": + widget.set_label("install") + item.action = None + elif state == "process-error-symbolic": + widget.set_icon_name("process-completed-symbolic") + item.action = None + + item.update_state() + + # self.__app.config.setttings.set_active(item["application"]) + # self.__app.config.settings.preferences_group.get_value("") + + included_packages = self.__app.config.settings.get_value("included-packages") + inc_bytes = included_packages.get_data_as_bytes().get_data().decode("utf-8") + try: + log.debug(pformat(json.loads(base64.b64decode(inc_bytes)), indent=4)) + except Exception as e: + log.error(f"content button state error json loads: {e}", e) + return + + def set_content(self, button, content=None): + """ + set_content sets the Packages pane content in the navigation split view + """ + # self.connect("noarg-signal", self.noarg_signal) + if content is None: + content = Gio.Application.get_default().split_view.get_content() + content.set_title("Packages") + + content.pane.set_content(self) + content.pane.set_reveal_bottom_bars(False) + content.set_child(self) + # self.show() + # self.scrolled_window.set_data(self.__window) + asyncio.ensure_future(self.build_screens()) + + async def build_screens(self, package_list=None): + """ + This entire function needs to be refactored + """ + # TODO: get values from config file + self.status_page.set_icon_name("package") + if isinstance(self.title, Content): + _content = self.title + + self.title = _content.get_title().title() + self.status_page.set_title(self.title) + self.set_content(None, _content) + else: + self.status_page.set_title(self.title) + + if self.package_list is None: + self.status_page.set_description( + "Install some Packages! If you're seeing this message update the yafti.yaml file." + ) + elif isinstance(self.package_list, list): + # for package in self.package_list: + # print(package) + # TODO: make work + log.debug( + f"^^^^^^^^^^^^^^^^^^^^^^^ self.package_list is {self.package_list} ^^^^^^^^^^^^^^^^^^^^^^^" + ) + + else: + self.status_page.set_description( + self.package_list.get("description", "Should make a github issue...") + ) + + match self.title.lower(): + case "apply changes": + log.debug( + f"-----------------------------------{self.title.lower()}-----------------------------------" + ) + self.list_store = Gtk.ListStore(str, str, str, str, str) + new_headers = [ + "ref", + "name", + "runtime", + "installation", + "version", + "options", + ] + + for p in self.package_list: + for k, v in p.items(): + self.list_store.append([k, v]) + self._all_packages.append( + {new_headers[0]: k, new_headers[1]: v} + ) + case "installed": + log.debug( + f"-----------------------------------{self.title.lower()}-----------------------------------" + ) + self.status_page.set_description("Currently installed on your system") + self.list_store.remove_all() + for p in self.currently_installed(): + # self.list_store.append([p.ref, p.name]) + pkg = Package(p.application, p.name, p.ref, True) + self.list_store.append(pkg) + + # not sure why installed is being called twice + if self.title.lower() not in self.__loaded: + self.__loaded.append(self.title.lower()) + self.__load_pages() + elif "currently-installed" not in self.__loaded: + self.__loaded.append("currently-installed") + self.__load_pages() + + case _: + log.debug( + f"-------------group package screen {self.title.lower()}--------------" + ) + if isinstance(self.package_list, dict): + packages = self.package_list.get("packages", []) + if packages: + self.list_store.remove_all() + for p in packages: + for k, v in p.items(): + is_installed = self.__app.config.is_installed(v) + + pkg = Package(v, v, k, is_installed) + self.list_store.append(pkg) + self.list_store.connect( + "notify", self.__on_page_changed, pkg + ) + + if self.title.lower() not in self.__loaded: + self.__loaded.append(self.title.lower()) + self.__load_pages() + + else: + # TODO: this needs to be handled correctly + log.debug("something went wrong -- before loop started") + + def __load_pages(self): + selection_dialogs = [] + _index = 0 + + for item in self.list_store: + selection_dialogs.append(item.dialog_box) + # _customize.connect("clicked", self.on_selection_button_clicked, selection_dialogs[-1]) # selection_dialogs[-1], _apps_list, item + item.install_button.connect( + "clicked", + self.on_selection_button_clicked, + selection_dialogs[_index], + item, + ) + self.bundle_list.add(item.action_row) + + _index += 1 + + def __on_page_changed(self, list_store, page, item): + log.debug("on_page_changed") + if item.installed is False: + item.installed = self.__app.config.is_installed(item.application) diff --git a/yafti/views/dialog.py b/yafti/views/dialog.py new file mode 100644 index 0000000..dc21225 --- /dev/null +++ b/yafti/views/dialog.py @@ -0,0 +1,45 @@ +from gi.repository import Adw, Gtk + + +@Gtk.Template(filename="yafti/gtk/dialog.ui") +class DialogBox(Adw.Window): + __gtype_name__ = "YaftiDialog" + + def __init__(self, parent=None, **kwargs): + super().__init__(**kwargs) + if parent: + self.set_transient_for(parent) + + sc = Gtk.ShortcutController.new() + sc.add_shortcut( + Gtk.Shortcut.new( + Gtk.ShortcutTrigger.parse_string("Escape"), + Gtk.CallbackAction.new(lambda x: self.hide()), + ) + ) + self.add_controller(sc) + + +# TODO: will move to this later. +# @Gtk.Template(filename="yafti/gtk/dialog.ui") +# class DialogBox(Adw.Window): +# __gtype_name__ = "YaftiDialog" +# +# label_text = Gtk.Template.Child() +# +# def __init__(self, window, title, text, **kwargs): +# super().__init__(**kwargs) +# self.set_transient_for(window) +# self.set_title(title) +# self.label_text.set_text(text) +# +# def hide(action, callback=None): +# self.hide() +# +# shortcut_controller = Gtk.ShortcutController.new() +# shortcut_controller.add_shortcut( +# Gtk.Shortcut.new( +# Gtk.ShortcutTrigger.parse_string("Escape"), Gtk.CallbackAction.new(hide) +# ) +# ) +# self.add_controller(shortcut_controller) diff --git a/yafti/views/settings.py b/yafti/views/settings.py new file mode 100644 index 0000000..9651ec9 --- /dev/null +++ b/yafti/views/settings.py @@ -0,0 +1,91 @@ +from gi.repository import Adw, Gio, Gtk + + +class Settings: + def __init__(self): + self.scrolled_window = Gtk.ScrolledWindow() + self.scrolled_window.set_vexpand(True) + + self.preference = Adw.PreferencesPage() + self.preferences_group = Adw.PreferencesGroup() + + # Connect to root application to get config object + application = Gio.Application.get_default() + + # https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1-latest/class.SpinRow.html + # This setting is intended to allow yafti to run as a daemon and check for changes or updates + self.interval_seconds = Adw.SpinRow( + title="Interval (Seconds)", + subtitle="Set the metric update interval", + digits=1, + snap_to_ticks=True, + can_focus=True, + selectable=False, + adjustment=Gtk.Adjustment( + value=application.config.settings.get_value("interval").get_double(), + lower=60.0, + upper=600.0, + step_increment=1.0, + page_increment=1, + page_size=0, + ), + visible=False, + ) + + application.config.settings.bind( + "interval", + self.interval_seconds, + "value", + Gio.SettingsBindFlags.GET_NO_CHANGES, + ) + + self.consent_accepted = Adw.SwitchRow( + title="Consent Acceptance", + subtitle="Accepted application consent policy", + selectable=False, + can_focus=False, + can_target=False, + active=False, + activatable=False, + visible=False, + ) + application.config.settings.bind( + "consent-accepted", + self.consent_accepted, + "active", + Gio.SettingsBindFlags.DEFAULT, + ) + + self.disable_yafti = Adw.SwitchRow( + title="Disable Yafti", + subtitle="Disable Yafti from running on the system.", + selectable=False, + can_focus=False, + can_target=False, + active=False, + activatable=True, + ) + + application.config.settings.bind( + "disabled", + self.disable_yafti, + "active", + Gio.SettingsBindFlags.DEFAULT, + ) + + self.preferences_group.add(self.consent_accepted) + self.preferences_group.add(self.interval_seconds) + self.preferences_group.add(self.disable_yafti) + self.preference.add(self.preferences_group) + + self.scrolled_window.set_child(self.preference) + + def set_content(self, button): + """ + set_content sets the Settings pane content in the navigation split view + """ + content = Gio.Application.get_default().split_view.get_content() + content.set_title("Settings") + content.set_visible(True) + content.pane.set_content(self.scrolled_window) + content.pane.set_reveal_bottom_bars(False) diff --git a/yafti/views/sidebar.py b/yafti/views/sidebar.py new file mode 100644 index 0000000..5cd718d --- /dev/null +++ b/yafti/views/sidebar.py @@ -0,0 +1,238 @@ +from gi.repository import Adw, Gdk, Gio, GObject, Gtk + +from yafti import log +from yafti.core.apply import YaftiProgress +from yafti.views.about import About +from yafti.views.content import Packages +from yafti.views.settings import Settings + + +class ListItem: + """ + ListItem class defines the sidebar button widget + """ + + def __init__(self, title: str, icon: str, pane: object) -> None: + self.title = title + self.icon = icon + self.pane = pane + + +class Sidebar(Adw.NavigationPage): + """ + Sidebar class defines the sidebar pane + """ + + def __init__(self, window=None, **kwargs): + super().__init__(**kwargs) + + # Primary Settings for Sidebar + self.set_title("yafti") + self.set_vexpand(True) + + # Set menu bar min width + self.set_size_request(220, -1) + + # Define sidebar header box + self.header_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) + self.theme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default()) + self.theme.add_search_path(path="icons") + + # TODO: get image and label from config file. + self.header_logo = Gtk.Image.new_from_file("yafti/assets/small_logo.png") + self.header_label = Gtk.Label(label="Bluefin Portal") + + self.header_box.append(self.header_logo) + self.header_box.append(self.header_label) + + # The sidebar show content button when collapsed + self.show_button = Gtk.ToggleButton( + icon_name="go-next-symbolic", + active=False, + visible=False, + margin_top=0, + margin_bottom=0, + ) + + # Bind to the parent Window split view show-content property + self.application = Gio.Application.get_default() + self.show_button.bind_property( + "active", + self.application.split_view, + "show-content", + GObject.BindingFlags.BIDIRECTIONAL, + ) + + # Connect to the 'notify::folded' signal of the Adw.NavigationSplitView to show the button + self.application.split_view.connect( + "notify::collapsed", self.on_split_view_folded, self.show_button + ) + + # TODO: fix issues with header + # Add the toolbar and header to the sidebar + self.toolbar = Adw.ToolbarView() + self.header = Adw.HeaderBar() + self.header.set_title_widget(self.header_box) + self.header.set_show_back_button(True) + self.header.set_can_focus(False) + self.header.set_decoration_layout("menu:close") + self.header.pack_end(self.show_button) + self.toolbar.set_content() + self.toolbar.add_top_bar(self.header) + + try: + self.set_child(self.toolbar) + except Exception: + self.set_child(Adw.ToolbarView()) + + self.list = Gtk.ListBox() + self.list.set_vexpand(False) + self.list.set_margin_top(12) + self.list.set_margin_start(6) + self.list.set_margin_end(6) + self.list.set_selection_mode(Gtk.SelectionMode.SINGLE) + + # Connect the signal + self.list.connect("row-activated", self.on_row_activated) + # TODO: need to add start and end screens to workflow still + # for name, details in self.application.yafti_config.screens.items(): + # if details.source not in SCREENS: + # continue + # + # screen = SCREENS.get(details.source) + # s = asyncio.ensure_future(screen.from_config(details.values)) + + # get plugins and make sidebar. + __list_items = [] + for b in window.bundle_list: + for k, v in b.items(): + # TODO: get a icon from config + __list_items.append( + ListItem(k, "package-x-generic-symbolic", Packages(k, window, v)) + ) + + top_list_items = __list_items + [ + # TODO: future support incoming + # ListItem( + # "Flatpaks", + # "package-x-generic-symbolic", + # Packages("flatpaks", window, package_list=[]), + # ), + # ListItem( + # "Gnome Extensions", + # "extension", + # Packages("gnome_extensions", window, package_list=[]) + # ), + ListItem( + "Currently Installed", + "applications-system-symbolic", + Packages("Installed", window, package_list=[]), + ), + ] + + # TODO: remove currently installed tab for initial release + # store all installed apps in glib settings. along with a sha256. + bottom_list_items = [ + ListItem("Settings", "emblem-system-symbolic", Settings()), + ListItem("About", "help-about-symbolic", About()), + ListItem( + "Apply", + "thunderbolt-symbolic", + YaftiProgress("apply", window, package_list=[]), + ), + ] + + separator = Gtk.Separator( + orientation=Gtk.Orientation.HORIZONTAL, + margin_start=2, + margin_end=2, + focusable=False, + focus_on_click=False, + can_focus=False, + can_target=False, + ) + + # Populate the sidebar list buttons + for k in top_list_items: + button = Adw.ActionRow( + activatable=True, + title=k.title, + icon_name=k.icon, + margin_bottom=0, + margin_top=0, + css_classes=["action-row-rounded"], + ) + button.set_focus_on_click(True) + button.set_can_focus(True) + button.connect("activated", self.on_button_activated, k.pane) + self.list.append(button) + + # Separator + self.list.append(separator) + + for k in bottom_list_items: + button = Adw.ActionRow( + activatable=True, + title=k.title, + icon_name=k.icon, + margin_bottom=0, + margin_top=0, + css_classes=["action-row-rounded"], + ) + button.set_focus_on_click(True) + button.set_can_focus(True) + button.connect("activated", self.on_button_activated, k.pane) + self.list.append(button) + + # Assign the list to the sidebar + self.toolbar.set_content(self.list) + + def on_button_activated(self, button, content): + """ + Set the accepted consent when a button is clicked + """ + log.debug("on btn activated: sidebar") + try: + btn_title = button.get_title() + for i in self.list: + invert_op = getattr(i, "get_title", None) + if callable(invert_op): + title = invert_op() + + if title == btn_title: + # content.set_parent(i.get_child()) + # i.set_parent(button) + # button.set_parent(i) + # self.get_child().set_focus_child(content) + content.set_content(button) + break + else: + continue + + # content.set_parent() + # content.set_content(button) + log.debug("XxXxXx" * 14) + # content.emit("noarg-signal") + except Exception as e: + content.connect("activate", button) + content.set_parent(button) + log.debug(f"on_button_activated Exception: {e}") + # TODO: this should be handled correctly + + def on_row_activated(self, list_box, row): + log.debug("on row activated: side bar") + + # self.set_child(list_box) + self.application.split_view.set_property("show-content", True) + + def on_split_view_folded(self, split_view, allocation, button): + """ + on_split_view_folded shows a button to return to content view in collapsed sidebar mode + """ + log.debug(f"on_split_view_folded: {allocation}") + # If the Adw.NavigationSplitView is folded, show the button + # If the Adw.NavigationSplitView is not folded, hide the button + if self.application.split_view.get_collapsed(): + button.set_visible(True) + else: + button.set_visible(False) diff --git a/yafti/screen/title.py b/yafti/views/title.py similarity index 75% rename from yafti/screen/title.py rename to yafti/views/title.py index 2d2faaf..f76dc89 100644 --- a/yafti/screen/title.py +++ b/yafti/views/title.py @@ -9,30 +9,10 @@ from yafti.abc import YaftiScreen, YaftiScreenConfig from yafti.registry import PLUGINS -_xml = """\ - - - - - - -""" - - -@Gtk.Template(string=_xml) +# TODO: use for welcome screen after accepted content page sourced from yafti.yaml + + +@Gtk.Template(filename="yafti/gtk/title.ui") class TitleScreen(YaftiScreen, Adw.Bin): __gtype_name__ = "YaftiTitleScreen" @@ -64,7 +44,9 @@ def render_links_list_box(self): links_list_box = Gtk.ListBox() links_list_box.set_selection_mode(Gtk.SelectionMode.NONE) links_list_box.add_css_class("boxed-list") + self.status_page.set_child(links_list_box) + return links_list_box def append_action_rows(self, links, links_list_box): @@ -103,4 +85,5 @@ def do_emit(*args, **kwargs): async def on_action_row_open(*args, plugin=None, config=None): if not plugin and not config: return + await PLUGINS.get(plugin)(config) diff --git a/yafti/views/tour.py b/yafti/views/tour.py new file mode 100644 index 0000000..68208bf --- /dev/null +++ b/yafti/views/tour.py @@ -0,0 +1,21 @@ +from gi.repository import Adw, Gtk + + +@Gtk.Template(filename="yafti/gtk/tour.ui") +class YaftiTour(Adw.Bin): + __gtype_name__ = "YaftiTour" + + status_page = Gtk.Template.Child() + assets_media = Gtk.Template.Child() + + def __init__(self, window, tour, **kwargs): + super().__init__(**kwargs) + self.__window = window + self.__tour = tour + + self.__build_ui() + + def __build_ui(self): + self.assets_media.set_resource(self.__tour["resource"]) + self.status_page.set_title(self.__tour["title"]) + self.status_page.set_description(self.__tour["description"]) diff --git a/yafti/windows/__init__.py b/yafti/windows/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/yafti/windows/dialog.py b/yafti/windows/dialog.py new file mode 100644 index 0000000..846cb59 --- /dev/null +++ b/yafti/windows/dialog.py @@ -0,0 +1,14 @@ +from gi.repository import Adw, Gtk + + +@Gtk.Template(resource_path="/org/ublue-os/yafti/dialog.ui") +class YaftiDialog(Adw.Window): + __gtype_name__ = "YaftiDialog" + + label_text = Gtk.Template.Child() + + def __init__(self, window, title, text, **kwargs): + super().__init__(**kwargs) + self.set_transient_for(window) + self.set_title(title) + self.label_text.set_text(text) diff --git a/yafti/windows/window.py b/yafti/windows/window.py new file mode 100644 index 0000000..9788279 --- /dev/null +++ b/yafti/windows/window.py @@ -0,0 +1,136 @@ +from gi.repository import Adw, Gio, GLib, Gtk + +from yafti import log + +XML = """\ + + + + + Filter + + All + win.filter + All + + + Open + win.filter + Open + + + Done + win.filter + Done + + + + Done Tasks + win.remove-done-tasks + + + Shortcuts + win.show-help-overlay + + +""" + + +class ApplicationWindow(Adw.ApplicationWindow): + """ + ApplicationWindow is the primary window + """ + + # TODO fix top bar so it works. + + def __init__(self, **kwargs): + log.debug("@@@@@@ New Window Entrypoint @@@@@@") + super().__init__(**kwargs) + + # Bind Application window dimensions to store state + application = Gio.Application.get_default() + application.config.window.bind( + "width", self, "default-width", Gio.SettingsBindFlags.DEFAULT + ) + application.config.window.bind( + "height", self, "default-height", Gio.SettingsBindFlags.DEFAULT + ) + application.config.window.bind( + "is-maximized", self, "maximized", Gio.SettingsBindFlags.DEFAULT + ) + application.config.window.bind( + "is-fullscreen", self, "fullscreened", Gio.SettingsBindFlags.DEFAULT + ) + + # builder = Gtk.Builder.new_from_string(XML, -1) + # application.add_main_option_entries(builder.get_object('main_menu')) + + # Dark Mode settings + # TODO: Get this working, the menu currently doesn't toggle + dark_mode = application.config.window.get_boolean("dark-mode") + self.style_manager = Adw.StyleManager.get_default() + + if dark_mode: + self.style_manager.set_color_scheme(Adw.ColorScheme.FORCE_DARK) + else: + self.style_manager.set_color_scheme(Adw.ColorScheme.DEFAULT) + + dark_mode_action = Gio.SimpleAction( + name="dark-mode", state=GLib.Variant.new_boolean(dark_mode) + ) + + dark_mode_action.connect("activate", self.toggle_dark_mode) + dark_mode_action.connect("change-state", self.change_color_scheme) + self.add_action(dark_mode_action) + + on_quit = Gio.SimpleAction(name="quit") + on_quit.connect("activate", application.quit) + self.add_action(on_quit) + + def toggle_dark_mode(self, action, _): + state = action.get_state() + old_state = state.get_boolean() + new_state = not old_state + action.change_state(GLib.Variant.new_boolean(new_state)) + + def change_color_scheme(self, action, new_state): + dark_mode = new_state.get_boolean() + style_manager = Adw.StyleManager.get_default() + + if dark_mode: + style_manager.set_color_scheme(Adw.ColorScheme.FORCE_DARK) + else: + style_manager.set_color_scheme(Adw.ColorScheme.DEFAULT) + + action.set_state(new_state) + + def next( + self, + widget: Gtk.Widget = None, + result: bool = None, + rebuild: bool = False, + mode: int = 0, + *args + ): + # TODO not working this is the button on the left hand side of the screen and should switch content views + # if rebuild: + # self.rebuild_ui(mode) + # + # if result is not None: + # self.__last_result = result + + content = Gio.Application.get_default().split_view.get_content() + content.set_title("You shouldn't see this title") + + # if button is None: + # button = Gtk.Button(label='punch it') + # + # button.connect('clicked', lambda event: self.content_box.append(Gtk.Label())) + # self.scrolled_window.set_child(button) + + # content.pane.set_content(content.get_next_sibling()) + # content.pane.set_reveal_bottom_bars(True) + + # cur_index = self.carousel.get_position() + # page = self.carousel.get_nth_page(cur_index + 1) + # self.carousel.scroll_to(page, True)