From c5eb00cb42a3c65ab9c5b8c7f5aa0a2f0b9919d8 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Wed, 18 Jun 2025 19:53:06 +0700 Subject: [PATCH 001/101] Experiment with Claude *grumbles about lack of support for a dot prefix* --- .gitignore | 2 ++ CLAUDE.md | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 CLAUDE.md diff --git a/.gitignore b/.gitignore index 91a949329..768716ca4 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ node_modules yarn-error.log ts/.svelte-kit .yarn +.claude/settings.local.json +CLAUDE.local.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..6ec6db642 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,82 @@ +# Claude Code Configuration + +## Project Overview + +Anki is a spaced repetition flashcard program with a multi-layered architecture. Main components: + +- Web frontend: Svelte/TypeScript in ts/ +- PyQt GUI, which embeds the web components in aqt/ +- Python library which wraps our rust Layer (pylib/, with Rust module in pylib/rsbridge) +- Core Rust layer in rslib/ +- Protobuf definitions in proto/ that are used by the different layers to + talk to each other. + +## Building/checking + +./check (check.bat) will format the code and run the main build & checks. +Please do this as a final step before marking a task as completed. + +## Quick iteration + +During development, you can build/check subsections of our code: + +- Rust: 'cargo check' +- Python: './tools/dmypy' +- TypeScript/Svelte: './ninja check:svelte' + +Be mindful that some changes (such as modifications to .proto files) may +need a full build with './check' first. + +## Build tooling + +'./check' and './ninja' invoke our build system, which is implemented in build/. It takes care of downloading required deps and invoking our build +steps. + +## Translations + +ftl/ contains our Fluent translation files. We have scripts in rslib/i18n +to auto-generate an API for Rust, TypeScript and Python so that our code can +access the translations in a type-safe manner. Changes should be made to +ftl/core or ftl/qt. Except for features specific to our Qt interface, prefer +the core module. When adding new strings, confirm the appropriate ftl file +first, and try to match the existing style. + +## Protobuf and IPC + +Our build scripts use the .proto files to define our Rust library's +non-Rust API. pylib/rsbridge exposes that API, and _backend.py exposes +snake_case methods for each protobuf RPC that call into the API. +Similar tooling creates a @generated/backend TypeScript module for +communicating with the Rust backend (which happens over POST requests). + +## Fixing errors + +When dealing with build errors or failing tests, invoke 'check' or one +of the quick iteration commands regularly. This helps verify your changes +are correct. To locate other instances of a problem, run the check again - +don't attempt to grep the codebase. + +## Ignores + +The files in out/ are auto-generated. Mostly you should ignore that folder, +though you may sometimes find it useful to view out/{pylib/anki,qt/_aqt,ts/lib/generated} when dealing with cross-language communication or our other generated sourcecode. + +## Launcher/installer + +The code for our launcher is in qt/launcher, with separate code for each +platform. + +## Rust dependencies + +Prefer adding to the root workspace, and using dep.workspace = true in the individual Rust project. + +## Rust utilities + +rslib/{process,io} contain some helpers for file and process operations, +which provide better error messages/context and some ergonomics. Use them +when possible. + +## Rust error handling + +in rslib, use error/mod.rs's AnkiError/Result and snafu. In our other Rust modules, prefer anyhow + additional context where appropriate. Unwrapping +in build scripts/tests is fine. From a63e4ef1c890c7fc88e21b942508ac35cec712a6 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Thu, 19 Jun 2025 13:15:04 +0700 Subject: [PATCH 002/101] Revert "Ignore TaskManager's on_done callback if collection unloaded (#4076)" This reverts commit ccc42227d8d8d37117d69f25d2aed22b708252b3. Closes #4094 --- qt/aqt/taskman.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/qt/aqt/taskman.py b/qt/aqt/taskman.py index 143c1022a..2ca1c6670 100644 --- a/qt/aqt/taskman.py +++ b/qt/aqt/taskman.py @@ -84,15 +84,8 @@ class TaskManager(QObject): fut = executor.submit(task, **args) if on_done is not None: - - def wrapped_done(future: Future) -> None: - if uses_collection and not (self.mw.col and self.mw.col.db): - print(f"Ignored on_done as collection unloaded: {repr(on_done)}") - return - on_done(future) - fut.add_done_callback( - lambda future: self.run_on_main(lambda: wrapped_done(future)) + lambda future: self.run_on_main(lambda: on_done(future)) ) return fut From ba0d590c1604469918d5ef56f60632bd5559bf94 Mon Sep 17 00:00:00 2001 From: user1823 <92206575+user1823@users.noreply.github.com> Date: Thu, 19 Jun 2025 11:53:49 +0530 Subject: [PATCH 003/101] Clear desired retention and decay when changing decks (#4095) These values are preset-specific and entries from previous deck may cause issues. --- rslib/src/card/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rslib/src/card/mod.rs b/rslib/src/card/mod.rs index 8d7821e2c..49d952ecf 100644 --- a/rslib/src/card/mod.rs +++ b/rslib/src/card/mod.rs @@ -189,6 +189,8 @@ impl Card { fn set_deck(&mut self, deck: DeckId) { self.remove_from_filtered_deck_restoring_queue(); self.memory_state = None; + self.desired_retention = None; + self.decay = None; self.deck_id = deck; } From bbf533b1723d99bfa50f5a1dca19deab5c71784b Mon Sep 17 00:00:00 2001 From: user1823 <92206575+user1823@users.noreply.github.com> Date: Thu, 19 Jun 2025 11:55:30 +0530 Subject: [PATCH 004/101] Update the default value of FSRS-6 decay in forgetting curve (#4096) Changed in https://github.com/open-spaced-repetition/fsrs-rs/commit/037345fd57472ea392a6086f217b1c73a9fa171a --- ts/routes/card-info/CardInfo.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ts/routes/card-info/CardInfo.svelte b/ts/routes/card-info/CardInfo.svelte index 020037dda..938c7d92a 100644 --- a/ts/routes/card-info/CardInfo.svelte +++ b/ts/routes/card-info/CardInfo.svelte @@ -22,12 +22,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html $: decay = (() => { const paramsLength = stats?.fsrsParams?.length ?? 0; if (paramsLength === 0) { - return 0.2; // default decay for FSRS-6 + return 0.1542; // default decay for FSRS-6 } if (paramsLength < 21) { return 0.5; // default decay for FSRS-4.5 and FSRS-5 } - return stats?.fsrsParams?.[20] ?? 0.2; + return stats?.fsrsParams?.[20] ?? 0.1542; })(); From 04996c77f3866ff23441ed357f075424e583f99a Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Thu, 19 Jun 2025 14:03:16 +0700 Subject: [PATCH 005/101] Migrate build system to uv (#4074) * Migrate build system to uv Closes #3787, and is a step towards #3081 and #4022 This change breaks our PyOxidizer bundling process. While we probably could update it to work with the new venvs & lockfile, my intention is to use this as a base to try out a uv-based packager/installer. Some notes about the changes: - Use uv for python download + venv installation - Drop python/requirements* in favour of pyproject files / uv.lock - Bumped to latest Python 3.9 version. The move to 3.13 should be a fairly trivial change when we're ready. - Dropped the old write_wheel.py in favour of uv/hatchling. This has the unfortunate side-effect of dropping leading zeros in our wheels, which we could try hack around in the future. - Switch to Qt 6.7 for the dev repo, as it's the first PyQt version with a Linux/ARM WebEngine wheel. - Unified our macOS deployment target with minimum required for ARM. - Dropped unused fluent python files - Dropped unused python license generation - Dropped helpers to run under Qt 5, as our wheels were already requiring Qt 6 to install. * Build action to create universal uv binary * Drop some PyOxidizer-related files * Use Windows ARM64 cargo/node binaries during build We can't provide ARM64 wheels to users yet due to #4079, but we can at least speed up the build. The rustls -> native-tls change on Windows is because ring requires clang to compile for ARM64, and I figured it's best to keep our Windows deps consistent. We already built the wheels with native-tls. * Make libankihelper a universal library We were shipping a single arch library in a purelib, leading to breakages when running on a different platform. * Use Python wheel for mpv/lame on Windows/Mac This is convenient, but suboptimal on a Mac at the moment. The first run of mpv will take a number of seconds for security checks to run, and our mpv code ends up timing out, repeating the process each time. Our installer stub will need to invoke mpv once first to get it validated. We could address this by distributing the audio with the installer/stub, or perhaps by putting the binaries in a .pkg file that's notarized+stapled and then included in the wheel. * Add some helper scripts to build a fully-locked wheel * Initial macOS launcher prototype * Add a hidden env var to preload our libs and audio helpers on macOS * qt/bundle -> qt/launcher - remove more of the old bundling code - handle app icon * Fat binary, notarization & dmg * Publish wheels on testpypi for testing * Use our Python pin for the launcher too * Python cleanups * Extend launcher to other platforms + more - Switch to Qt 6.8 for repo default, as 6.7 depends on an older libwebp/tiff which is unavailable on newer installs - Drop tools/mac-x86, as we no longer need to test against Qt 5 - Add flags to cross compile wheels on Mac and Linux - Bump glibc target to 2_36, building on Debian Stable - Increase mpv timeout on macOS to allow for initial gatekeeper checks - Ship both arm64 and amd64 uv on Linux, with a bash stub to pick the appropriate arch. * Fix pylint on Linux * Fix failure to run from /usr/local/bin * Remove remaining pyoxidizer refs, and clean up duplicate release folder * Rust dep updates - Rust 1.87 for now (1.88 due out in around a week) - Nom looks involved, so I left it for now - prost-reflect depends on a new prost version that got yanked * Python 3.13 + dep updates Updated protoc binaries + add helper in order to try fix build breakage. Ended up being due to an AI-generated update to pip-system-certs that was not reviewed carefully enough: https://gitlab.com/alelec/pip-system-certs/-/issues/36 The updated mypy/black needed some tweaks to our files. * Windows compilation fixes * Automatically run Anki after installing on Windows * Touch pyproject.toml upon install, so we check for updates * Update Python deps - urllib3 for CVE - pip-system-certs got fixed - markdown/pytest also updated --- .cargo/config.toml | 3 +- .deny.toml | 6 +- .dprint.json | 1 - .gitmodules | 6 - .isort.cfg | 1 - .mypy.ini | 4 +- .python-version | 1 + .ruff.toml | 2 +- .vscode.dist/settings.json | 6 +- Cargo.lock | 1950 ++++++--------- Cargo.toml | 131 +- build/configure/src/aqt.rs | 10 +- build/configure/src/bundle.rs | 442 ---- build/configure/src/launcher.rs | 44 + build/configure/src/main.rs | 14 +- build/configure/src/platform.rs | 28 +- build/configure/src/pylib.rs | 10 +- build/configure/src/python.rs | 144 +- build/configure/src/rust.rs | 4 +- build/ninja_gen/Cargo.toml | 17 + build/ninja_gen/src/archives.rs | 24 +- build/ninja_gen/src/bin/update_protoc.rs | 126 + build/ninja_gen/src/bin/update_uv.rs | 144 ++ build/ninja_gen/src/node.rs | 4 + build/ninja_gen/src/protobuf.rs | 26 +- build/ninja_gen/src/python.rs | 121 +- build/runner/Cargo.toml | 7 +- build/runner/src/bundle/artifacts.rs | 62 - build/runner/src/bundle/binary.rs | 53 - build/runner/src/bundle/folder.rs | 156 -- build/runner/src/bundle/mod.rs | 6 - build/runner/src/main.rs | 12 - build/runner/src/paths.rs | 5 - build/runner/src/pyenv.rs | 60 +- build/runner/src/run.rs | 3 +- cargo/licenses.json | 629 +++-- docs/development.md | 9 +- docs/windows.md | 7 +- ftl/extract-strings.py | 36 - ftl/format.py | 99 - ftl/format_check.py | 14 - ninja | 2 +- pylib/anki/foreign_data/__init__.py | 3 +- pylib/anki/importing/noteimp.py | 4 +- pylib/anki/lang.py | 4 +- pylib/anki/scheduler/base.py | 1 + pylib/anki/stats.py | 7 +- pylib/anki/template.py | 1 + pylib/anki/utils.py | 4 +- pylib/hatch_build.py | 42 + pylib/pyproject.toml | 35 + pylib/rsbridge/build.rs | 26 +- pyproject.toml | 42 +- python/README.md | 3 - python/licenses.json | 152 -- python/licenses.sh | 23 - python/requirements.anki.in | 9 - python/requirements.aqt.in | 10 - python/requirements.base.in | 2 - python/requirements.base.txt | 54 - python/requirements.bundle.in | 8 - python/requirements.bundle.txt | 494 ---- python/requirements.dev.in | 27 - python/requirements.dev.txt | 715 ------ python/requirements.qt5_14.in | 3 - python/requirements.qt5_14.txt | 42 - python/requirements.qt5_15.in | 3 - python/requirements.qt5_15.txt | 54 - python/requirements.qt6_6.in | 5 - python/requirements.qt6_6.txt | 56 - python/requirements.qt6_8.in | 5 - python/requirements.qt6_8.txt | 71 - python/requirements.win.in | 2 - python/requirements.win.txt | 16 - python/update_python_deps.sh | 25 - python/update_win_deps.bat | 1 - python/version.py | 10 + python/write_wheel.py | 191 -- qt/aqt/__init__.py | 27 +- qt/aqt/_macos_helper.py | 7 +- qt/aqt/browser/sidebar/model.py | 6 +- qt/aqt/editor.py | 21 +- qt/aqt/errors.py | 1 + qt/aqt/mediasrv.py | 10 +- qt/aqt/mpv.py | 3 +- qt/aqt/package.py | 128 +- qt/aqt/qt/qt6.py | 2 +- qt/aqt/reviewer.py | 5 +- qt/aqt/sound.py | 25 +- qt/aqt/stylesheets.py | 14 +- qt/aqt/utils.py | 28 +- qt/bundle/Cargo.lock | 629 ----- qt/bundle/Cargo.toml | 60 - qt/bundle/PyOxidizer | 1 - qt/bundle/build.rs | 95 - qt/bundle/mac/Cargo.toml | 20 - qt/bundle/mac/src/codesign.rs | 43 - qt/bundle/mac/src/dmg.rs | 51 - qt/bundle/mac/src/main.rs | 239 -- qt/bundle/mac/src/notarize.rs | 103 - qt/bundle/pyoxidizer.bzl | 189 -- qt/bundle/qt.exclude | 10 - qt/bundle/src/anki.rs | 35 - qt/bundle/src/main.rs | 32 - qt/bundle/win/Cargo.toml | 15 - qt/bundle/win/anki.exe.manifest | 9 - qt/bundle/win/src/main.rs | 153 -- qt/hatch_build.py | 51 + qt/launcher/Cargo.toml | 26 + qt/launcher/build.rs | 10 + qt/{bundle => launcher}/lin/README.md | 0 qt/launcher/lin/anki | 30 + qt/{bundle => launcher}/lin/anki.1 | 0 qt/{bundle => launcher}/lin/anki.desktop | 0 qt/{bundle => launcher}/lin/anki.png | Bin qt/{bundle => launcher}/lin/anki.xml | 0 qt/{bundle => launcher}/lin/anki.xpm | 0 qt/launcher/lin/build.sh | 66 + qt/{bundle => launcher}/lin/install.sh | 2 +- qt/{bundle => launcher}/lin/uninstall.sh | 0 .../mac/src => launcher/mac}/Info.plist | 8 +- qt/launcher/mac/build.sh | 53 + .../mac/dmg/anki-logo-bg.png | Bin qt/{bundle => launcher}/mac/dmg/build.sh | 13 +- qt/{bundle => launcher}/mac/dmg/dmg_ds_store | Bin .../set-dmg-settings.app/Contents/Info.plist | 0 .../Contents/MacOS/applet | Bin .../dmg/set-dmg-settings.app/Contents/PkgInfo | 0 .../Contents/Resources/Scripts/main.scpt | Bin .../Contents/Resources/applet.icns | Bin .../Contents/Resources/applet.rsrc | Bin .../Resources/description.rtfd/TXT.rtf | 0 .../Contents/_CodeSignature/CodeResources | 0 .../mac/dmg/set-dmg-settings.scpt | Bin .../mac/entitlements.python.xml | 0 qt/{bundle => launcher}/mac/icon/Assets.car | Bin .../AppIcon.appiconset/Contents.json | 0 .../AppIcon.appiconset/round-1024-512.png | Bin .../mac/icon/Assets.xcassets/Contents.json | 0 qt/{bundle => launcher}/mac/icon/build.sh | 0 qt/launcher/mac/notarize.sh | 17 + qt/launcher/pyproject.toml | 22 + qt/launcher/src/bin/build_win.rs | 295 +++ qt/launcher/src/main.rs | 117 + qt/launcher/src/platform/mac.rs | 80 + qt/launcher/src/platform/mod.rs | 18 + qt/launcher/src/platform/unix.rs | 74 + qt/launcher/src/platform/windows.rs | 118 + qt/{bundle => launcher}/win/anki-console.bat | 0 qt/{bundle => launcher}/win/anki-icon.ico | Bin qt/{bundle => launcher}/win/anki-manifest.rc | 0 qt/launcher/win/anki.exe.manifest | 25 + qt/{bundle => launcher}/win/anki.template.nsi | 37 +- qt/launcher/win/build.bat | 5 + qt/{bundle => launcher}/win/fileassoc.nsh | 0 qt/mac/ankihelper.xcodeproj/project.pbxproj | 4 +- qt/mac/helper_build.py | 43 +- qt/pyproject.toml | 80 + qt/release/publish.sh | 13 + qt/release/pyproject.toml | 80 + qt/release/update.sh | 62 + rslib/Cargo.toml | 5 +- rslib/linkchecker/tests/links.rs | 2 +- rslib/process/src/lib.rs | 20 + rslib/src/card_rendering/tts/windows.rs | 15 +- .../import_export/package/colpkg/export.rs | 10 +- .../import_export/package/colpkg/import.rs | 2 +- rslib/src/import_export/package/media.rs | 5 +- rslib/src/notetype/cardgen.rs | 2 +- rslib/src/scheduler/answering/learning.rs | 2 +- rslib/src/scheduler/answering/mod.rs | 2 +- rslib/src/scheduler/fsrs/simulator.rs | 4 +- rslib/src/scheduler/new.rs | 2 +- rslib/src/scheduler/reviews.rs | 8 +- rslib/src/scheduler/states/load_balancer.rs | 4 +- rslib/src/storage/sqlite.rs | 14 +- rslib/src/sync/http_server/mod.rs | 8 +- rslib/src/sync/http_server/routes.rs | 4 +- rslib/src/sync/media/database/client/mod.rs | 11 +- rslib/src/sync/media/zip.rs | 6 +- rslib/src/sync/request/mod.rs | 10 +- run | 3 +- run.bat | 2 +- rust-toolchain.toml | 2 +- tools/build | 3 +- tools/build-arm-lin | 12 + tools/build-x64-mac | 10 + tools/build.bat | 4 +- tools/mac-x86 | 18 - tools/minilints/src/main.rs | 5 +- tools/publish | 7 + tools/run-qt5.14 | 12 - tools/run-qt5.15 | 11 - tools/run-qt5.15.bat | 10 - tools/run-qt6.6 | 9 + tools/run-qt6.8 | 12 +- uv.lock | 2198 +++++++++++++++++ 197 files changed, 5695 insertions(+), 6648 deletions(-) create mode 100644 .python-version delete mode 100644 build/configure/src/bundle.rs create mode 100644 build/configure/src/launcher.rs create mode 100644 build/ninja_gen/src/bin/update_protoc.rs create mode 100644 build/ninja_gen/src/bin/update_uv.rs delete mode 100644 build/runner/src/bundle/artifacts.rs delete mode 100644 build/runner/src/bundle/binary.rs delete mode 100644 build/runner/src/bundle/folder.rs delete mode 100644 build/runner/src/bundle/mod.rs delete mode 100644 ftl/extract-strings.py delete mode 100644 ftl/format.py delete mode 100644 ftl/format_check.py create mode 100644 pylib/hatch_build.py create mode 100644 pylib/pyproject.toml delete mode 100644 python/README.md delete mode 100644 python/licenses.json delete mode 100755 python/licenses.sh delete mode 100644 python/requirements.anki.in delete mode 100644 python/requirements.aqt.in delete mode 100644 python/requirements.base.in delete mode 100644 python/requirements.base.txt delete mode 100644 python/requirements.bundle.in delete mode 100644 python/requirements.bundle.txt delete mode 100644 python/requirements.dev.in delete mode 100644 python/requirements.dev.txt delete mode 100644 python/requirements.qt5_14.in delete mode 100644 python/requirements.qt5_14.txt delete mode 100644 python/requirements.qt5_15.in delete mode 100644 python/requirements.qt5_15.txt delete mode 100644 python/requirements.qt6_6.in delete mode 100644 python/requirements.qt6_6.txt delete mode 100644 python/requirements.qt6_8.in delete mode 100644 python/requirements.qt6_8.txt delete mode 100644 python/requirements.win.in delete mode 100644 python/requirements.win.txt delete mode 100755 python/update_python_deps.sh delete mode 100644 python/update_win_deps.bat create mode 100644 python/version.py delete mode 100644 python/write_wheel.py delete mode 100644 qt/bundle/Cargo.lock delete mode 100644 qt/bundle/Cargo.toml delete mode 160000 qt/bundle/PyOxidizer delete mode 100644 qt/bundle/build.rs delete mode 100644 qt/bundle/mac/Cargo.toml delete mode 100644 qt/bundle/mac/src/codesign.rs delete mode 100644 qt/bundle/mac/src/dmg.rs delete mode 100644 qt/bundle/mac/src/main.rs delete mode 100644 qt/bundle/mac/src/notarize.rs delete mode 100644 qt/bundle/pyoxidizer.bzl delete mode 100644 qt/bundle/qt.exclude delete mode 100644 qt/bundle/src/anki.rs delete mode 100644 qt/bundle/src/main.rs delete mode 100644 qt/bundle/win/Cargo.toml delete mode 100644 qt/bundle/win/anki.exe.manifest delete mode 100644 qt/bundle/win/src/main.rs create mode 100644 qt/hatch_build.py create mode 100644 qt/launcher/Cargo.toml create mode 100644 qt/launcher/build.rs rename qt/{bundle => launcher}/lin/README.md (100%) create mode 100644 qt/launcher/lin/anki rename qt/{bundle => launcher}/lin/anki.1 (100%) rename qt/{bundle => launcher}/lin/anki.desktop (100%) rename qt/{bundle => launcher}/lin/anki.png (100%) rename qt/{bundle => launcher}/lin/anki.xml (100%) rename qt/{bundle => launcher}/lin/anki.xpm (100%) create mode 100755 qt/launcher/lin/build.sh rename qt/{bundle => launcher}/lin/install.sh (92%) rename qt/{bundle => launcher}/lin/uninstall.sh (100%) rename qt/{bundle/mac/src => launcher/mac}/Info.plist (92%) create mode 100755 qt/launcher/mac/build.sh rename qt/{bundle => launcher}/mac/dmg/anki-logo-bg.png (100%) rename qt/{bundle => launcher}/mac/dmg/build.sh (87%) rename qt/{bundle => launcher}/mac/dmg/dmg_ds_store (100%) rename qt/{bundle => launcher}/mac/dmg/set-dmg-settings.app/Contents/Info.plist (100%) rename qt/{bundle => launcher}/mac/dmg/set-dmg-settings.app/Contents/MacOS/applet (100%) rename qt/{bundle => launcher}/mac/dmg/set-dmg-settings.app/Contents/PkgInfo (100%) rename qt/{bundle => launcher}/mac/dmg/set-dmg-settings.app/Contents/Resources/Scripts/main.scpt (100%) rename qt/{bundle => launcher}/mac/dmg/set-dmg-settings.app/Contents/Resources/applet.icns (100%) rename qt/{bundle => launcher}/mac/dmg/set-dmg-settings.app/Contents/Resources/applet.rsrc (100%) rename qt/{bundle => launcher}/mac/dmg/set-dmg-settings.app/Contents/Resources/description.rtfd/TXT.rtf (100%) rename qt/{bundle => launcher}/mac/dmg/set-dmg-settings.app/Contents/_CodeSignature/CodeResources (100%) rename qt/{bundle => launcher}/mac/dmg/set-dmg-settings.scpt (100%) rename qt/{bundle => launcher}/mac/entitlements.python.xml (100%) rename qt/{bundle => launcher}/mac/icon/Assets.car (100%) rename qt/{bundle => launcher}/mac/icon/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename qt/{bundle => launcher}/mac/icon/Assets.xcassets/AppIcon.appiconset/round-1024-512.png (100%) rename qt/{bundle => launcher}/mac/icon/Assets.xcassets/Contents.json (100%) rename qt/{bundle => launcher}/mac/icon/build.sh (100%) create mode 100755 qt/launcher/mac/notarize.sh create mode 100644 qt/launcher/pyproject.toml create mode 100644 qt/launcher/src/bin/build_win.rs create mode 100644 qt/launcher/src/main.rs create mode 100644 qt/launcher/src/platform/mac.rs create mode 100644 qt/launcher/src/platform/mod.rs create mode 100644 qt/launcher/src/platform/unix.rs create mode 100644 qt/launcher/src/platform/windows.rs rename qt/{bundle => launcher}/win/anki-console.bat (100%) rename qt/{bundle => launcher}/win/anki-icon.ico (100%) rename qt/{bundle => launcher}/win/anki-manifest.rc (100%) create mode 100644 qt/launcher/win/anki.exe.manifest rename qt/{bundle => launcher}/win/anki.template.nsi (90%) create mode 100644 qt/launcher/win/build.bat rename qt/{bundle => launcher}/win/fileassoc.nsh (100%) create mode 100644 qt/pyproject.toml create mode 100755 qt/release/publish.sh create mode 100644 qt/release/pyproject.toml create mode 100755 qt/release/update.sh create mode 100755 tools/build-arm-lin create mode 100755 tools/build-x64-mac delete mode 100755 tools/mac-x86 create mode 100755 tools/publish delete mode 100755 tools/run-qt5.14 delete mode 100755 tools/run-qt5.15 delete mode 100755 tools/run-qt5.15.bat create mode 100755 tools/run-qt6.6 create mode 100644 uv.lock diff --git a/.cargo/config.toml b/.cargo/config.toml index 67f0dea34..3fbb3be1b 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -5,7 +5,8 @@ DESCRIPTORS_BIN = { value = "out/rslib/proto/descriptors.bin", relative = true } # build script will append .exe if necessary PROTOC = { value = "out/extracted/protoc/bin/protoc", relative = true } PYO3_NO_PYTHON = "1" -MACOSX_DEPLOYMENT_TARGET = "10.13.4" +MACOSX_DEPLOYMENT_TARGET = "11" +PYTHONDONTWRITEBYTECODE = "1" # prevent junk files on Windows [term] color = "always" diff --git a/.deny.toml b/.deny.toml index 8a379fa55..7cdf0cf99 100644 --- a/.deny.toml +++ b/.deny.toml @@ -5,9 +5,6 @@ db-path = "~/.cargo/advisory-db" db-urls = ["https://github.com/rustsec/advisory-db"] ignore = [ - # pyoxidizer is stuck on an old ring version - "RUSTSEC-2025-0009", - "RUSTSEC-2025-0010", # burn depends on an unmaintained package 'paste' "RUSTSEC-2024-0436", ] @@ -17,12 +14,11 @@ allow = [ "MIT", "Apache-2.0", "Apache-2.0 WITH LLVM-exception", + "CDLA-Permissive-2.0", "ISC", "MPL-2.0", - "Unicode-DFS-2016", "BSD-2-Clause", "BSD-3-Clause", - "OpenSSL", "CC0-1.0", "Unlicense", "Zlib", diff --git a/.dprint.json b/.dprint.json index 8e9f19b40..4230cdcd6 100644 --- a/.dprint.json +++ b/.dprint.json @@ -20,7 +20,6 @@ "ftl/usage", "licenses.json", ".dmypy.json", - "qt/bundle/PyOxidizer", "target", ".mypy_cache", "extra", diff --git a/.gitmodules b/.gitmodules index 90cec9ca9..50b5aa9f3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,9 +6,3 @@ path = ftl/qt-repo url = https://github.com/ankitects/anki-desktop-ftl.git shallow = true -[submodule "qt/bundle/PyOxidizer"] - path = qt/bundle/PyOxidizer - url = https://github.com/ankitects/PyOxidizer.git - shallow = true - update = none - diff --git a/.isort.cfg b/.isort.cfg index 109f5c21e..a26991a95 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -2,4 +2,3 @@ py_version=39 known_first_party=anki,aqt,tests profile=black -extend_skip=qt/bundle diff --git a/.mypy.ini b/.mypy.ini index 648c6a6ea..9fb8d3689 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -18,7 +18,7 @@ mypy_path = ftl, pylib/tools, python -exclude = (qt/bundle/PyOxidizer|pylib/anki/_vendor) +exclude = (pylib/anki/_vendor) [mypy-anki.*] disallow_untyped_defs = True @@ -165,3 +165,5 @@ ignore_missing_imports = True ignore_missing_imports = True [mypy-pip_system_certs.*] ignore_missing_imports = True +[mypy-anki_audio] +ignore_missing_imports = True diff --git a/.python-version b/.python-version new file mode 100644 index 000000000..86f8c02eb --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13.5 diff --git a/.ruff.toml b/.ruff.toml index 498ecbdac..fb6ffa2d8 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -1,2 +1,2 @@ target-version = "py39" -extend-exclude = ["qt/bundle"] +extend-exclude = [] diff --git a/.vscode.dist/settings.json b/.vscode.dist/settings.json index ffac17cae..ab91e06ab 100644 --- a/.vscode.dist/settings.json +++ b/.vscode.dist/settings.json @@ -31,11 +31,13 @@ "rust-analyzer.rustfmt.extraArgs": ["+nightly"], "search.exclude": { "**/node_modules": true, - ".bazel/**": true, - "qt/bundle/PyOxidizer": true + ".bazel/**": true }, "rust-analyzer.cargo.buildScripts.enable": true, "python.analysis.typeCheckingMode": "off", + "python.analysis.exclude": [ + "out/launcher/**" + ], "terminal.integrated.env.windows": { "PATH": "c:\\msys64\\usr\\bin;${env:Path}" } diff --git a/Cargo.lock b/Cargo.lock index 28073f3ac..66173027b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,20 +13,20 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -103,7 +103,7 @@ dependencies = [ "csv", "data-encoding", "difflib", - "dirs 5.0.1", + "dirs 6.0.0", "envy", "flate2", "fluent", @@ -116,7 +116,7 @@ dependencies = [ "hyper 1.6.0", "id_tree", "inflections", - "itertools 0.13.0", + "itertools 0.14.0", "nom", "num_cpus", "num_enum", @@ -128,10 +128,10 @@ dependencies = [ "prettyplease", "prost", "prost-reflect", - "pulldown-cmark 0.9.6", - "rand 0.8.5", + "pulldown-cmark 0.13.0", + "rand 0.9.1", "regex", - "reqwest 0.12.15", + "reqwest 0.12.20", "rusqlite", "rustls-pemfile 2.2.0", "scopeguard", @@ -142,8 +142,8 @@ dependencies = [ "serde_tuple", "sha1", "snafu", - "strum 0.26.3", - "syn 2.0.101", + "strum 0.27.1", + "syn 2.0.103", "tempfile", "tokio", "tokio-util", @@ -154,9 +154,9 @@ dependencies = [ "unic-ucd-category", "unicase", "unicode-normalization", - "windows 0.56.0", + "windows 0.61.3", "wiremock", - "zip 0.6.6", + "zip 4.1.0", "zstd", ] @@ -178,7 +178,7 @@ dependencies = [ "fluent-syntax", "inflections", "intl-memoizer", - "itertools 0.13.0", + "itertools 0.14.0", "num-format", "phf 0.11.3", "serde", @@ -199,7 +199,7 @@ dependencies = [ name = "anki_process" version = "0.0.0" dependencies = [ - "itertools 0.13.0", + "itertools 0.14.0", "snafu", ] @@ -211,14 +211,14 @@ dependencies = [ "anki_proto_gen", "anyhow", "inflections", - "itertools 0.13.0", + "itertools 0.14.0", "prost", "prost-build", "prost-reflect", "prost-types", "serde", "snafu", - "strum 0.26.3", + "strum 0.27.1", ] [[package]] @@ -229,7 +229,7 @@ dependencies = [ "anyhow", "camino", "inflections", - "itertools 0.13.0", + "itertools 0.14.0", "prost-reflect", "prost-types", "regex", @@ -238,9 +238,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", @@ -253,36 +253,36 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", - "once_cell", + "once_cell_polyfill", "windows-sys 0.59.0", ] @@ -292,18 +292,6 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" -[[package]] -name = "apple-bundles" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "716b8a7bacf7325eb3e7a1a7f5ead4da91e1e16d9b56a25edea0e1e4ba21fd8e" -dependencies = [ - "anyhow", - "plist", - "simple-file-manifest", - "walkdir", -] - [[package]] name = "arbitrary" version = "1.4.1" @@ -358,9 +346,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07" +checksum = "d615619615a650c571269c00dca41db04b9210037fa76ed8239f70404ab56985" dependencies = [ "futures-core", "memchr", @@ -389,7 +377,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -400,7 +388,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -423,14 +411,14 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.7.9" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" dependencies = [ - "async-trait", "axum-core", "axum-macros", "bytes", + "form_urlencoded", "futures-util", "http 1.3.1", "http-body 1.0.1", @@ -459,24 +447,23 @@ dependencies = [ [[package]] name = "axum-client-ip" -version = "0.6.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eefda7e2b27e1bda4d6fa8a06b50803b8793769045918bc37ad062d48a6efac" +checksum = "3f08a543641554404b42acd0d2494df12ca2be034d7b8ee4dbbf7446f940a2ef" dependencies = [ "axum", - "forwarded-header-value", + "client-ip", "serde", ] [[package]] name = "axum-core" -version = "0.4.5" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" dependencies = [ - "async-trait", "bytes", - "futures-util", + "futures-core", "http 1.3.1", "http-body 1.0.1", "http-body-util", @@ -491,22 +478,21 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.9.6" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c794b30c904f0a1c2fb7740f7df7f7972dfaa14ef6f57cb6178dc63e5dca2f04" +checksum = "45bf463831f5131b7d3c756525b305d40f1185b688565648a92e1392ca35713d" dependencies = [ "axum", "axum-core", "bytes", - "fastrand", "futures-util", - "headers 0.4.0", + "headers 0.4.1", "http 1.3.1", "http-body 1.0.1", "http-body-util", "mime", - "multer", "pin-project-lite", + "rustversion", "serde", "tower", "tower-layer", @@ -515,20 +501,20 @@ dependencies = [ [[package]] name = "axum-macros" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" +checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -539,12 +525,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.7" @@ -559,9 +539,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "bincode" @@ -596,9 +576,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" dependencies = [ "serde", ] @@ -631,15 +611,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - [[package]] name = "bstr" version = "1.12.0" @@ -653,9 +624,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" [[package]] name = "burn" @@ -683,7 +654,7 @@ dependencies = [ "burn-common", "burn-tensor", "derive-new 0.7.0", - "hashbrown 0.15.2", + "hashbrown 0.15.4", "log", "num-traits", "portable-atomic", @@ -729,7 +700,7 @@ dependencies = [ "derive-new 0.7.0", "flate2", "half", - "hashbrown 0.15.2", + "hashbrown 0.15.4", "log", "num-traits", "portable-atomic-util", @@ -756,7 +727,7 @@ dependencies = [ "derive-new 0.7.0", "futures-lite", "half", - "hashbrown 0.15.2", + "hashbrown 0.15.4", "log", "num-traits", "rand 0.9.1", @@ -808,7 +779,7 @@ dependencies = [ "derive-new 0.7.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -818,7 +789,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d63629f2c8b82ee52dbb9c18becded5117c2faf57365dc271a55c16d139cd91a" dependencies = [ "burn-tensor", - "hashbrown 0.15.2", + "hashbrown 0.15.4", "portable-atomic-util", "serde", ] @@ -873,7 +844,7 @@ dependencies = [ "burn-common", "burn-ir", "burn-tensor", - "hashbrown 0.15.2", + "hashbrown 0.15.4", "log", "spin 0.10.0", ] @@ -890,10 +861,10 @@ dependencies = [ "cubecl", "derive-new 0.7.0", "half", - "hashbrown 0.15.2", + "hashbrown 0.15.4", "num-traits", "rand 0.9.1", - "rand_distr 0.5.1", + "rand_distr", "serde", "serde_bytes", ] @@ -932,9 +903,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.22.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" dependencies = [ "bytemuck_derive", ] @@ -947,7 +918,7 @@ checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -970,9 +941,9 @@ checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659" [[package]] name = "camino" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab" [[package]] name = "candle-core" @@ -987,12 +958,12 @@ dependencies = [ "num-traits", "num_cpus", "rand 0.9.1", - "rand_distr 0.5.1", + "rand_distr", "rayon", "safetensors", "thiserror 1.0.69", "ug", - "yoke", + "yoke 0.7.5", "zip 1.1.4", ] @@ -1002,20 +973,11 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - [[package]] name = "cc" -version = "1.2.20" +version = "1.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" +checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" dependencies = [ "jobserver", "libc", @@ -1024,9 +986,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" @@ -1036,9 +998,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1073,21 +1035,11 @@ dependencies = [ "half", ] -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - [[package]] name = "clap" -version = "4.5.37" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", "clap_derive", @@ -1095,9 +1047,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.37" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", @@ -1108,30 +1060,39 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.47" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06f5378ea264ad4f82bbc826628b5aad714a75abf6ece087e923010eb937fb6" +checksum = "aad5b1b4de04fead402672b48897030eec1f3bfe1550776322f59f6d6e6a5677" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "client-ip" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31211fc26899744f5b22521fdc971e5f3875991d8880537537470685a0e9552d" +dependencies = [ + "http 1.3.1", +] [[package]] name = "coarsetime" @@ -1173,14 +1134,14 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ "serde", "termcolor", - "unicode-width 0.2.0", + "unicode-width 0.2.1", ] [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "colored" @@ -1205,7 +1166,7 @@ name = "configure" version = "0.0.0" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.14.0", "ninja_gen", ] @@ -1217,9 +1178,9 @@ checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "convert_case" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" dependencies = [ "unicode-segmentation", ] @@ -1236,9 +1197,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -1281,25 +1242,22 @@ dependencies = [ [[package]] name = "criterion" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +checksum = "3bf7af66b0989381bd0be551bd7cc91912a655a58c6918420c9527b1fd8b4679" dependencies = [ "anes", "cast", "ciborium", "clap", "criterion-plot", - "is-terminal", - "itertools 0.10.5", + "itertools 0.13.0", "num-traits", - "once_cell", "oorandom", "plotters", "rayon", "regex", "serde", - "serde_derive", "serde_json", "tinytemplate", "walkdir", @@ -1385,7 +1343,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1456,7 +1414,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b03bf4211cdbd68bb0fb8291e0ed825c13da0d1ac01b7c02dce3cee44a6138be" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "bytemuck", "cubecl-common", "cubecl-ir", @@ -1527,11 +1485,12 @@ dependencies = [ [[package]] name = "cubecl-hip-sys" -version = "6.4.0" +version = "6.4.4348200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7557762176858fa0357504025f09ae6e979c3547776ff8b6a1025ef0702450" +checksum = "283fa7401056c53fb27e18f5d1806246bb5f937c4ecbd2453896f7a9ec495c73" dependencies = [ "libc", + "regex", ] [[package]] @@ -1582,7 +1541,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1594,7 +1553,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1696,7 +1655,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1707,7 +1666,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1716,17 +1675,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" -[[package]] -name = "dbus" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" -dependencies = [ - "libc", - "libdbus-sys", - "winapi", -] - [[package]] name = "deadpool" version = "0.10.0" @@ -1762,7 +1710,7 @@ checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1773,7 +1721,7 @@ checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1784,7 +1732,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1805,7 +1753,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1815,7 +1763,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1835,19 +1783,10 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "unicode-xid", ] -[[package]] -name = "des" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" -dependencies = [ - "cipher", -] - [[package]] name = "difflib" version = "0.4.0" @@ -1904,7 +1843,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.0", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1915,7 +1854,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1942,18 +1881,6 @@ dependencies = [ "dtoa", ] -[[package]] -name = "duct" -version = "0.13.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ab5718d1224b63252cd0c6f74f6480f9ffeb117438a2e0f5cf6d9a4798929c" -dependencies = [ - "libc", - "once_cell", - "os_pipe", - "shared_child", -] - [[package]] name = "dunce" version = "1.0.5" @@ -2003,6 +1930,20 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067" +[[package]] +name = "embed-resource" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0963f530273dc3022ab2bdc3fcd6d488e850256f2284a82b7413cb9481ee85dd" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 0.8.23", + "vswhom", + "winreg 0.55.0", +] + [[package]] name = "encoding_rs" version = "0.8.35" @@ -2021,7 +1962,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -2034,6 +1975,12 @@ dependencies = [ "regex", ] +[[package]] +name = "env_home" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" + [[package]] name = "env_logger" version = "0.11.8" @@ -2064,9 +2011,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", "windows-sys 0.59.0", @@ -2123,17 +2070,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "find-winsdk" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8cbf17b871570c1f8612b763bac3e86290602bcf5dc3c5ce657e0e1e9071d9e" -dependencies = [ - "serde", - "serde_derive", - "winreg 0.5.1", -] - [[package]] name = "fixedbitset" version = "0.5.7" @@ -2142,11 +2078,12 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", + "libz-rs-sys", "miniz_oxide", ] @@ -2158,9 +2095,9 @@ checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" [[package]] name = "fluent" -version = "0.16.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb74634707bebd0ce645a981148e8fb8c7bccd4c33c652aeffd28bf2f96d555a" +checksum = "8137a6d5a2c50d6b0ebfcb9aaa91a28154e0a70605f112d30cb0cd4a78670477" dependencies = [ "fluent-bundle", "unic-langid", @@ -2168,16 +2105,16 @@ dependencies = [ [[package]] name = "fluent-bundle" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493" +checksum = "01203cb8918f5711e73891b347816d932046f95f54207710bda99beaeb423bf4" dependencies = [ "fluent-langneg", "fluent-syntax", "intl-memoizer", "intl_pluralrules", - "rustc-hash 1.1.0", - "self_cell 0.10.3", + "rustc-hash 2.1.1", + "self_cell", "smallvec", "unic-langid", ] @@ -2193,11 +2130,12 @@ dependencies = [ [[package]] name = "fluent-syntax" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" +checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198" dependencies = [ - "thiserror 1.0.69", + "memchr", + "thiserror 2.0.12", ] [[package]] @@ -2239,7 +2177,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -2263,26 +2201,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "forwarded-header-value" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" -dependencies = [ - "nonempty", - "thiserror 1.0.69", -] - -[[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "fsevent-sys" version = "4.1.0" @@ -2294,16 +2212,16 @@ dependencies = [ [[package]] name = "fsrs" -version = "4.0.0" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2a3f0432b200326eed062fdcf5b1cef82ab9ba6635021a45f355ccca187ca3" +checksum = "c1f3a8c3df2c324ebab71461178fe8c1fe2d7373cf603f312b652befd026f06d" dependencies = [ "burn", "itertools 0.14.0", "log", "ndarray", - "ndarray-rand", "priority-queue", + "rand 0.9.1", "rayon", "serde", "snafu", @@ -2320,7 +2238,7 @@ dependencies = [ "camino", "clap", "fluent-syntax", - "itertools 0.13.0", + "itertools 0.14.0", "regex", "serde_json", "snafu", @@ -2406,7 +2324,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -2579,7 +2497,7 @@ dependencies = [ "num-traits", "once_cell", "paste", - "pulp 0.21.4", + "pulp 0.21.5", "raw-cpuid 11.5.0", "rayon", "seq-macro", @@ -2694,11 +2612,11 @@ dependencies = [ [[package]] name = "getopts" -version = "0.2.21" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1" dependencies = [ - "unicode-width 0.1.14", + "unicode-width 0.2.1", ] [[package]] @@ -2710,15 +2628,15 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "js-sys", @@ -2791,7 +2709,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "gpu-alloc-types", ] @@ -2801,7 +2719,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -2818,13 +2736,13 @@ dependencies = [ [[package]] name = "gpu-descriptor" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "gpu-descriptor-types", - "hashbrown 0.15.2", + "hashbrown 0.15.4", ] [[package]] @@ -2833,7 +2751,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -2857,9 +2775,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" +checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" dependencies = [ "atomic-waker", "bytes", @@ -2885,7 +2803,7 @@ dependencies = [ "crunchy", "num-traits", "rand 0.9.1", - "rand_distr 0.5.1", + "rand_distr", "serde", ] @@ -2927,9 +2845,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "allocator-api2", "equivalent", @@ -2939,11 +2857,11 @@ dependencies = [ [[package]] name = "hashlink" -version = "0.8.4" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.4", ] [[package]] @@ -2963,11 +2881,11 @@ dependencies = [ [[package]] name = "headers" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "bytes", "headers-core 0.3.0", "http 1.3.1", @@ -3002,15 +2920,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hermit-abi" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -3033,15 +2945,6 @@ dependencies = [ "digest", ] -[[package]] -name = "home" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "html5ever" version = "0.26.0" @@ -3175,7 +3078,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.9", + "h2 0.4.10", "http 1.3.1", "http-body 1.0.1", "httparse", @@ -3189,35 +3092,20 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.2" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "futures-util", - "http 0.2.12", - "hyper 0.14.32", - "rustls 0.21.12", - "tokio", - "tokio-rustls 0.24.1", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" -dependencies = [ - "futures-util", "http 1.3.1", "hyper 1.6.0", "hyper-util", - "rustls 0.23.26", + "rustls", "rustls-native-certs", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.2", + "tokio-rustls", "tower-service", - "webpki-roots 0.26.8", + "webpki-roots", ] [[package]] @@ -3251,17 +3139,21 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" dependencies = [ + "base64 0.22.1", "bytes", "futures-channel", + "futures-core", "futures-util", "http 1.3.1", "http-body 1.0.1", "hyper 1.6.0", + "ipnet", "libc", + "percent-encoding", "pin-project-lite", "socket2", "tokio", @@ -3281,7 +3173,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.0", + "windows-core 0.61.2", ] [[package]] @@ -3295,21 +3187,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", - "yoke", + "potential_utf", + "yoke 0.8.0", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -3318,31 +3211,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -3350,67 +3223,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", - "yoke", + "yoke 0.8.0", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "id_tree" version = "1.8.0" @@ -3439,9 +3299,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -3470,7 +3330,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.4", ] [[package]] @@ -3491,7 +3351,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "inotify-sys", "libc", ] @@ -3505,21 +3365,11 @@ dependencies = [ "libc", ] -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "block-padding", - "generic-array", -] - [[package]] name = "intl-memoizer" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe22e020fce238ae18a6d5d8c502ee76a52a6e880d99477657e6acc30ec57bda" +checksum = "310da2e345f5eb861e7a07ee182262e94975051db9e4223e909ba90f392f163f" dependencies = [ "type-map", "unic-langid", @@ -3541,14 +3391,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] -name = "is-terminal" -version = "0.4.16" +name = "iri-string" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" dependencies = [ - "hermit-abi 0.5.0", - "libc", - "windows-sys 0.59.0", + "memchr", + "serde", ] [[package]] @@ -3592,9 +3441,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a064218214dc6a10fbae5ec5fa888d80c45d611aba169222fc272072bf7aef6" +checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" dependencies = [ "jiff-static", "log", @@ -3605,13 +3454,13 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "199b7932d97e325aff3a7030e141eafe7f2c6268e1d1b24859b753a627f45254" +checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -3626,7 +3475,7 @@ version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", "libc", ] @@ -3669,9 +3518,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kqueue" -version = "1.0.8" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" dependencies = [ "kqueue-sys", "libc", @@ -3687,6 +3536,20 @@ dependencies = [ "libc", ] +[[package]] +name = "launcher" +version = "1.0.0" +dependencies = [ + "anki_io", + "anki_process", + "anyhow", + "dirs 6.0.0", + "embed-resource", + "libc", + "libc-stdhandle", + "winapi", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -3695,35 +3558,35 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" [[package]] -name = "libdbus-sys" -version = "0.2.5" +name = "libc-stdhandle" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +checksum = "6dac2473dc28934c5e0b82250dab231c9d3b94160d91fe9ff483323b05797551" dependencies = [ "cc", - "pkg-config", + "libc", ] [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.53.2", ] [[package]] name = "libm" -version = "0.2.13" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" @@ -3731,22 +3594,31 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "libc", "redox_syscall", ] [[package]] name = "libsqlite3-sys" -version = "0.27.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +checksum = "91632f3b4fb6bd1d72aa3d78f41ffecfcf2b1a6648d8c241dbe7dbfaf4875e15" dependencies = [ "cc", "pkg-config", "vcpkg", ] +[[package]] +name = "libz-rs-sys" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221" +dependencies = [ + "zlib-rs", +] + [[package]] name = "linkcheck" version = "0.4.1" @@ -3777,11 +3649,11 @@ version = "0.0.0" dependencies = [ "anki", "futures", - "itertools 0.13.0", + "itertools 0.14.0", "linkcheck", "regex", - "reqwest 0.12.15", - "strum 0.26.3", + "reqwest 0.12.20", + "strum 0.27.1", "tokio", ] @@ -3794,12 +3666,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -3808,9 +3674,9 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "litrs" @@ -3820,9 +3686,9 @@ checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -3834,6 +3700,12 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "lzma-sys" version = "0.1.20" @@ -3853,54 +3725,29 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "macerator" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f684f0f95ca0724667e4baf9bf60dc662cb2f6235fd9402d754f5512440efe0e" +checksum = "bce07f822458c4c303081d133a90610406162e7c8df17434956ac1892faf447b" dependencies = [ "bytemuck", + "cfg_aliases", "half", "macerator-macros", + "moddef", "num-traits", "paste", ] [[package]] name = "macerator-macros" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806a19478649154a009ef47e9361db11ed392a8f7978590eed4a590ceef01bdd" +checksum = "a2b955a106dca78c0577269d67a6d56114abb8644b810fc995a22348276bb9dd" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.101", -] - -[[package]] -name = "makeapp" -version = "0.0.0" -dependencies = [ - "anyhow", - "apple-bundles", - "camino", - "clap", - "glob", - "plist", - "serde", - "serde_json", - "simple-file-manifest", - "walkdir", -] - -[[package]] -name = "makeexe" -version = "0.0.0" -dependencies = [ - "anyhow", - "camino", - "clap", - "tugger-windows-codesign", - "walkdir", + "syn 2.0.103", ] [[package]] @@ -3963,7 +3810,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -3983,15 +3830,15 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matchit" -version = "0.7.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "matrixmultiply" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" dependencies = [ "autocfg", "num_cpus", @@ -4008,9 +3855,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "mdbook" -version = "0.4.48" +version = "0.4.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6fbb4ac2d9fd7aa987c3510309ea3c80004a968d063c42f0d34fea070817c1" +checksum = "a87e65420ab45ca9c1b8cdf698f95b710cc826d373fa550f0f7fad82beac9328" dependencies = [ "ammonia", "anyhow", @@ -4027,7 +3874,6 @@ dependencies = [ "memchr", "notify", "notify-debouncer-mini", - "once_cell", "opener", "pathdiff", "pulldown-cmark 0.10.3", @@ -4038,7 +3884,7 @@ dependencies = [ "shlex", "tempfile", "tokio", - "toml", + "toml 0.5.11", "topological-sort", "walkdir", "warp", @@ -4046,9 +3892,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memmap2" @@ -4075,7 +3921,7 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block", "core-graphics-types", "foreign-types 0.5.0", @@ -4120,25 +3966,31 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] +[[package]] +name = "moddef" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e519fd9c6131c1c9a4a67f8bdc4f32eb4105b16c1468adea1b8e68c98c85ec4" + [[package]] name = "multer" version = "3.1.0" @@ -4158,9 +4010,9 @@ dependencies = [ [[package]] name = "multimap" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" [[package]] name = "naga" @@ -4170,11 +4022,11 @@ checksum = "2b977c445f26e49757f9aca3631c3b8b836942cb278d69a92e7b80d3b24da632" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.9.0", + "bitflags 2.9.1", "cfg_aliases", "codespan-reporting 0.12.0", "half", - "hashbrown 0.15.2", + "hashbrown 0.15.4", "hexf-parse", "indexmap", "log", @@ -4220,17 +4072,6 @@ dependencies = [ "rayon", ] -[[package]] -name = "ndarray-rand" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f093b3db6fd194718dcdeea6bd8c829417deae904e3fcc7732dabcd4416d25d8" -dependencies = [ - "ndarray", - "rand 0.8.5", - "rand_distr 0.4.3", -] - [[package]] name = "ndk-sys" version = "0.5.0+25.2.9519653" @@ -4255,9 +4096,13 @@ dependencies = [ "camino", "dunce", "globset", - "itertools 0.13.0", + "itertools 0.14.0", "maplit", "num_cpus", + "regex", + "reqwest 0.12.20", + "serde_json", + "sha2", "walkdir", "which", ] @@ -4272,12 +4117,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nonempty" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" - [[package]] name = "normpath" version = "1.3.0" @@ -4293,7 +4132,7 @@ version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "filetime", "fsevent-sys", "inotify", @@ -4451,11 +4290,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", ] @@ -4477,7 +4316,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -4486,7 +4325,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c9bff0aa1d48904a1385ea2a8b97576fbdcbc9a3cfccd0d31fe978e1c4038c5" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "libloading", "nvml-wrapper-sys", "static_assertions", @@ -4527,6 +4366,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + [[package]] name = "oorandom" version = "11.1.5" @@ -4535,23 +4380,22 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "opener" -version = "0.7.2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0812e5e4df08da354c851a3376fead46db31c2214f849d3de356d774d057681" +checksum = "771b9704f8cd8b424ec747a320b30b47517a6966ba2c7da90047c16f4a962223" dependencies = [ "bstr", - "dbus", "normpath", "windows-sys 0.59.0", ] [[package]] name = "openssl" -version = "0.10.72" +version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cfg-if", "foreign-types 0.3.2", "libc", @@ -4568,7 +4412,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -4579,9 +4423,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.107" +version = "0.9.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ "cc", "libc", @@ -4613,39 +4457,12 @@ dependencies = [ "num-traits", ] -[[package]] -name = "os_pipe" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" -[[package]] -name = "p12" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4873306de53fe82e7e484df31e1e947d61514b6ea2ed6cd7b45d63006fd9224" -dependencies = [ - "cbc", - "cipher", - "des", - "getrandom 0.2.16", - "hmac", - "lazy_static", - "rc2", - "sha1", - "yasna", -] - [[package]] name = "parking" version = "2.2.1" @@ -4654,9 +4471,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -4664,9 +4481,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", @@ -4710,15 +4527,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "pem" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" -dependencies = [ - "base64 0.13.1", -] - [[package]] name = "percent-encoding" version = "2.3.1" @@ -4732,9 +4540,9 @@ source = "git+https://github.com/ankitects/rust-url.git?rev=bb930b8d089f4d30d7d1 [[package]] name = "pest" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" +checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", "thiserror 2.0.12", @@ -4743,9 +4551,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" +checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" dependencies = [ "pest", "pest_generator", @@ -4753,24 +4561,23 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" +checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "pest_meta" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" +checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" dependencies = [ - "once_cell", "pest", "sha2", ] @@ -4854,7 +4661,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -4892,7 +4699,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -4913,19 +4720,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "plist" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" -dependencies = [ - "base64 0.22.1", - "indexmap", - "quick-xml", - "serde", - "time", -] - [[package]] name = "plotters" version = "0.3.7" @@ -4956,9 +4750,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" dependencies = [ "serde", ] @@ -4972,6 +4766,15 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -4984,7 +4787,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.25", + "zerocopy", ] [[package]] @@ -5001,12 +4804,12 @@ checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" [[package]] name = "prettyplease" -version = "0.2.32" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" +checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55" dependencies = [ "proc-macro2", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -5076,7 +4879,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.101", + "syn 2.0.103", "tempfile", ] @@ -5090,7 +4893,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -5127,25 +4930,26 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.9.6" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" +checksum = "76979bea66e7875e7509c4ec5300112b316af87fa7a252ca91c448b32dfe3993" dependencies = [ - "bitflags 2.9.0", - "getopts", + "bitflags 2.9.1", "memchr", + "pulldown-cmark-escape 0.10.1", "unicase", ] [[package]] name = "pulldown-cmark" -version = "0.10.3" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76979bea66e7875e7509c4ec5300112b316af87fa7a252ca91c448b32dfe3993" +checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", + "getopts", "memchr", - "pulldown-cmark-escape", + "pulldown-cmark-escape 0.11.0", "unicase", ] @@ -5155,6 +4959,12 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd348ff538bc9caeda7ee8cad2d1d48236a1f443c1fa3913c6a02fe0043b1dd3" +[[package]] +name = "pulldown-cmark-escape" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" + [[package]] name = "pulp" version = "0.18.22" @@ -5169,9 +4979,9 @@ dependencies = [ [[package]] name = "pulp" -version = "0.21.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fb7a99b37aaef4c7dd2fd15a819eb8010bfc7a2c2155230d51f497316cad6d" +checksum = "96b86df24f0a7ddd5e4b95c94fc9ed8a98f1ca94d3b01bdce2824097e7835907" dependencies = [ "bytemuck", "cfg-if", @@ -5183,11 +4993,10 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5203598f366b11a02b13aa20cab591229ff0a89fd121a308a5df751d5fc9219" +checksum = "8970a78afe0628a3e3430376fc5fd76b6b45c4d43360ffd6cdd40bdde72b682a" dependencies = [ - "cfg-if", "indoc", "libc", "memoffset", @@ -5201,9 +5010,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99636d423fa2ca130fa5acde3059308006d46f98caac629418e53f7ebb1e9999" +checksum = "458eb0c55e7ece017adeba38f2248ff3ac615e53660d7c71a238d7d2a01c7598" dependencies = [ "once_cell", "target-lexicon", @@ -5211,9 +5020,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78f9cf92ba9c409279bc3305b5409d90db2d2c22392d443a87df3a1adad59e33" +checksum = "7114fe5457c61b276ab77c5055f206295b812608083644a5c5b2640c3102565c" dependencies = [ "libc", "pyo3-build-config", @@ -5221,43 +5030,34 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b999cb1a6ce21f9a6b147dcf1be9ffedf02e0043aec74dc390f3007047cecd9" +checksum = "a8725c0a622b374d6cb051d11a0983786448f7785336139c3c94f5aa6bef7e50" dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "pyo3-macros-backend" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "822ece1c7e1012745607d5cf0bcb2874769f0f7cb34c4cde03b9358eb9ef911a" +checksum = "4109984c22491085343c05b0dbc54ddc405c3cf7b4374fc533f5c3313a572ccc" dependencies = [ "heck", "proc-macro2", "pyo3-build-config", "quote", - "syn 2.0.101", -] - -[[package]] -name = "quick-xml" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" -dependencies = [ - "memchr", + "syn 2.0.103", ] [[package]] name = "quinn" -version = "0.11.7" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012" +checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" dependencies = [ "bytes", "cfg_aliases", @@ -5265,7 +5065,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.1.1", - "rustls 0.23.26", + "rustls", "socket2", "thiserror 2.0.12", "tokio", @@ -5275,16 +5075,17 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.11" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcbafbbdbb0f638fe3f35f3c56739f77a8a1d070cb25603226c83339b391472b" +checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" dependencies = [ "bytes", - "getrandom 0.3.2", + "getrandom 0.3.3", + "lru-slab", "rand 0.9.1", - "ring 0.17.14", + "ring", "rustc-hash 2.1.1", - "rustls 0.23.26", + "rustls", "rustls-pki-types", "slab", "thiserror 2.0.12", @@ -5295,9 +5096,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5" +checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" dependencies = [ "cfg_aliases", "libc", @@ -5378,17 +5179,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", -] - -[[package]] -name = "rand_distr" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" -dependencies = [ - "num-traits", - "rand 0.8.5", + "getrandom 0.3.3", ] [[package]] @@ -5422,7 +5213,7 @@ version = "11.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -5457,27 +5248,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "rc2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62c64daa8e9438b84aaae55010a93f396f8e60e3911590fcba770d04643fc1dd" -dependencies = [ - "cipher", -] - -[[package]] -name = "rcgen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" -dependencies = [ - "pem", - "ring 0.16.20", - "time", - "yasna", -] - [[package]] name = "reborrow" version = "0.5.5" @@ -5486,11 +5256,11 @@ checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -5586,7 +5356,6 @@ dependencies = [ "http 0.2.12", "http-body 0.4.6", "hyper 0.14.32", - "hyper-rustls 0.24.2", "hyper-tls 0.5.0", "ipnet", "js-sys", @@ -5596,7 +5365,6 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.12", "rustls-pemfile 1.0.4", "serde", "serde_json", @@ -5605,46 +5373,41 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", - "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.25.4", "winreg 0.50.0", ] [[package]] name = "reqwest" -version = "0.12.15" +version = "0.12.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" dependencies = [ "base64 0.22.1", "bytes", + "futures-channel", "futures-core", "futures-util", "http 1.3.1", "http-body 1.0.1", "http-body-util", "hyper 1.6.0", - "hyper-rustls 0.27.5", + "hyper-rustls", "hyper-tls 0.6.0", "hyper-util", - "ipnet", "js-sys", "log", - "mime", "mime_guess", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.26", + "rustls", "rustls-native-certs", - "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", "serde_json", @@ -5652,33 +5415,17 @@ dependencies = [ "sync_wrapper 1.0.2", "tokio", "tokio-native-tls", - "tokio-rustls 0.26.2", - "tokio-socks", + "tokio-rustls", "tokio-util", "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.26.8", - "windows-registry", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", + "webpki-roots", ] [[package]] @@ -5691,7 +5438,7 @@ dependencies = [ "cfg-if", "getrandom 0.2.16", "libc", - "untrusted 0.9.0", + "untrusted", "windows-sys 0.52.0", ] @@ -5751,7 +5498,7 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn 2.0.101", + "syn 2.0.103", "unicode-ident", ] @@ -5766,24 +5513,24 @@ dependencies = [ "clap", "flate2", "junction", - "reqwest 0.12.15", + "reqwest 0.12.20", "sha2", "tar", "termcolor", "tokio", "which", "xz2", - "zip 0.6.6", + "zip 4.1.0", "zstd", ] [[package]] name = "rusqlite" -version = "0.30.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d" +checksum = "3de23c3319433716cf134eed225fe9986bc24f63bed9be9f20c329029e672dc7" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -5793,9 +5540,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc-hash" @@ -5820,52 +5567,27 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.44" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "errno", "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustix" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" -dependencies = [ - "bitflags 2.9.0", - "errno", - "libc", - "linux-raw-sys 0.9.4", + "linux-raw-sys", "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.21.12" +version = "0.23.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" -dependencies = [ - "log", - "ring 0.17.14", - "rustls-webpki 0.101.7", - "sct", -] - -[[package]] -name = "rustls" -version = "0.23.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" +checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" dependencies = [ "once_cell", - "ring 0.17.14", + "ring", "rustls-pki-types", - "rustls-webpki 0.103.1", + "rustls-webpki", "subtle", "zeroize", ] @@ -5902,39 +5624,30 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ "web-time", + "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.103.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" dependencies = [ - "ring 0.17.14", - "untrusted 0.9.0", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" -dependencies = [ - "ring 0.17.14", + "ring", "rustls-pki-types", - "untrusted 0.9.0", + "untrusted", ] [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" @@ -6001,23 +5714,13 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring 0.17.14", - "untrusted 0.9.0", -] - [[package]] name = "security-framework" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -6030,8 +5733,8 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ - "bitflags 2.9.0", - "core-foundation 0.10.0", + "bitflags 2.9.1", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -6047,15 +5750,6 @@ dependencies = [ "libc", ] -[[package]] -name = "self_cell" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" -dependencies = [ - "self_cell 1.2.0", -] - [[package]] name = "self_cell" version = "1.2.0" @@ -6122,7 +5816,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -6155,14 +5849,23 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", ] [[package]] name = "serde_tuple" -version = "0.5.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f025b91216f15a2a32aa39669329a475733590a015835d1783549a56d09427" +checksum = "f0f9b739e59a0e07b7a73bc11c3dcd6abf790d0f54042b67a422d4bd1f6cf6c0" dependencies = [ "serde", "serde_tuple_macros", @@ -6170,9 +5873,9 @@ dependencies = [ [[package]] name = "serde_tuple_macros" -version = "0.5.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4076151d1a2b688e25aaf236997933c66e18b870d0369f8b248b8ab2be630d7e" +checksum = "9e87546e85c5047d03b454d12ee25266fc269a461a4029956ca58d246b9aefae" dependencies = [ "proc-macro2", "quote", @@ -6204,9 +5907,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -6222,16 +5925,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shared_child" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fa9338aed9a1df411814a5b2252f7cd206c55ae9bf2fa763f8de84603aa60c" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "shlex" version = "1.3.0" @@ -6248,10 +5941,10 @@ dependencies = [ ] [[package]] -name = "simple-file-manifest" -version = "0.11.0" +name = "simd-adler32" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd19be0257552dd56d1bb6946f89f193c6e5b9f13cc9327c4bc84a357507c74" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "siphasher" @@ -6267,12 +5960,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "slotmap" @@ -6285,9 +5975,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "snafu" @@ -6307,7 +5997,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -6318,20 +6008,14 @@ checksum = "27207bb65232eda1f588cf46db2fee75c0808d557f6b3cf19a75f5d6d7c94df1" [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -6358,7 +6042,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -6432,7 +6116,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -6445,7 +6129,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -6467,9 +6151,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.101" +version = "2.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" dependencies = [ "proc-macro2", "quote", @@ -6493,13 +6177,13 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -6508,7 +6192,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7dddc5f0fee506baf8b9fdb989e242f17e4b11c61dfbb0635b705217199eea" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "byteorder", "enum-as-inner", "libc", @@ -6522,7 +6206,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01198a2debb237c62b6826ec7081082d951f46dbb64b0e8c7649a452230d1dfc" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "byteorder", "enum-as-inner", "libc", @@ -6541,7 +6225,7 @@ dependencies = [ "memchr", "ntapi", "rayon", - "windows 0.57.0", + "windows 0.56.0", ] [[package]] @@ -6598,14 +6282,14 @@ checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.3", "once_cell", - "rustix 1.0.5", + "rustix", "windows-sys 0.59.0", ] @@ -6635,7 +6319,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" dependencies = [ - "rustix 1.0.5", + "rustix", "windows-sys 0.59.0", ] @@ -6676,7 +6360,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -6687,7 +6371,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -6701,12 +6385,11 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -6742,9 +6425,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -6777,9 +6460,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.44.2" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "bytes", @@ -6800,7 +6483,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -6813,35 +6496,13 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls 0.21.12", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.23.26", - "tokio", -] - -[[package]] -name = "tokio-socks" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" -dependencies = [ - "either", - "futures-util", - "thiserror 1.0.69", + "rustls", "tokio", ] @@ -6880,22 +6541,46 @@ dependencies = [ ] [[package]] -name = "toml_datetime" -version = "0.6.9" +name = "toml" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" -version = "0.22.25" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10558ed0bd2a1562e630926a2d1f0b98c827da99fabd3fe20920a59642504485" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", + "toml_write", "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "topological-sort" version = "0.2.2" @@ -6920,16 +6605,18 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.5.2" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "bytes", + "futures-util", "http 1.3.1", "http-body 1.0.1", - "http-body-util", + "iri-string", "pin-project-lite", + "tower", "tower-layer", "tower-service", "tracing", @@ -6973,20 +6660,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -7027,58 +6714,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "tugger-common" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90d950380afdb1a6bbe74f29485a04e821869dfad11f5929ff1c5b1dac09d02" -dependencies = [ - "anyhow", - "fs2", - "glob", - "hex", - "log", - "once_cell", - "reqwest 0.11.27", - "sha2", - "tempfile", - "url", - "zip 0.6.6", -] - -[[package]] -name = "tugger-windows" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f181ac4fc7f8facfd418824d13045cd068ee73de44319a6116868c22789782" -dependencies = [ - "anyhow", - "duct", - "find-winsdk", - "glob", - "once_cell", - "semver", - "tugger-common", - "winapi", -] - -[[package]] -name = "tugger-windows-codesign" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3f09f8bdace495373cec3fc607bc39f00720a984ba82e310cc9382462fd364" -dependencies = [ - "anyhow", - "duct", - "log", - "p12", - "rcgen", - "time", - "tugger-common", - "tugger-windows", - "yasna", -] - [[package]] name = "tungstenite" version = "0.21.0" @@ -7100,11 +6735,11 @@ dependencies = [ [[package]] name = "type-map" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" +checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90" dependencies = [ - "rustc-hash 1.1.0", + "rustc-hash 2.1.1", ] [[package]] @@ -7137,7 +6772,7 @@ dependencies = [ "serde", "thiserror 1.0.69", "tracing", - "yoke", + "yoke 0.7.5", ] [[package]] @@ -7163,9 +6798,9 @@ checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" [[package]] name = "unic-langid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44" +checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05" dependencies = [ "unic-langid-impl", "unic-langid-macros", @@ -7173,18 +6808,18 @@ dependencies = [ [[package]] name = "unic-langid-impl" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5" +checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658" dependencies = [ "tinystr", ] [[package]] name = "unic-langid-macros" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da1cd2c042d3c7569a1008806b02039e7a4a2bdf8f8e96bd3c792434a0e275e" +checksum = "d5957eb82e346d7add14182a3315a7e298f04e1ba4baac36f7f0dbfedba5fc25" dependencies = [ "proc-macro-hack", "tinystr", @@ -7194,13 +6829,13 @@ dependencies = [ [[package]] name = "unic-langid-macros-impl" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ed7f4237ba393424195053097c1516bd4590dc82b84f2f97c5c69e12704555b" +checksum = "a1249a628de3ad34b821ecb1001355bca3940bcb2f88558f1a8bd82e977f75b5" dependencies = [ "proc-macro-hack", "quote", - "syn 2.0.101", + "syn 2.0.103", "unic-langid-impl", ] @@ -7263,9 +6898,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" [[package]] name = "unicode-xid" @@ -7279,12 +6914,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "untrusted" version = "0.9.0" @@ -7315,12 +6944,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -7335,9 +6958,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" [[package]] name = "valuable" @@ -7353,7 +6976,7 @@ checksum = "41b6d82be61465f97d42bd1d15bf20f3b0a3a0905018f38f9d6f6962055b0b5c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -7368,6 +6991,26 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -7417,9 +7060,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -7436,7 +7079,7 @@ version = "0.12.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1fbb4ef9bbca0c1170e0b00dd28abc9e3b68669821600cad1caaed606583c6d" dependencies = [ - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] @@ -7461,7 +7104,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "wasm-bindgen-shared", ] @@ -7496,7 +7139,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -7545,9 +7188,9 @@ dependencies = [ [[package]] name = "web_atoms" -version = "0.1.0" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "954c5a41f2bcb7314344079d0891505458cc2f4b422bdea1d5bfbe6d1a04903b" +checksum = "57ffde1dc01240bdf9992e3205668b235e59421fd085e8a317ed98da0178d414" dependencies = [ "phf 0.11.3", "phf_codegen 0.11.3", @@ -7557,30 +7200,24 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.4" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" - -[[package]] -name = "webpki-roots" -version = "0.26.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" +checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" dependencies = [ "rustls-pki-types", ] [[package]] name = "wgpu" -version = "25.0.0" +version = "25.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6049eb2014a0e0d8689f9b787605dd71d5bbfdc74095ead499f3cff705c229" +checksum = "ec8fb398f119472be4d80bc3647339f56eb63b2a331f6a3d16e25d8144197dd9" dependencies = [ "arrayvec", - "bitflags 2.9.0", + "bitflags 2.9.1", "cfg_aliases", "document-features", - "hashbrown 0.15.2", + "hashbrown 0.15.4", "js-sys", "log", "naga", @@ -7600,17 +7237,17 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "25.0.1" +version = "25.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19813e647da7aa3cdaa84f5846e2c64114970ea7c86b1e6aae8be08091f4bdc" +checksum = "f7b882196f8368511d613c6aeec80655160db6646aebddf8328879a88d54e500" dependencies = [ "arrayvec", "bit-set", "bit-vec", - "bitflags 2.9.0", + "bitflags 2.9.1", "cfg_aliases", "document-features", - "hashbrown 0.15.2", + "hashbrown 0.15.4", "indexmap", "log", "naga", @@ -7658,15 +7295,15 @@ dependencies = [ [[package]] name = "wgpu-hal" -version = "25.0.1" +version = "25.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb7c4a1dc42ff14c23c9b11ebf1ee85cde661a9b1cf0392f79c1faca5bc559fb" +checksum = "f968767fe4d3d33747bbd1473ccd55bf0f6451f55d733b5597e67b5deab4ad17" dependencies = [ "android_system_properties", "arrayvec", "ash", "bit-set", - "bitflags 2.9.0", + "bitflags 2.9.1", "block", "bytemuck", "cfg-if", @@ -7677,7 +7314,7 @@ dependencies = [ "gpu-alloc", "gpu-allocator", "gpu-descriptor", - "hashbrown 0.15.2", + "hashbrown 0.15.4", "js-sys", "khronos-egl", "libc", @@ -7709,7 +7346,7 @@ version = "25.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aa49460c2a8ee8edba3fca54325540d904dd85b2e086ada762767e17d06e8bc" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "bytemuck", "js-sys", "log", @@ -7719,15 +7356,13 @@ dependencies = [ [[package]] name = "which" -version = "5.0.0" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14" +checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d" dependencies = [ - "either", - "home", - "once_cell", - "rustix 0.38.44", - "windows-sys 0.48.0", + "env_home", + "rustix", + "winsafe", ] [[package]] @@ -7771,16 +7406,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" -dependencies = [ - "windows-core 0.57.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.58.0" @@ -7791,6 +7416,28 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + [[package]] name = "windows-core" version = "0.56.0" @@ -7803,18 +7450,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-core" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" -dependencies = [ - "windows-implement 0.57.0", - "windows-interface 0.57.0", - "windows-result 0.1.2", - "windows-targets 0.52.6", -] - [[package]] name = "windows-core" version = "0.58.0" @@ -7830,15 +7465,26 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.61.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement 0.60.0", "windows-interface 0.59.1", "windows-link", - "windows-result 0.3.2", - "windows-strings 0.4.0", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link", + "windows-threading", ] [[package]] @@ -7849,18 +7495,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", -] - -[[package]] -name = "windows-implement" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -7871,7 +7506,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -7882,7 +7517,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -7893,18 +7528,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", -] - -[[package]] -name = "windows-interface" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -7915,7 +7539,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -7926,24 +7550,23 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] -name = "windows-registry" -version = "0.4.0" +name = "windows-numerics" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-result 0.3.2", - "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-core 0.61.2", + "windows-link", ] [[package]] @@ -7966,9 +7589,9 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ "windows-link", ] @@ -7985,18 +7608,9 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ "windows-link", ] @@ -8028,6 +7642,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -8061,9 +7684,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" dependencies = [ "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", @@ -8075,6 +7698,15 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -8215,23 +7847,13 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.7" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a" -dependencies = [ - "serde", - "winapi", -] - [[package]] name = "winreg" version = "0.50.0" @@ -8242,6 +7864,22 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + [[package]] name = "wiremock" version = "0.6.3" @@ -8272,7 +7910,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -8284,20 +7922,14 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "xattr" @@ -8306,7 +7938,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" dependencies = [ "libc", - "rustix 1.0.5", + "rustix", ] [[package]] @@ -8335,15 +7967,6 @@ dependencies = [ "lzma-sys", ] -[[package]] -name = "yasna" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" -dependencies = [ - "time", -] - [[package]] name = "yoke" version = "0.7.5" @@ -8352,7 +7975,19 @@ checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", - "yoke-derive", + "yoke-derive 0.7.5", + "zerofrom", +] + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive 0.8.0", "zerofrom", ] @@ -8364,17 +7999,20 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "synstructure", ] [[package]] -name = "zerocopy" -version = "0.7.35" +name = "yoke-derive" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ - "zerocopy-derive 0.7.35", + "proc-macro2", + "quote", + "syn 2.0.103", + "synstructure", ] [[package]] @@ -8383,18 +8021,7 @@ version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "zerocopy-derive 0.8.25", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", + "zerocopy-derive", ] [[package]] @@ -8405,7 +8032,7 @@ checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -8425,7 +8052,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "synstructure", ] @@ -8436,38 +8063,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] -name = "zerovec" -version = "0.10.4" +name = "zerotrie" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" dependencies = [ - "yoke", + "displaydoc", + "yoke 0.8.0", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke 0.8.0", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", -] - -[[package]] -name = "zip" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" -dependencies = [ - "byteorder", - "crc32fast", - "crossbeam-utils", - "flate2", - "time", + "syn 2.0.103", ] [[package]] @@ -8485,6 +8110,39 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "zip" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7dcdb4229c0e79c2531a24de7726a0e980417a74fb4d030a35f535665439a0" +dependencies = [ + "arbitrary", + "crc32fast", + "flate2", + "indexmap", + "memchr", + "time", + "zopfli", +] + +[[package]] +name = "zlib-rs" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a" + +[[package]] +name = "zopfli" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] + [[package]] name = "zstd" version = "0.13.3" diff --git a/Cargo.toml b/Cargo.toml index db0b9f79f..980956b05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,7 @@ members = [ "build/runner", "ftl", "pylib/rsbridge", - "qt/bundle/mac", - "qt/bundle/win", + "qt/launcher", "rslib", "rslib/i18n", "rslib/io", @@ -23,7 +22,6 @@ members = [ "rslib/sync", "tools/minilints", ] -exclude = ["qt/bundle"] resolver = "2" [workspace.dependencies.percent-encoding-iri] @@ -35,7 +33,7 @@ git = "https://github.com/ankitects/linkcheck.git" rev = "184b2ca50ed39ca43da13f0b830a463861adb9ca" [workspace.dependencies.fsrs] -version = "4.0.0" +version = "4.1.1" # git = "https://github.com/open-spaced-repetition/fsrs-rs.git" # rev = "a7f7efc10f0a26b14ee348cc7402155685f2a24f" # path = "../open-spaced-repetition/fsrs-rs" @@ -54,99 +52,98 @@ ninja_gen = { "path" = "build/ninja_gen" } unicase = "=2.6.0" # any changes could invalidate sqlite indexes # normal -ammonia = "4.0.0" -anyhow = "1.0.90" -apple-bundles = "0.17.0" -async-compression = { version = "0.4.17", features = ["zstd", "tokio"] } +ammonia = "4.1.0" +anyhow = "1.0.98" +async-compression = { version = "0.4.24", features = ["zstd", "tokio"] } async-stream = "0.3.6" -async-trait = "0.1.83" -axum = { version = "0.7", features = ["multipart", "macros"] } -axum-client-ip = "0.6" -axum-extra = { version = "0.9.4", features = ["typed-header"] } -blake3 = "1.5.4" -bytes = "1.7.2" -camino = "1.1.9" -chrono = { version = "0.4.38", default-features = false, features = ["std", "clock"] } -clap = { version = "4.5.20", features = ["derive"] } -coarsetime = "0.1.34" -convert_case = "0.6.0" -criterion = { version = "0.5.1" } -csv = "1.3.0" -data-encoding = "2.6.0" +async-trait = "0.1.88" +axum = { version = "0.8.4", features = ["multipart", "macros"] } +axum-client-ip = "1.1.3" +axum-extra = { version = "0.10.1", features = ["typed-header"] } +blake3 = "1.8.2" +bytes = "1.10.1" +camino = "1.1.10" +chrono = { version = "0.4.41", default-features = false, features = ["std", "clock"] } +clap = { version = "4.5.40", features = ["derive"] } +coarsetime = "0.1.36" +convert_case = "0.8.0" +criterion = { version = "0.6.0" } +csv = "1.3.1" +data-encoding = "2.9.0" difflib = "0.4.0" -dirs = "5.0.1" +dirs = "6.0.0" dunce = "1.0.5" +embed-resource = "3.0.4" envy = "0.4.2" -flate2 = "1.0.34" -fluent = "0.16.1" -fluent-bundle = "0.15.3" -fluent-syntax = "0.11.1" +flate2 = "1.1.2" +fluent = "0.17.0" +fluent-bundle = "0.16.0" +fluent-syntax = "0.12.0" fnv = "1.0.7" futures = "0.3.31" -glob = "0.3.1" -globset = "0.4.15" +globset = "0.4.16" hex = "0.4.3" htmlescape = "0.3.1" hyper = "1" id_tree = "1.8.0" inflections = "1.1.1" -intl-memoizer = "0.5.2" -itertools = "0.13.0" +intl-memoizer = "0.5.3" +itertools = "0.14.0" junction = "1.2.0" -lazy_static = "1.5.0" +libc = "0.2" +libc-stdhandle = "0.1" maplit = "1.0.2" nom = "7.1.3" num-format = "0.4.4" -num_cpus = "1.16.0" +num_cpus = "1.17.0" num_enum = "0.7.3" -once_cell = "1.20.2" +once_cell = "1.21.3" pbkdf2 = { version = "0.12", features = ["simple"] } -phf = { version = "0.11.2", features = ["macros"] } -pin-project = "1.1.6" -plist = "1.7.0" -prettyplease = "0.2.24" +phf = { version = "0.11.3", features = ["macros"] } +pin-project = "1.1.10" +prettyplease = "0.2.34" prost = "0.13" prost-build = "0.13" -prost-reflect = "0.14" +prost-reflect = "0.14.7" prost-types = "0.13" -pulldown-cmark = "0.9.6" -pyo3 = { version = "0.24", features = ["extension-module", "abi3", "abi3-py39"] } -rand = "0.8.5" -regex = "1.11.0" -reqwest = { version = "0.12.8", default-features = false, features = ["json", "socks", "stream", "multipart"] } -rusqlite = { version = "0.30.0", features = ["trace", "functions", "collation", "bundled"] } +pulldown-cmark = "0.13.0" +pyo3 = { version = "0.25.1", features = ["extension-module", "abi3", "abi3-py39"] } +rand = "0.9.1" +regex = "1.11.1" +reqwest = { version = "0.12.20", default-features = false, features = ["json", "socks", "stream", "multipart"] } +rusqlite = { version = "0.36.0", features = ["trace", "functions", "collation", "bundled"] } rustls-pemfile = "2.2.0" scopeguard = "1.2.0" -serde = { version = "1.0.210", features = ["derive"] } -serde-aux = "4.5.0" -serde_json = "1.0.132" -serde_repr = "0.1.19" -serde_tuple = "0.5.0" +serde = { version = "1.0.219", features = ["derive"] } +serde-aux = "4.7.0" +serde_json = "1.0.140" +serde_repr = "0.1.20" +serde_tuple = "1.1.0" sha1 = "0.10.6" -sha2 = { version = "0.10.8" } -simple-file-manifest = "0.11.0" +sha2 = { version = "0.10.9" } snafu = { version = "0.8.6", features = ["rust_1_61"] } -strum = { version = "0.26.3", features = ["derive"] } -syn = { version = "2.0.82", features = ["parsing", "printing"] } -tar = "0.4.42" -tempfile = "3.13.0" +strum = { version = "0.27.1", features = ["derive"] } +syn = { version = "2.0.103", features = ["parsing", "printing"] } +tar = "0.4.44" +tempfile = "3.20.0" termcolor = "1.4.1" -tokio = { version = "1.40", features = ["fs", "rt-multi-thread", "macros", "signal"] } -tokio-util = { version = "0.7.12", features = ["io"] } -tower-http = { version = "0.5", features = ["trace"] } -tracing = { version = "0.1.40", features = ["max_level_trace", "release_max_level_debug"] } +tokio = { version = "1.45", features = ["fs", "rt-multi-thread", "macros", "signal"] } +tokio-util = { version = "0.7.15", features = ["io"] } +tower-http = { version = "0.6.6", features = ["trace"] } +tracing = { version = "0.1.41", features = ["max_level_trace", "release_max_level_debug"] } tracing-appender = "0.2.3" -tracing-subscriber = { version = "0.3.18", features = ["fmt", "env-filter"] } -tugger-windows-codesign = "0.10.0" -unic-langid = { version = "0.9.5", features = ["macros"] } +tracing-subscriber = { version = "0.3.19", features = ["fmt", "env-filter"] } +unic-langid = { version = "0.9.6", features = ["macros"] } unic-ucd-category = "0.9.0" unicode-normalization = "0.1.24" walkdir = "2.5.0" -which = "5.0.0" -wiremock = "0.6.2" +which = "8.0.0" +winapi = { version = "0.3", features = ["wincon"] } +windows = { version = "0.61.3", features = ["Media_SpeechSynthesis", "Media_Core", "Foundation_Collections", "Storage_Streams"] } +wiremock = "0.6.3" xz2 = "0.1.7" -zip = { version = "0.6.6", default-features = false, features = ["deflate", "time"] } -zstd = { version = "0.13.2", features = ["zstdmt"] } +zip = { version = "4.1.0", default-features = false, features = ["deflate", "time"] } +zstd = { version = "0.13.3", features = ["zstdmt"] } # Apply mild optimizations to our dependencies in dev mode, which among other things # improves sha2 performance by about 21x. Opt 1 chosen due to diff --git a/build/configure/src/aqt.rs b/build/configure/src/aqt.rs index 5b2b8ec49..60316fcd2 100644 --- a/build/configure/src/aqt.rs +++ b/build/configure/src/aqt.rs @@ -364,20 +364,14 @@ fn build_wheel(build: &mut Build) -> Result<()> { BuildWheel { name: "aqt", version: anki_version(), - src_folder: "qt/aqt", - gen_folder: "$builddir/qt/_aqt", platform: None, - deps: inputs![":qt:aqt", glob!("qt/aqt/**"), "python/requirements.aqt.in"], + deps: inputs![":qt:aqt", glob!("qt/aqt/**"), "qt/pyproject.toml"], }, ) } fn check_python(build: &mut Build) -> Result<()> { - python_format( - build, - "qt", - inputs![glob!("qt/**/*.py", "qt/bundle/PyOxidizer/**")], - )?; + python_format(build, "qt", inputs![glob!("qt/**/*.py")])?; build.add_action( "check:pytest:aqt", diff --git a/build/configure/src/bundle.rs b/build/configure/src/bundle.rs deleted file mode 100644 index 50fe7414c..000000000 --- a/build/configure/src/bundle.rs +++ /dev/null @@ -1,442 +0,0 @@ -// Copyright: Ankitects Pty Ltd and contributors -// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -use std::env; - -use anyhow::Result; -use ninja_gen::action::BuildAction; -use ninja_gen::archives::download_and_extract; -use ninja_gen::archives::empty_manifest; -use ninja_gen::archives::with_exe; -use ninja_gen::archives::OnlineArchive; -use ninja_gen::archives::Platform; -use ninja_gen::build::BuildProfile; -use ninja_gen::cargo::CargoBuild; -use ninja_gen::cargo::RustOutput; -use ninja_gen::git::SyncSubmodule; -use ninja_gen::glob; -use ninja_gen::input::BuildInput; -use ninja_gen::inputs; -use ninja_gen::python::PythonEnvironment; -use ninja_gen::Build; -use ninja_gen::Utf8Path; - -use crate::anki_version; -use crate::platform::overriden_python_target_platform; -use crate::platform::overriden_rust_target_triple; - -#[derive(Debug, PartialEq, Eq)] -enum DistKind { - Standard, -} - -impl DistKind { - fn folder_name(&self) -> &'static str { - match self { - DistKind::Standard => "std", - } - } - - fn name(&self) -> &'static str { - match self { - DistKind::Standard => "standard", - } - } -} - -pub fn build_bundle(build: &mut Build) -> Result<()> { - // install into venv - setup_primary_venv(build)?; - install_anki_wheels(build)?; - - // bundle venv into output binary + extra_files - build_pyoxidizer(build)?; - build_artifacts(build)?; - build_binary(build)?; - - // package up outputs with Qt/other deps - download_dist_folder_deps(build)?; - build_dist_folder(build, DistKind::Standard)?; - - build_packages(build)?; - - Ok(()) -} - -fn targetting_macos_arm() -> bool { - cfg!(all(target_os = "macos", target_arch = "aarch64")) - && overriden_python_target_platform().is_none() -} - -const WIN_AUDIO: OnlineArchive = OnlineArchive { - url: "https://github.com/ankitects/anki-bundle-extras/releases/download/anki-2022-02-09/audio-win-amd64.tar.gz", - sha256: "0815a601baba05e03bc36b568cdc2332b1cf4aa17125fc33c69de125f8dd687f", -}; - -const MAC_ARM_AUDIO: OnlineArchive = OnlineArchive { - url: "https://github.com/ankitects/anki-bundle-extras/releases/download/anki-2022-05-26/audio-mac-arm64.tar.gz", - sha256: "f6c4af9be59ae1c82a16f5c6307f13cbf31b49ad7b69ce1cb6e0e7b403cfdb8f", -}; - -const MAC_AMD_AUDIO: OnlineArchive = OnlineArchive { - url: "https://github.com/ankitects/anki-bundle-extras/releases/download/anki-2022-05-26/audio-mac-amd64.tar.gz", - sha256: "ecbb3c878805cdd58b1a0b8e3fd8c753b8ce3ad36c8b5904a79111f9db29ff42", -}; - -const MAC_ARM_QT6: OnlineArchive = OnlineArchive { - url: "https://github.com/ankitects/anki-bundle-extras/releases/download/anki-2024-02-29/pyqt6.6-mac-arm64.tar.zst", - sha256: "9b2ade4ae9b80506689062845e83e8c60f7fa9843545bf7bb2d11d3e2f105878", -}; - -const MAC_AMD_QT6: OnlineArchive = OnlineArchive { - url: "https://github.com/ankitects/anki-bundle-extras/releases/download/anki-2024-02-29/pyqt6.6-mac-amd64.tar.zst", - sha256: "dbd0871e4da22820d1fa9ab29220d631467d1178038dcab4b15169ad7f499b1b", -}; - -const LINUX_QT_PLUGINS: OnlineArchive = OnlineArchive { - url: "https://github.com/ankitects/anki-bundle-extras/releases/download/anki-2023-05-02/qt-plugins-linux-amd64.tar.gz", - sha256: "66bb568aca7242bc55ad419bf5c96755ca15d2a743e1c3a09cba8b83230b138b", -}; - -const NSIS_PLUGINS: OnlineArchive = OnlineArchive { - url: "https://github.com/ankitects/anki-bundle-extras/releases/download/anki-2023-05-19/nsis.tar.zst", - sha256: "6133f730ece699de19714d0479c73bc848647d277e9cc80dda9b9ebe532b40a8", -}; - -fn download_dist_folder_deps(build: &mut Build) -> Result<()> { - let mut bundle_deps = vec![":wheels"]; - if cfg!(windows) { - download_and_extract(build, "win_amd64_audio", WIN_AUDIO, empty_manifest())?; - download_and_extract(build, "nsis_plugins", NSIS_PLUGINS, empty_manifest())?; - bundle_deps.extend([":extract:win_amd64_audio", ":extract:nsis_plugins"]); - } else if cfg!(target_os = "macos") { - if targetting_macos_arm() { - download_and_extract(build, "mac_arm_audio", MAC_ARM_AUDIO, empty_manifest())?; - download_and_extract(build, "mac_arm_qt6", MAC_ARM_QT6, empty_manifest())?; - bundle_deps.extend([":extract:mac_arm_audio", ":extract:mac_arm_qt6"]); - } else { - download_and_extract(build, "mac_amd_audio", MAC_AMD_AUDIO, empty_manifest())?; - download_and_extract(build, "mac_amd_qt6", MAC_AMD_QT6, empty_manifest())?; - bundle_deps.extend([":extract:mac_amd_audio", ":extract:mac_amd_qt6"]); - } - } else { - download_and_extract( - build, - "linux_qt_plugins", - LINUX_QT_PLUGINS, - empty_manifest(), - )?; - bundle_deps.extend([":extract:linux_qt_plugins"]); - } - build.add_dependency( - "bundle:deps", - inputs![bundle_deps - .iter() - .map(ToString::to_string) - .collect::>()], - ); - Ok(()) -} - -struct Venv { - label: &'static str, - path_without_builddir: &'static str, -} - -impl Venv { - fn label_as_target(&self, suffix: &str) -> String { - format!(":{}{suffix}", self.label) - } -} - -const PRIMARY_VENV: Venv = Venv { - label: "bundle:pyenv", - path_without_builddir: "bundle/pyenv", -}; - -fn setup_primary_venv(build: &mut Build) -> Result<()> { - let mut qt6_reqs = inputs![ - "python/requirements.bundle.txt", - "python/requirements.qt6_6.txt", - ]; - if cfg!(windows) { - qt6_reqs = inputs![qt6_reqs, "python/requirements.win.txt"]; - } - build.add_action( - PRIMARY_VENV.label, - PythonEnvironment { - folder: PRIMARY_VENV.path_without_builddir, - base_requirements_txt: "python/requirements.base.txt".into(), - requirements_txt: qt6_reqs, - extra_binary_exports: &[], - }, - )?; - Ok(()) -} - -struct InstallAnkiWheels { - venv: Venv, -} - -impl BuildAction for InstallAnkiWheels { - fn command(&self) -> &str { - "$pip install --force-reinstall --no-deps $in" - } - - fn files(&mut self, build: &mut impl ninja_gen::build::FilesHandle) { - build.add_inputs("pip", inputs![self.venv.label_as_target(":pip")]); - build.add_inputs("in", inputs![":wheels"]); - build.add_output_stamp("bundle/wheels.stamp"); - } -} - -fn install_anki_wheels(build: &mut Build) -> Result<()> { - build.add_action( - "bundle:add_wheels:qt6", - InstallAnkiWheels { venv: PRIMARY_VENV }, - )?; - Ok(()) -} - -fn build_pyoxidizer(build: &mut Build) -> Result<()> { - let offline_build = env::var("OFFLINE_BUILD").is_ok(); - - build.add_action( - "bundle:pyoxidizer:repo", - SyncSubmodule { - path: "qt/bundle/PyOxidizer", - offline_build, - }, - )?; - let target = - overriden_rust_target_triple().unwrap_or_else(|| Platform::current().as_rust_triple()); - let output_bin = format!("bundle/rust/{target}/release/pyoxidizer",); - build.add_action( - "bundle:pyoxidizer:bin", - CargoBuild { - inputs: inputs![ - ":bundle:pyoxidizer:repo", - "out/env", - glob!["qt/bundle/PyOxidizer/**"] - ], - // can't use ::Binary() here, as we're in a separate workspace - outputs: &[RustOutput::Data("bin", &with_exe(&output_bin))], - target: Some(target), - extra_args: &format!( - "--manifest-path={} --target-dir={} -p pyoxidizer", - "qt/bundle/PyOxidizer/Cargo.toml", "$builddir/bundle/rust" - ), - release_override: Some(BuildProfile::Release), - }, - )?; - Ok(()) -} - -struct BuildArtifacts {} - -impl BuildAction for BuildArtifacts { - fn command(&self) -> &str { - "$runner build-artifacts $bundle_root $pyoxidizer_bin" - } - - fn files(&mut self, build: &mut impl ninja_gen::build::FilesHandle) { - build.add_inputs("pyoxidizer_bin", inputs![":bundle:pyoxidizer:bin"]); - build.add_inputs("", inputs![PRIMARY_VENV.label_as_target("")]); - build.add_inputs("", inputs![":bundle:add_wheels:qt6", glob!["qt/bundle/**"]]); - build.add_variable("bundle_root", "$builddir/bundle"); - build.add_outputs_ext( - "pyo3_config", - vec!["bundle/artifacts/pyo3-build-config-file.txt"], - true, - ); - } - - fn check_output_timestamps(&self) -> bool { - true - } -} - -fn build_artifacts(build: &mut Build) -> Result<()> { - build.add_action("bundle:artifacts", BuildArtifacts {}) -} - -struct BuildBundle {} - -impl BuildAction for BuildBundle { - fn command(&self) -> &str { - "$runner build-bundle-binary" - } - - fn files(&mut self, build: &mut impl ninja_gen::build::FilesHandle) { - build.add_inputs("", inputs![":bundle:artifacts", glob!["qt/bundle/**"]]); - build.add_outputs( - "", - vec![RustOutput::Binary("anki").path( - Utf8Path::new("$builddir/bundle/rust"), - Some( - overriden_rust_target_triple() - .unwrap_or_else(|| Platform::current().as_rust_triple()), - ), - // our pyoxidizer bin uses lto on the release profile - BuildProfile::Release, - )], - ); - } -} - -fn build_binary(build: &mut Build) -> Result<()> { - build.add_action("bundle:binary", BuildBundle {}) -} - -struct BuildDistFolder { - kind: DistKind, - deps: BuildInput, -} - -impl BuildAction for BuildDistFolder { - fn command(&self) -> &str { - "$runner build-dist-folder $kind $out_folder " - } - - fn files(&mut self, build: &mut impl ninja_gen::build::FilesHandle) { - build.add_inputs("", &self.deps); - build.add_variable("kind", self.kind.name()); - let folder = match self.kind { - DistKind::Standard => "bundle/std", - }; - build.add_outputs("out_folder", vec![folder]); - build.add_outputs("stamp", vec![format!("{folder}.stamp")]); - } - - fn check_output_timestamps(&self) -> bool { - true - } -} - -fn build_dist_folder(build: &mut Build, kind: DistKind) -> Result<()> { - let deps = inputs![":bundle:deps", ":bundle:binary", glob!["qt/bundle/**"]]; - let group = match kind { - DistKind::Standard => "bundle:folder:std", - }; - build.add_action(group, BuildDistFolder { kind, deps }) -} - -fn build_packages(build: &mut Build) -> Result<()> { - if cfg!(windows) { - build_windows_installers(build) - } else if cfg!(target_os = "macos") { - build_mac_app(build, DistKind::Standard)?; - build_dmgs(build) - } else { - build_tarball(build, DistKind::Standard) - } -} - -struct BuildTarball { - kind: DistKind, -} - -impl BuildAction for BuildTarball { - fn command(&self) -> &str { - "chmod -R a+r $folder && tar -I '$zstd' --transform $transform -cf $tarball -C $folder ." - } - - fn files(&mut self, build: &mut impl ninja_gen::build::FilesHandle) { - let input_folder_name = self.kind.folder_name(); - let input_folder_target = format!(":bundle:folder:{input_folder_name}"); - let input_folder_path = format!("$builddir/bundle/{input_folder_name}"); - - let version = anki_version(); - let qt = match self.kind { - DistKind::Standard => "qt6", - }; - let output_folder_base = format!("anki-{version}-linux-{qt}"); - let output_tarball = format!("bundle/package/{output_folder_base}.tar.zst"); - - build.add_inputs("", inputs![input_folder_target]); - build.add_variable("zstd", "zstd -c --long -T0 -18"); - build.add_variable("transform", format!("s%^.%{output_folder_base}%S")); - build.add_variable("folder", input_folder_path); - build.add_outputs("tarball", vec![output_tarball]); - } -} - -fn build_tarball(build: &mut Build, kind: DistKind) -> Result<()> { - let name = kind.folder_name(); - build.add_action(format!("bundle:package:{name}"), BuildTarball { kind }) -} - -struct BuildWindowsInstallers {} - -impl BuildAction for BuildWindowsInstallers { - fn command(&self) -> &str { - "cargo run -p makeexe --target-dir=out/rust -- $version $src_root $bundle_root $out" - } - - fn files(&mut self, build: &mut impl ninja_gen::build::FilesHandle) { - let version = anki_version(); - let outputs = ["qt6"].iter().map(|qt| { - let output_base = format!("anki-{version}-windows-{qt}"); - format!("bundle/package/{output_base}.exe") - }); - - build.add_inputs("", inputs![":bundle:folder:std"]); - build.add_variable("version", &version); - build.add_variable("bundle_root", "$builddir/bundle"); - build.add_outputs("out", outputs); - } -} - -fn build_windows_installers(build: &mut Build) -> Result<()> { - build.add_action("bundle:package", BuildWindowsInstallers {}) -} - -struct BuildMacApp { - kind: DistKind, -} - -impl BuildAction for BuildMacApp { - fn command(&self) -> &str { - "cargo run -p makeapp --target-dir=out/rust -- build-app $version $kind $stamp" - } - - fn files(&mut self, build: &mut impl ninja_gen::build::FilesHandle) { - let folder_name = self.kind.folder_name(); - build.add_inputs("", inputs![format!(":bundle:folder:{folder_name}")]); - build.add_variable("version", anki_version()); - build.add_variable("kind", self.kind.name()); - build.add_outputs("stamp", vec![format!("bundle/app/{folder_name}.stamp")]); - } -} - -fn build_mac_app(build: &mut Build, kind: DistKind) -> Result<()> { - build.add_action(format!("bundle:app:{}", kind.name()), BuildMacApp { kind }) -} - -struct BuildDmgs {} - -impl BuildAction for BuildDmgs { - fn command(&self) -> &str { - "cargo run -p makeapp --target-dir=out/rust -- build-dmgs $dmgs" - } - - fn files(&mut self, build: &mut impl ninja_gen::build::FilesHandle) { - let version = anki_version(); - let platform = if targetting_macos_arm() { - "apple" - } else { - "intel" - }; - let qt = &["qt6"][..]; - let dmgs = qt - .iter() - .map(|qt| format!("bundle/dmg/anki-{version}-mac-{platform}-{qt}.dmg")); - - build.add_inputs("", inputs![":bundle:app"]); - build.add_outputs("dmgs", dmgs); - } -} - -fn build_dmgs(build: &mut Build) -> Result<()> { - build.add_action("bundle:dmg", BuildDmgs {}) -} diff --git a/build/configure/src/launcher.rs b/build/configure/src/launcher.rs new file mode 100644 index 000000000..4a1927289 --- /dev/null +++ b/build/configure/src/launcher.rs @@ -0,0 +1,44 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +use anyhow::Result; +use ninja_gen::archives::download_and_extract; +use ninja_gen::archives::empty_manifest; +use ninja_gen::archives::OnlineArchive; +use ninja_gen::command::RunCommand; +use ninja_gen::hashmap; +use ninja_gen::inputs; +use ninja_gen::Build; + +pub fn setup_uv_universal(build: &mut Build) -> Result<()> { + if !cfg!(target_arch = "aarch64") { + return Ok(()); + } + + build.add_action( + "launcher:uv_universal", + RunCommand { + command: "/usr/bin/lipo", + args: "-create -output $out $arm_bin $x86_bin", + inputs: hashmap! { + "arm_bin" => inputs![":extract:uv:bin"], + "x86_bin" => inputs![":extract:uv_mac_x86:bin"], + }, + outputs: hashmap! { + "out" => vec!["launcher/uv"], + }, + }, + ) +} + +pub fn build_launcher(build: &mut Build) -> Result<()> { + setup_uv_universal(build)?; + download_and_extract(build, "nsis_plugins", NSIS_PLUGINS, empty_manifest())?; + + Ok(()) +} + +const NSIS_PLUGINS: OnlineArchive = OnlineArchive { + url: "https://github.com/ankitects/anki-bundle-extras/releases/download/anki-2023-05-19/nsis.tar.zst", + sha256: "6133f730ece699de19714d0479c73bc848647d277e9cc80dda9b9ebe532b40a8", +}; diff --git a/build/configure/src/main.rs b/build/configure/src/main.rs index f88a32155..afd1cfb4a 100644 --- a/build/configure/src/main.rs +++ b/build/configure/src/main.rs @@ -2,7 +2,7 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html mod aqt; -mod bundle; +mod launcher; mod platform; mod pylib; mod python; @@ -13,13 +13,14 @@ use std::env; use anyhow::Result; use aqt::build_and_check_aqt; -use bundle::build_bundle; +use launcher::build_launcher; use ninja_gen::glob; use ninja_gen::inputs; use ninja_gen::protobuf::check_proto; use ninja_gen::protobuf::setup_protoc; -use ninja_gen::python::setup_python; +use ninja_gen::python::setup_uv; use ninja_gen::Build; +use platform::overriden_python_venv_platform; use pylib::build_pylib; use pylib::check_pylib; use python::check_python; @@ -47,7 +48,10 @@ fn main() -> Result<()> { check_proto(build, inputs![glob!["proto/**/*.proto"]])?; if env::var("OFFLINE_BUILD").is_err() { - setup_python(build)?; + setup_uv( + build, + overriden_python_venv_platform().unwrap_or(build.host_platform), + )?; } setup_venv(build)?; @@ -57,7 +61,7 @@ fn main() -> Result<()> { build_and_check_aqt(build)?; if env::var("OFFLINE_BUILD").is_err() { - build_bundle(build)?; + build_launcher(build)?; } setup_sphinx(build)?; diff --git a/build/configure/src/platform.rs b/build/configure/src/platform.rs index 4aec36e65..ce8a7a5ba 100644 --- a/build/configure/src/platform.rs +++ b/build/configure/src/platform.rs @@ -5,18 +5,30 @@ use std::env; use ninja_gen::archives::Platform; -/// Usually None to use the host architecture; can be overriden by setting -/// MAC_X86 to build for x86_64 on Apple Silicon +/// Please see [`overriden_python_target_platform()`] for details. pub fn overriden_rust_target_triple() -> Option<&'static str> { - overriden_python_target_platform().map(|p| p.as_rust_triple()) + overriden_python_wheel_platform().map(|p| p.as_rust_triple()) } -/// Usually None to use the host architecture; can be overriden by setting -/// MAC_X86 to build for x86_64 on Apple Silicon -pub fn overriden_python_target_platform() -> Option { - if env::var("MAC_X86").is_ok() { - Some(Platform::MacX64) +/// Usually None to use the host architecture, except on Windows which +/// always uses x86_64, since WebEngine is unavailable for ARM64. +pub fn overriden_python_venv_platform() -> Option { + if cfg!(target_os = "windows") { + Some(Platform::WindowsX64) } else { None } } + +/// Like [`overriden_python_venv_platform`], but: +/// If MAC_X86 is set, an X86 wheel will be built on macOS ARM. +/// If LIN_ARM64 is set, an ARM64 wheel will be built on Linux AMD64. +pub fn overriden_python_wheel_platform() -> Option { + if env::var("MAC_X86").is_ok() { + Some(Platform::MacX64) + } else if env::var("LIN_ARM64").is_ok() { + Some(Platform::LinuxArm) + } else { + overriden_python_venv_platform() + } +} diff --git a/build/configure/src/pylib.rs b/build/configure/src/pylib.rs index 7d269cbd2..bcef1ecc4 100644 --- a/build/configure/src/pylib.rs +++ b/build/configure/src/pylib.rs @@ -14,7 +14,7 @@ use ninja_gen::python::PythonTest; use ninja_gen::Build; use crate::anki_version; -use crate::platform::overriden_python_target_platform; +use crate::platform::overriden_python_wheel_platform; use crate::python::BuildWheel; use crate::python::GenPythonProto; @@ -50,7 +50,7 @@ pub fn build_pylib(build: &mut Build) -> Result<()> { output: &format!( "pylib/anki/_rsbridge.{}", match build.host_platform { - Platform::WindowsX64 => "pyd", + Platform::WindowsX64 | Platform::WindowsArm => "pyd", _ => "so", } ), @@ -64,13 +64,11 @@ pub fn build_pylib(build: &mut Build) -> Result<()> { BuildWheel { name: "anki", version: anki_version(), - src_folder: "pylib/anki", - gen_folder: "$builddir/pylib/anki", - platform: overriden_python_target_platform().or(Some(build.host_platform)), + platform: overriden_python_wheel_platform().or(Some(build.host_platform)), deps: inputs![ ":pylib:anki", glob!("pylib/anki/**"), - "python/requirements.anki.in", + "pylib/pyproject.toml" ], }, )?; diff --git a/build/configure/src/python.rs b/build/configure/src/python.rs index 17dddde16..7cd66b76e 100644 --- a/build/configure/src/python.rs +++ b/build/configure/src/python.rs @@ -20,74 +20,27 @@ use ninja_gen::python::PythonTypecheck; use ninja_gen::rsync::RsyncFiles; use ninja_gen::Build; -// When updating Qt, make sure to update the .txt file in bundle.rs as well. pub fn setup_venv(build: &mut Build) -> Result<()> { - let platform_deps = if cfg!(windows) { - inputs![ - "python/requirements.qt6_6.txt", - "python/requirements.win.txt", - ] - } else if cfg!(target_os = "macos") { - inputs!["python/requirements.qt6_6.txt",] - } else if std::env::var("PYTHONPATH").is_ok() { - // assume we have a system-provided Qt - inputs![] - } else if cfg!(target_arch = "aarch64") { - inputs!["python/requirements.qt6_8.txt"] - } else { - inputs!["python/requirements.qt6_6.txt"] - }; - let requirements_txt = inputs!["python/requirements.dev.txt", platform_deps]; + let extra_binary_exports = &[ + "mypy", + "black", + "isort", + "pylint", + "pytest", + "protoc-gen-mypy", + ]; build.add_action( "pyenv", PythonEnvironment { - folder: "pyenv", - base_requirements_txt: inputs!["python/requirements.base.txt"], - requirements_txt, - extra_binary_exports: &[ - "pip-compile", - "pip-sync", - "mypy", - "black", // Required for offline build - "isort", - "pylint", - "pytest", - "protoc-gen-mypy", // ditto + venv_folder: "pyenv", + deps: inputs![ + "pyproject.toml", + "pylib/pyproject.toml", + "qt/pyproject.toml", + "uv.lock" ], - }, - )?; - - // optional venvs for testing other Qt versions - let mut venv_reqs = inputs!["python/requirements.bundle.txt"]; - if cfg!(windows) { - venv_reqs = inputs![venv_reqs, "python/requirements.win.txt"]; - } - - build.add_action( - "pyenv-qt6.8", - PythonEnvironment { - folder: "pyenv-qt6.8", - base_requirements_txt: inputs!["python/requirements.base.txt"], - requirements_txt: inputs![&venv_reqs, "python/requirements.qt6_8.txt"], - extra_binary_exports: &[], - }, - )?; - build.add_action( - "pyenv-qt5.15", - PythonEnvironment { - folder: "pyenv-qt5.15", - base_requirements_txt: inputs!["python/requirements.base.txt"], - requirements_txt: inputs![&venv_reqs, "python/requirements.qt5_15.txt"], - extra_binary_exports: &[], - }, - )?; - build.add_action( - "pyenv-qt5.14", - PythonEnvironment { - folder: "pyenv-qt5.14", - base_requirements_txt: inputs!["python/requirements.base.txt"], - requirements_txt: inputs![venv_reqs, "python/requirements.qt5_14.txt"], - extra_binary_exports: &[], + extra_args: "--all-packages --extra qt --extra audio", + extra_binary_exports, }, )?; @@ -133,45 +86,66 @@ impl BuildAction for GenPythonProto { pub struct BuildWheel { pub name: &'static str, pub version: String, - pub src_folder: &'static str, - pub gen_folder: &'static str, pub platform: Option, pub deps: BuildInput, } impl BuildAction for BuildWheel { fn command(&self) -> &str { - "$pyenv_bin $script $src $gen $out" + "$uv build --wheel --out-dir=$out_dir --project=$project_dir" } fn files(&mut self, build: &mut impl FilesHandle) { - build.add_inputs("pyenv_bin", inputs![":pyenv:bin"]); - build.add_inputs("script", inputs!["python/write_wheel.py"]); + build.add_inputs("uv", inputs![":uv_binary"]); build.add_inputs("", &self.deps); - build.add_variable("src", self.src_folder); - build.add_variable("gen", self.gen_folder); + // Set the project directory based on which package we're building + let project_dir = if self.name == "anki" { "pylib" } else { "qt" }; + build.add_variable("project_dir", project_dir); + + // Set environment variable for uv to use our pyenv + build.add_variable("pyenv_path", "$builddir/pyenv"); + build.add_env_var("UV_PROJECT_ENVIRONMENT", "$pyenv_path"); + + // Set output directory + build.add_variable("out_dir", "$builddir/wheels/"); + + // Calculate the wheel filename that uv will generate let tag = if let Some(platform) = self.platform { - let platform = match platform { - Platform::LinuxX64 => "manylinux_2_35_x86_64", - Platform::LinuxArm => "manylinux_2_35_aarch64", + let platform_tag = match platform { + Platform::LinuxX64 => "manylinux_2_36_x86_64", + Platform::LinuxArm => "manylinux_2_36_aarch64", Platform::MacX64 => "macosx_12_0_x86_64", Platform::MacArm => "macosx_12_0_arm64", Platform::WindowsX64 => "win_amd64", + Platform::WindowsArm => "win_arm64", }; - format!("cp39-abi3-{platform}") + format!("cp39-abi3-{platform_tag}") } else { "py3-none-any".into() }; + + // Set environment variable for hatch_build.py to use the correct platform tag + build.add_variable("wheel_tag", &tag); + build.add_env_var("ANKI_WHEEL_TAG", "$wheel_tag"); + let name = self.name; - let version = &self.version; - let wheel_path = format!("wheels/{name}-{version}-{tag}.whl"); + + // Normalize version like hatchling does: remove leading zeros from version + // parts + let normalized_version = self + .version + .split('.') + .map(|part| part.parse::().unwrap_or(0).to_string()) + .collect::>() + .join("."); + + let wheel_path = format!("wheels/{name}-{normalized_version}-{tag}.whl"); build.add_outputs("out", vec![wheel_path]); } } pub fn check_python(build: &mut Build) -> Result<()> { - python_format(build, "ftl", inputs![glob!("ftl/**/*.py")])?; python_format(build, "tools", inputs![glob!("tools/**/*.py")])?; build.add_action( @@ -183,7 +157,6 @@ pub fn check_python(build: &mut Build) -> Result<()> { "qt/tools", "out/pylib/anki", "out/qt/_aqt", - "ftl", "python", "tools", ], @@ -262,8 +235,7 @@ struct Sphinx { impl BuildAction for Sphinx { fn command(&self) -> &str { if env::var("OFFLINE_BUILD").is_err() { - "$pip install sphinx sphinx_rtd_theme sphinx-autoapi \ - && $python python/sphinx/build.py" + "$uv sync --extra sphinx && $python python/sphinx/build.py" } else { "$python python/sphinx/build.py" } @@ -271,7 +243,10 @@ impl BuildAction for Sphinx { fn files(&mut self, build: &mut impl FilesHandle) { if env::var("OFFLINE_BUILD").is_err() { - build.add_inputs("pip", inputs![":pyenv:pip"]); + build.add_inputs("uv", inputs![":uv_binary"]); + // Set environment variable to use the existing pyenv + build.add_variable("pyenv_path", "$builddir/pyenv"); + build.add_env_var("UV_PROJECT_ENVIRONMENT", "$pyenv_path"); } build.add_inputs("python", inputs![":pyenv:bin"]); build.add_inputs("", &self.deps); @@ -294,7 +269,12 @@ pub(crate) fn setup_sphinx(build: &mut Build) -> Result<()> { build.add_action( "python:sphinx", Sphinx { - deps: inputs![":pylib", ":qt", ":python:sphinx:copy_conf"], + deps: inputs![ + ":pylib", + ":qt", + ":python:sphinx:copy_conf", + "pyproject.toml" + ], }, )?; Ok(()) diff --git a/build/configure/src/rust.rs b/build/configure/src/rust.rs index f5da67086..1ff0fc97c 100644 --- a/build/configure/src/rust.rs +++ b/build/configure/src/rust.rs @@ -154,7 +154,7 @@ fn build_rsbridge(build: &mut Build) -> Result<()> { "$builddir/buildhash", // building on Windows requires python3.lib if cfg!(windows) { - inputs![":extract:python"] + inputs![":pyenv:bin"] } else { inputs![] } @@ -247,7 +247,7 @@ pub fn check_minilints(build: &mut Build) -> Result<()> { let files = inputs![ glob![ "**/*.{py,rs,ts,svelte,mjs,md}", - "{node_modules,qt/bundle/PyOxidizer,ts/.svelte-kit}/**" + "{node_modules,ts/.svelte-kit}/**" ], "Cargo.lock" ]; diff --git a/build/ninja_gen/Cargo.toml b/build/ninja_gen/Cargo.toml index 7757116c6..cacab6a7b 100644 --- a/build/ninja_gen/Cargo.toml +++ b/build/ninja_gen/Cargo.toml @@ -16,5 +16,22 @@ globset.workspace = true itertools.workspace = true maplit.workspace = true num_cpus.workspace = true +regex.workspace = true +serde_json.workspace = true +sha2.workspace = true walkdir.workspace = true which.workspace = true + +[target.'cfg(windows)'.dependencies] +reqwest = { workspace = true, features = ["blocking", "json", "native-tls"] } + +[target.'cfg(not(windows))'.dependencies] +reqwest = { workspace = true, features = ["blocking", "json", "rustls-tls"] } + +[[bin]] +name = "update_uv" +path = "src/bin/update_uv.rs" + +[[bin]] +name = "update_protoc" +path = "src/bin/update_protoc.rs" diff --git a/build/ninja_gen/src/archives.rs b/build/ninja_gen/src/archives.rs index 9dd784bdd..3f87d3ff5 100644 --- a/build/ninja_gen/src/archives.rs +++ b/build/ninja_gen/src/archives.rs @@ -26,22 +26,21 @@ pub enum Platform { MacX64, MacArm, WindowsX64, + WindowsArm, } impl Platform { pub fn current() -> Self { - if cfg!(windows) { - Self::WindowsX64 - } else { - let os = std::env::consts::OS; - let arch = std::env::consts::ARCH; - match (os, arch) { - ("linux", "x86_64") => Self::LinuxX64, - ("linux", "aarch64") => Self::LinuxArm, - ("macos", "x86_64") => Self::MacX64, - ("macos", "aarch64") => Self::MacArm, - _ => panic!("unsupported os/arch {os} {arch} - PR welcome!"), - } + let os = std::env::consts::OS; + let arch = std::env::consts::ARCH; + match (os, arch) { + ("linux", "x86_64") => Self::LinuxX64, + ("linux", "aarch64") => Self::LinuxArm, + ("macos", "x86_64") => Self::MacX64, + ("macos", "aarch64") => Self::MacArm, + ("windows", "x86_64") => Self::WindowsX64, + ("windows", "aarch64") => Self::WindowsArm, + _ => panic!("unsupported os/arch {os} {arch} - PR welcome!"), } } @@ -62,6 +61,7 @@ impl Platform { Platform::MacX64 => "x86_64-apple-darwin", Platform::MacArm => "aarch64-apple-darwin", Platform::WindowsX64 => "x86_64-pc-windows-msvc", + Platform::WindowsArm => "aarch64-pc-windows-msvc", } } } diff --git a/build/ninja_gen/src/bin/update_protoc.rs b/build/ninja_gen/src/bin/update_protoc.rs new file mode 100644 index 000000000..224dbaa50 --- /dev/null +++ b/build/ninja_gen/src/bin/update_protoc.rs @@ -0,0 +1,126 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +use std::error::Error; +use std::fs; +use std::path::Path; + +use regex::Regex; +use reqwest::blocking::Client; +use serde_json::Value; +use sha2::Digest; +use sha2::Sha256; + +fn fetch_protoc_release_info() -> Result> { + let client = Client::new(); + + println!("Fetching latest protoc release info from GitHub..."); + // Fetch latest release info + let response = client + .get("https://api.github.com/repos/protocolbuffers/protobuf/releases/latest") + .header("User-Agent", "Anki-Build-Script") + .send()?; + + let release_info: Value = response.json()?; + let assets = release_info["assets"] + .as_array() + .expect("assets should be an array"); + + // Map platform names to their corresponding asset patterns + let platform_patterns = [ + ("LinuxX64", "linux-x86_64"), + ("LinuxArm", "linux-aarch_64"), + ("MacX64", "osx-universal_binary"), // Mac uses universal binary for both + ("MacArm", "osx-universal_binary"), + ("WindowsX64", "win64"), // Windows uses x86 binary for both archs + ("WindowsArm", "win64"), + ]; + + let mut match_blocks = Vec::new(); + + for (platform, pattern) in platform_patterns { + // Find the asset matching the platform pattern + let asset = assets.iter().find(|asset| { + let name = asset["name"].as_str().unwrap_or(""); + name.starts_with("protoc-") && name.contains(pattern) && name.ends_with(".zip") + }); + + if asset.is_none() { + eprintln!("No asset found for platform {platform} pattern {pattern}"); + continue; + } + + let asset = asset.unwrap(); + let download_url = asset["browser_download_url"].as_str().unwrap(); + let asset_name = asset["name"].as_str().unwrap(); + + // Download the file and calculate SHA256 locally + println!("Downloading and checksumming {asset_name} for {platform}..."); + let response = client + .get(download_url) + .header("User-Agent", "Anki-Build-Script") + .send()?; + + let bytes = response.bytes()?; + let mut hasher = Sha256::new(); + hasher.update(&bytes); + let sha256 = format!("{:x}", hasher.finalize()); + + // Handle platform-specific match patterns + let match_pattern = match platform { + "MacX64" => "Platform::MacX64 | Platform::MacArm", + "MacArm" => continue, // Skip MacArm since it's handled with MacX64 + "WindowsX64" => "Platform::WindowsX64 | Platform::WindowsArm", + "WindowsArm" => continue, // Skip WindowsArm since it's handled with WindowsX64 + _ => &format!("Platform::{}", platform), + }; + + match_blocks.push(format!( + " {} => {{\n OnlineArchive {{\n url: \"{}\",\n sha256: \"{}\",\n }}\n }}", + match_pattern, download_url, sha256 + )); + } + + Ok(format!( + "pub fn protoc_archive(platform: Platform) -> OnlineArchive {{\n match platform {{\n{}\n }}\n}}", + match_blocks.join(",\n") + )) +} + +fn read_protobuf_rs() -> Result> { + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".to_string()); + let path = Path::new(&manifest_dir).join("src/protobuf.rs"); + println!("Reading {}", path.display()); + let content = fs::read_to_string(path)?; + Ok(content) +} + +fn update_protoc_text(old_text: &str, new_protoc_text: &str) -> Result> { + let re = + Regex::new(r"(?ms)^pub fn protoc_archive\(platform: Platform\) -> OnlineArchive \{.*?\n\}") + .unwrap(); + if !re.is_match(old_text) { + return Err("Could not find protoc_archive function block to replace".into()); + } + let new_content = re.replace(old_text, new_protoc_text).to_string(); + println!("Original lines: {}", old_text.lines().count()); + println!("Updated lines: {}", new_content.lines().count()); + Ok(new_content) +} + +fn write_protobuf_rs(content: &str) -> Result<(), Box> { + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".to_string()); + let path = Path::new(&manifest_dir).join("src/protobuf.rs"); + println!("Writing to {}", path.display()); + fs::write(path, content)?; + Ok(()) +} + +fn main() -> Result<(), Box> { + let new_protoc_archive = fetch_protoc_release_info()?; + let content = read_protobuf_rs()?; + let updated_content = update_protoc_text(&content, &new_protoc_archive)?; + write_protobuf_rs(&updated_content)?; + println!("Successfully updated protoc_archive function in protobuf.rs"); + Ok(()) +} diff --git a/build/ninja_gen/src/bin/update_uv.rs b/build/ninja_gen/src/bin/update_uv.rs new file mode 100644 index 000000000..39cf87668 --- /dev/null +++ b/build/ninja_gen/src/bin/update_uv.rs @@ -0,0 +1,144 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +use std::error::Error; +use std::fs; +use std::path::Path; + +use regex::Regex; +use reqwest::blocking::Client; +use serde_json::Value; + +fn fetch_uv_release_info() -> Result> { + let client = Client::new(); + + println!("Fetching latest uv release info from GitHub..."); + // Fetch latest release info + let response = client + .get("https://api.github.com/repos/astral-sh/uv/releases/latest") + .header("User-Agent", "Anki-Build-Script") + .send()?; + + let release_info: Value = response.json()?; + let assets = release_info["assets"] + .as_array() + .expect("assets should be an array"); + + // Map platform names to their corresponding asset patterns + let platform_patterns = [ + ("LinuxX64", "x86_64-unknown-linux-gnu"), + ("LinuxArm", "aarch64-unknown-linux-gnu"), + ("MacX64", "x86_64-apple-darwin"), + ("MacArm", "aarch64-apple-darwin"), + ("WindowsX64", "x86_64-pc-windows-msvc"), + ("WindowsArm", "aarch64-pc-windows-msvc"), + ]; + + let mut match_blocks = Vec::new(); + + for (platform, pattern) in platform_patterns { + // Find the asset matching the platform pattern (the binary) + let asset = assets.iter().find(|asset| { + let name = asset["name"].as_str().unwrap_or(""); + name.contains(pattern) && (name.ends_with(".tar.gz") || name.ends_with(".zip")) + }); + if asset.is_none() { + eprintln!("No asset found for platform {platform} pattern {pattern}"); + continue; + } + let asset = asset.unwrap(); + let download_url = asset["browser_download_url"].as_str().unwrap(); + let asset_name = asset["name"].as_str().unwrap(); + + // Find the corresponding .sha256 or .sha256sum asset + let sha_asset = assets.iter().find(|a| { + let name = a["name"].as_str().unwrap_or(""); + name == format!("{}.sha256", asset_name) || name == format!("{}.sha256sum", asset_name) + }); + if sha_asset.is_none() { + eprintln!("No sha256 asset found for {asset_name}"); + continue; + } + let sha_asset = sha_asset.unwrap(); + let sha_url = sha_asset["browser_download_url"].as_str().unwrap(); + println!("Fetching SHA256 for {platform}..."); + let sha_text = client + .get(sha_url) + .header("User-Agent", "Anki-Build-Script") + .send()? + .text()?; + // The sha file is usually of the form: " " + let sha256 = sha_text.split_whitespace().next().unwrap_or(""); + + match_blocks.push(format!( + " Platform::{} => {{\n OnlineArchive {{\n url: \"{}\",\n sha256: \"{}\",\n }}\n }}", + platform, download_url, sha256 + )); + } + + Ok(format!( + "pub fn uv_archive(platform: Platform) -> OnlineArchive {{\n match platform {{\n{}\n }}", + match_blocks.join(",\n") + )) +} + +fn read_python_rs() -> Result> { + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".to_string()); + let path = Path::new(&manifest_dir).join("src/python.rs"); + println!("Reading {}", path.display()); + let content = fs::read_to_string(path)?; + Ok(content) +} + +fn update_uv_text(old_text: &str, new_uv_text: &str) -> Result> { + let re = Regex::new(r"(?ms)^pub fn uv_archive\(platform: Platform\) -> OnlineArchive \{.*?\n\s*\}\s*\n\s*\}\s*\n\s*\}").unwrap(); + if !re.is_match(old_text) { + return Err("Could not find uv_archive function block to replace".into()); + } + let new_content = re.replace(old_text, new_uv_text).to_string(); + println!("Original lines: {}", old_text.lines().count()); + println!("Updated lines: {}", new_content.lines().count()); + Ok(new_content) +} + +fn write_python_rs(content: &str) -> Result<(), Box> { + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".to_string()); + let path = Path::new(&manifest_dir).join("src/python.rs"); + println!("Writing to {}", path.display()); + fs::write(path, content)?; + Ok(()) +} + +fn main() -> Result<(), Box> { + let new_uv_archive = fetch_uv_release_info()?; + let content = read_python_rs()?; + let updated_content = update_uv_text(&content, &new_uv_archive)?; + write_python_rs(&updated_content)?; + println!("Successfully updated uv_archive function in python.rs"); + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_update_uv_text_with_actual_file() { + let content = fs::read_to_string("src/python.rs").unwrap(); + let original_lines = content.lines().count(); + + const EXPECTED_LINES_REMOVED: usize = 38; + + let updated = update_uv_text(&content, "").unwrap(); + let updated_lines = updated.lines().count(); + + assert_eq!( + updated_lines, + original_lines - EXPECTED_LINES_REMOVED, + "Expected line count to decrease by exactly {} lines (original: {}, updated: {})", + EXPECTED_LINES_REMOVED, + original_lines, + updated_lines + ); + } +} diff --git a/build/ninja_gen/src/node.rs b/build/ninja_gen/src/node.rs index dac056c10..10b3e6184 100644 --- a/build/ninja_gen/src/node.rs +++ b/build/ninja_gen/src/node.rs @@ -38,6 +38,10 @@ pub fn node_archive(platform: Platform) -> OnlineArchive { url: "https://nodejs.org/dist/v20.11.0/node-v20.11.0-win-x64.zip", sha256: "893115cd92ad27bf178802f15247115e93c0ef0c753b93dca96439240d64feb5", }, + Platform::WindowsArm => OnlineArchive { + url: "https://nodejs.org/dist/v20.11.0/node-v20.11.0-win-arm64.zip", + sha256: "89c1f7034dcd6ff5c17f2af61232a96162a1902f862078347dcf274a938b6142", + }, } } diff --git a/build/ninja_gen/src/protobuf.rs b/build/ninja_gen/src/protobuf.rs index 2643c2ab1..1198f653d 100644 --- a/build/ninja_gen/src/protobuf.rs +++ b/build/ninja_gen/src/protobuf.rs @@ -21,26 +21,26 @@ pub fn protoc_archive(platform: Platform) -> OnlineArchive { match platform { Platform::LinuxX64 => { OnlineArchive { - url: "https://github.com/protocolbuffers/protobuf/releases/download/v21.8/protoc-21.8-linux-x86_64.zip", - sha256: "f90d0dd59065fef94374745627336d622702b67f0319f96cee894d41a974d47a", + url: "https://github.com/protocolbuffers/protobuf/releases/download/v31.1/protoc-31.1-linux-x86_64.zip", + sha256: "96553041f1a91ea0efee963cb16f462f5985b4d65365f3907414c360044d8065", } - } + }, Platform::LinuxArm => { OnlineArchive { - url: "https://github.com/protocolbuffers/protobuf/releases/download/v21.8/protoc-21.8-linux-aarch_64.zip", - sha256: "f3d8eb5839d6186392d8c7b54fbeabbb6fcdd90618a500b77cb2e24faa245cad", + url: "https://github.com/protocolbuffers/protobuf/releases/download/v31.1/protoc-31.1-linux-aarch_64.zip", + sha256: "6c554de11cea04c56ebf8e45b54434019b1cd85223d4bbd25c282425e306ecc2", } - } + }, Platform::MacX64 | Platform::MacArm => { OnlineArchive { - url: "https://github.com/protocolbuffers/protobuf/releases/download/v21.8/protoc-21.8-osx-universal_binary.zip", - sha256: "e3324d3bc2e9bc967a0bec2472e0ec73b26f952c7c87f2403197414f780c3c6c", + url: "https://github.com/protocolbuffers/protobuf/releases/download/v31.1/protoc-31.1-osx-universal_binary.zip", + sha256: "99ea004549c139f46da5638187a85bbe422d78939be0fa01af1aa8ab672e395f", } - } - Platform::WindowsX64 => { + }, + Platform::WindowsX64 | Platform::WindowsArm => { OnlineArchive { - url: "https://github.com/protocolbuffers/protobuf/releases/download/v21.8/protoc-21.8-win64.zip", - sha256: "3657053024faa439ff5f8c1dd2ee06bac0f9b9a3d660e99944f015a7451e87ec", + url: "https://github.com/protocolbuffers/protobuf/releases/download/v31.1/protoc-31.1-win64.zip", + sha256: "70381b116ab0d71cb6a5177d9b17c7c13415866603a0fd40d513dafe32d56c35", } } } @@ -67,7 +67,7 @@ fn clang_format_archive(platform: Platform) -> OnlineArchive { sha256: "238be68d9478163a945754f06a213483473044f5a004c4125d3d9d8d3556466e", } } - Platform::WindowsX64 => { + Platform::WindowsX64 | Platform::WindowsArm=> { OnlineArchive { url: "https://github.com/ankitects/clang-format-binaries/releases/download/anki-2021-01-09/clang-format_windows_x86_64.zip", sha256: "7d9f6915e3f0fb72407830f0fc37141308d2e6915daba72987a52f309fbeaccc", diff --git a/build/ninja_gen/src/python.rs b/build/ninja_gen/src/python.rs index 3a8931697..7ac65e85f 100644 --- a/build/ninja_gen/src/python.rs +++ b/build/ninja_gen/src/python.rs @@ -9,6 +9,7 @@ use maplit::hashmap; use crate::action::BuildAction; use crate::archives::download_and_extract; +use crate::archives::with_exe; use crate::archives::OnlineArchive; use crate::archives::Platform; use crate::hash::simple_hash; @@ -16,82 +17,113 @@ use crate::input::BuildInput; use crate::inputs; use crate::Build; -/// When updating this, pyoxidizer.bzl needs updating too, but it uses different -/// files. -pub fn python_archive(platform: Platform) -> OnlineArchive { +// To update, run 'cargo run --bin update_uv'. +// You'll need to do this when bumping Python versions, as uv bakes in +// the latest known version. +// When updating Python version, make sure to update version tag in BuildWheel +// too. +pub fn uv_archive(platform: Platform) -> OnlineArchive { match platform { Platform::LinuxX64 => { OnlineArchive { - url: "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.9.18+20240107-x86_64_v2-unknown-linux-gnu-install_only.tar.gz", - sha256: "9426bca501ae0a257392b10719e2e20ff5fa5e22a3ce4599d6ad0b3139f86417", + url: "https://github.com/astral-sh/uv/releases/download/0.7.13/uv-x86_64-unknown-linux-gnu.tar.gz", + sha256: "909278eb197c5ed0e9b5f16317d1255270d1f9ea4196e7179ce934d48c4c2545", } - } + }, Platform::LinuxArm => { OnlineArchive { - url: "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.9.18+20240107-aarch64-unknown-linux-gnu-install_only.tar.gz", - sha256: "7d19e1ecd6e582423f7c74a0c67491eaa982ce9d5c5f35f0e4289f83127abcb8", + url: "https://github.com/astral-sh/uv/releases/download/0.7.13/uv-aarch64-unknown-linux-gnu.tar.gz", + sha256: "0b2ad9fe4295881615295add8cc5daa02549d29cc9a61f0578e397efcf12f08f", } - } + }, Platform::MacX64 => { OnlineArchive { - url: "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.9.18+20240107-x86_64-apple-darwin-install_only.tar.gz", - sha256: "5a0bf895a5cb08d6d008140abb41bb2c8cd638a665273f7d8eb258bc89de439b", + url: "https://github.com/astral-sh/uv/releases/download/0.7.13/uv-x86_64-apple-darwin.tar.gz", + sha256: "d785753ac092e25316180626aa691c5dfe1fb075290457ba4fdb72c7c5661321", } - } + }, Platform::MacArm => { OnlineArchive { - url: "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.9.18+20240107-aarch64-apple-darwin-install_only.tar.gz", - sha256: "bf0cd90204a2cc6da48cae1e4b32f48c9f7031fbe1238c5972104ccb0155d368", + url: "https://github.com/astral-sh/uv/releases/download/0.7.13/uv-aarch64-apple-darwin.tar.gz", + sha256: "721f532b73171586574298d4311a91d5ea2c802ef4db3ebafc434239330090c6", } - } + }, Platform::WindowsX64 => { OnlineArchive { - url: "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.9.18+20240107-x86_64-pc-windows-msvc-shared-install_only.tar.gz", - sha256: "8f0544cd593984f7ecb90c685931249c579302124b9821064873f3a14ed07005", + url: "https://github.com/astral-sh/uv/releases/download/0.7.13/uv-x86_64-pc-windows-msvc.zip", + sha256: "e199b10bef1a7cc540014483e7f60f825a174988f41020e9d2a6b01bd60f0669", + } + }, + Platform::WindowsArm => { + OnlineArchive { + url: "https://github.com/astral-sh/uv/releases/download/0.7.13/uv-aarch64-pc-windows-msvc.zip", + sha256: "bb40708ad549ad6a12209cb139dd751bf0ede41deb679ce7513ce197bd9ef234", } } } } -/// Returns the Python binary, which can be used to create venvs. -/// Downloads if missing. -pub fn setup_python(build: &mut Build) -> Result<()> { - // if changing this, make sure you remove out/pyenv - let python_binary = match env::var("PYTHON_BINARY") { +pub fn setup_uv(build: &mut Build, platform: Platform) -> Result<()> { + let uv_binary = match env::var("UV_BINARY") { Ok(path) => { assert!( Utf8Path::new(&path).is_absolute(), - "PYTHON_BINARY must be absolute" + "UV_BINARY must be absolute" ); path.into() } Err(_) => { download_and_extract( build, - "python", - python_archive(build.host_platform), + "uv", + uv_archive(platform), hashmap! { "bin" => [ - if cfg!(windows) { "python.exe" } else { "bin/python3"} - ] }, + with_exe("uv") + ] }, )?; - inputs![":extract:python:bin"] + inputs![":extract:uv:bin"] } }; - build.add_dependency("python_binary", python_binary); + build.add_dependency("uv_binary", uv_binary); + + // Our macOS packaging needs access to the x86 binary on ARM. + if cfg!(target_arch = "aarch64") { + download_and_extract( + build, + "uv_mac_x86", + uv_archive(Platform::MacX64), + hashmap! { "bin" => [ + with_exe("uv") + ] }, + )?; + } + // Our Linux packaging needs access to the ARM binary on x86 + if cfg!(target_arch = "x86_64") { + download_and_extract( + build, + "uv_lin_arm", + uv_archive(Platform::LinuxArm), + hashmap! { "bin" => [ + with_exe("uv") + ] }, + )?; + } + Ok(()) } pub struct PythonEnvironment { - pub folder: &'static str, - pub base_requirements_txt: BuildInput, - pub requirements_txt: BuildInput, + pub deps: BuildInput, + // todo: rename + pub venv_folder: &'static str, + pub extra_args: &'static str, pub extra_binary_exports: &'static [&'static str], } impl BuildAction for PythonEnvironment { fn command(&self) -> &str { if env::var("OFFLINE_BUILD").is_err() { - "$runner pyenv $python_binary $builddir/$pyenv_folder $system_pkgs $base_requirements $requirements" + "$runner pyenv $uv_binary $builddir/$pyenv_folder -- $extra_args" } else { "echo 'OFFLINE_BUILD is set. Using the existing PythonEnvironment.'" } @@ -99,7 +131,7 @@ impl BuildAction for PythonEnvironment { fn files(&mut self, build: &mut impl crate::build::FilesHandle) { let bin_path = |binary: &str| -> Vec { - let folder = self.folder; + let folder = self.venv_folder; let path = if cfg!(windows) { format!("{folder}/scripts/{binary}.exe") } else { @@ -108,21 +140,24 @@ impl BuildAction for PythonEnvironment { vec![path] }; + build.add_inputs("", &self.deps); + build.add_variable("pyenv_folder", self.venv_folder); if env::var("OFFLINE_BUILD").is_err() { - build.add_inputs("python_binary", inputs![":python_binary"]); - build.add_variable("pyenv_folder", self.folder); - build.add_inputs("base_requirements", &self.base_requirements_txt); - build.add_inputs("requirements", &self.requirements_txt); - build.add_outputs_ext("pip", bin_path("pip"), true); + build.add_inputs("uv_binary", inputs![":uv_binary"]); + + // Add --python flag to extra_args if PYTHON_BINARY is set + let mut args = self.extra_args.to_string(); + if let Ok(python_binary) = env::var("PYTHON_BINARY") { + args = format!("--python {} {}", python_binary, args); + } + build.add_variable("extra_args", args); } + build.add_outputs_ext("bin", bin_path("python"), true); for binary in self.extra_binary_exports { build.add_outputs_ext(*binary, bin_path(binary), true); } - } - - fn check_output_timestamps(&self) -> bool { - true + build.add_output_stamp(format!("{}/.stamp", self.venv_folder)); } } diff --git a/build/runner/Cargo.toml b/build/runner/Cargo.toml index 54722f01d..1fffe7050 100644 --- a/build/runner/Cargo.toml +++ b/build/runner/Cargo.toml @@ -15,7 +15,6 @@ camino.workspace = true clap.workspace = true flate2.workspace = true junction.workspace = true -reqwest = { workspace = true, features = ["rustls-tls", "rustls-tls-native-roots"] } sha2.workspace = true tar.workspace = true termcolor.workspace = true @@ -24,3 +23,9 @@ which.workspace = true xz2.workspace = true zip.workspace = true zstd.workspace = true + +[target.'cfg(windows)'.dependencies] +reqwest = { workspace = true, features = ["native-tls"] } + +[target.'cfg(not(windows))'.dependencies] +reqwest = { workspace = true, features = ["rustls-tls", "rustls-tls-native-roots"] } diff --git a/build/runner/src/bundle/artifacts.rs b/build/runner/src/bundle/artifacts.rs deleted file mode 100644 index ec5506717..000000000 --- a/build/runner/src/bundle/artifacts.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright: Ankitects Pty Ltd and contributors -// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -use std::env; -use std::fs; -use std::process::Command; - -use camino::Utf8PathBuf; -use clap::Args; - -use crate::run::run_command; - -#[derive(Args, Debug)] -pub struct BuildArtifactsArgs { - bundle_root: Utf8PathBuf, - pyoxidizer_bin: String, -} - -pub fn build_artifacts(args: BuildArtifactsArgs) { - // build.rs doesn't declare inputs from venv, so we need to force a rebuild to - // ensure changes to our libs/the venv get included - let artifacts = args.bundle_root.join("artifacts"); - if artifacts.exists() { - fs::remove_dir_all(&artifacts).unwrap(); - } - let bundle_root = args.bundle_root.canonicalize_utf8().unwrap(); - let build_folder = bundle_root.join("build"); - if build_folder.exists() { - fs::remove_dir_all(&build_folder).unwrap(); - } - - run_command( - Command::new(&args.pyoxidizer_bin) - .args([ - "--system-rust", - "run-build-script", - "qt/bundle/build.rs", - "--var", - "venv", - "out/bundle/pyenv", - "--var", - "build", - build_folder.as_str(), - ]) - .env("CARGO_MANIFEST_DIR", "qt/bundle") - .env("CARGO_TARGET_DIR", "out/bundle/rust") - .env("PROFILE", "release") - .env("OUT_DIR", &artifacts) - .env("TARGET", env!("TARGET")) - .env("SDKROOT", "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk") - .env("MACOSX_DEPLOYMENT_TARGET", macos_deployment_target()) - .env("CARGO_BUILD_TARGET", env!("TARGET")), - ); -} - -pub fn macos_deployment_target() -> &'static str { - if env!("TARGET") == "x86_64-apple-darwin" { - "10.13.4" - } else { - "11" - } -} diff --git a/build/runner/src/bundle/binary.rs b/build/runner/src/bundle/binary.rs deleted file mode 100644 index e9119220a..000000000 --- a/build/runner/src/bundle/binary.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright: Ankitects Pty Ltd and contributors -// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -use std::process::Command; - -use anki_process::CommandExt; -use camino::Utf8Path; -use camino::Utf8PathBuf; - -use super::artifacts::macos_deployment_target; -use crate::run::run_command; - -pub fn build_bundle_binary() { - let mut features = String::from("build-mode-prebuilt-artifacts"); - if cfg!(target_os = "linux") || cfg!(target_os = "macos") { - features.push_str(",global-allocator-jemalloc,allocator-jemalloc"); - } - - let mut command = Command::new("cargo"); - command - .args([ - "build", - "--manifest-path=qt/bundle/Cargo.toml", - "--target-dir=out/bundle/rust", - "--release", - "--no-default-features", - ]) - .arg(format!("--features={features}")) - .env( - "DEFAULT_PYTHON_CONFIG_RS", - // included in main.rs, so relative to qt/bundle/src - "../../../out/bundle/artifacts/", - ) - .env( - "PYO3_CONFIG_FILE", - Utf8Path::new("out/bundle/artifacts/pyo3-build-config-file.txt") - .canonicalize_utf8() - .unwrap(), - ) - .env("MACOSX_DEPLOYMENT_TARGET", macos_deployment_target()) - .env("SDKROOT", "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk") - .env("CARGO_BUILD_TARGET", env!("TARGET")); - if env!("TARGET") == "x86_64-apple-darwin" { - let xcode_path = Command::run_with_output(["xcode-select", "-p"]).unwrap(); - let ld_classic = Utf8PathBuf::from(xcode_path.stdout.trim()) - .join("Toolchains/XcodeDefault.xctoolchain/usr/bin/ld-classic"); - if ld_classic.exists() { - // work around XCode 15's default linker not supporting macOS 10.15-12. - command.env("RUSTFLAGS", format!("-Clink-arg=-fuse-ld={ld_classic}")); - } - } - run_command(&mut command); -} diff --git a/build/runner/src/bundle/folder.rs b/build/runner/src/bundle/folder.rs deleted file mode 100644 index cdbfd21e8..000000000 --- a/build/runner/src/bundle/folder.rs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright: Ankitects Pty Ltd and contributors -// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -use std::env; -use std::fs; -use std::process::Command; - -use camino::Utf8Path; -use camino::Utf8PathBuf; -use clap::Args; -use clap::ValueEnum; - -use crate::paths::absolute_msys_path; -use crate::paths::unix_path; -use crate::run::run_command; - -#[derive(Clone, Copy, ValueEnum, Debug)] -enum DistKind { - Standard, - Alternate, -} - -#[derive(Args, Debug)] -pub struct BuildDistFolderArgs { - kind: DistKind, - folder_root: Utf8PathBuf, -} - -pub fn build_dist_folder(args: BuildDistFolderArgs) { - let BuildDistFolderArgs { kind, folder_root } = args; - fs::create_dir_all(&folder_root).unwrap(); - // Start with Qt, as it's the largest, and we use --delete to ensure there are - // no stale files in lib/. Skipped on macOS as Qt is handled later. - if !cfg!(target_os = "macos") { - copy_qt_from_venv(kind, &folder_root); - } - clean_top_level_files(&folder_root); - copy_binary_and_pylibs(&folder_root); - if cfg!(target_os = "linux") { - copy_linux_extras(kind, &folder_root); - } else if cfg!(windows) { - copy_windows_extras(&folder_root); - } - fs::write(folder_root.with_extension("stamp"), b"").unwrap(); -} - -fn copy_qt_from_venv(kind: DistKind, folder_root: &Utf8Path) { - let python39 = if cfg!(windows) { "" } else { "python3.9/" }; - let qt_root = match kind { - DistKind::Standard => { - folder_root.join(format!("../pyenv/lib/{python39}site-packages/PyQt6")) - } - DistKind::Alternate => { - folder_root.join(format!("../pyenv-qt5/lib/{python39}site-packages/PyQt5")) - } - }; - let src_path = absolute_msys_path(&qt_root); - let lib_path = folder_root.join("lib"); - fs::create_dir_all(&lib_path).unwrap(); - let dst_path = with_slash(absolute_msys_path(&lib_path)); - run_command(Command::new("rsync").args([ - "-a", - "--delete", - "--exclude-from", - "qt/bundle/qt.exclude", - &src_path, - &dst_path, - ])); -} - -fn copy_linux_extras(kind: DistKind, folder_root: &Utf8Path) { - // add README, installer, etc - run_command(Command::new("rsync").args(["-a", "qt/bundle/lin/", &with_slash(folder_root)])); - - // add extra IME plugins from download - let lib_path = folder_root.join("lib"); - let src_path = folder_root - .join("../../extracted/linux_qt_plugins") - .join(match kind { - DistKind::Standard => "qt6", - DistKind::Alternate => "qt5", - }); - let dst_path = lib_path.join(match kind { - DistKind::Standard => "PyQt6/Qt6/plugins", - DistKind::Alternate => "PyQt5/Qt5/plugins", - }); - run_command(Command::new("rsync").args(["-a", &with_slash(src_path), &with_slash(dst_path)])); -} - -fn copy_windows_extras(folder_root: &Utf8Path) { - run_command(Command::new("rsync").args([ - "-a", - "out/extracted/win_amd64_audio/", - &with_slash(folder_root), - ])); -} - -fn clean_top_level_files(folder_root: &Utf8Path) { - let mut to_remove = vec![]; - for entry in fs::read_dir(folder_root).unwrap() { - let entry = entry.unwrap(); - if entry.file_name() == "lib" { - continue; - } else { - to_remove.push(entry.path()); - } - } - for path in to_remove { - if path.is_dir() { - fs::remove_dir_all(path).unwrap() - } else { - fs::remove_file(path).unwrap() - } - } -} - -fn with_slash

(path: P) -> String -where - P: AsRef, -{ - format!("{}/", path.as_ref()) -} - -fn copy_binary_and_pylibs(folder_root: &Utf8Path) { - let binary = folder_root - .join("../rust") - .join(env!("TARGET")) - .join("release") - .join(if cfg!(windows) { "anki.exe" } else { "anki" }); - let extra_files = folder_root - .join("../build") - .join(env!("TARGET")) - .join("release/resources/extra_files"); - run_command(Command::new("rsync").args([ - "-a", - "--exclude", - "PyQt6", - // misleading, as it misses the GPL PyQt, and our Rust/JS - // dependencies - "--exclude", - "COPYING.txt", - &unix_path(&binary), - &with_slash(unix_path(&extra_files)), - &with_slash(unix_path(folder_root)), - ])); - let google_py = if cfg!(windows) { - folder_root.join("../pyenv/lib/site-packages/google") - } else { - folder_root.join("../pyenv/lib/python3.9/site-packages/google") - }; - run_command(Command::new("rsync").args([ - "-a", - &unix_path(&google_py), - &with_slash(unix_path(&folder_root.join("lib"))), - ])); -} diff --git a/build/runner/src/bundle/mod.rs b/build/runner/src/bundle/mod.rs deleted file mode 100644 index 30a3608ab..000000000 --- a/build/runner/src/bundle/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright: Ankitects Pty Ltd and contributors -// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -pub mod artifacts; -pub mod binary; -pub mod folder; diff --git a/build/runner/src/main.rs b/build/runner/src/main.rs index 8fdf8f06f..41cc1fa2e 100644 --- a/build/runner/src/main.rs +++ b/build/runner/src/main.rs @@ -7,7 +7,6 @@ mod archive; mod build; -mod bundle; mod paths; mod pyenv; mod rsync; @@ -19,11 +18,6 @@ use archive::archive_command; use archive::ArchiveArgs; use build::run_build; use build::BuildArgs; -use bundle::artifacts::build_artifacts; -use bundle::artifacts::BuildArtifactsArgs; -use bundle::binary::build_bundle_binary; -use bundle::folder::build_dist_folder; -use bundle::folder::BuildDistFolderArgs; use clap::Parser; use clap::Subcommand; use pyenv::setup_pyenv; @@ -48,9 +42,6 @@ enum Command { Rsync(RsyncArgs), Run(RunArgs), Build(BuildArgs), - BuildArtifacts(BuildArtifactsArgs), - BuildBundleBinary, - BuildDistFolder(BuildDistFolderArgs), #[clap(subcommand)] Archive(ArchiveArgs), } @@ -62,9 +53,6 @@ fn main() -> Result<()> { Command::Rsync(args) => rsync_files(args), Command::Yarn(args) => setup_yarn(args), Command::Build(args) => run_build(args), - Command::BuildArtifacts(args) => build_artifacts(args), - Command::BuildBundleBinary => build_bundle_binary(), - Command::BuildDistFolder(args) => build_dist_folder(args), Command::Archive(args) => archive_command(args)?, }; Ok(()) diff --git a/build/runner/src/paths.rs b/build/runner/src/paths.rs index 2021120cb..c28dde1b9 100644 --- a/build/runner/src/paths.rs +++ b/build/runner/src/paths.rs @@ -16,8 +16,3 @@ pub fn absolute_msys_path(path: &Utf8Path) -> String { // and \ -> / format!("/{drive}/{}", path[7..].replace('\\', "/")) } - -/// Converts backslashes to forward slashes -pub fn unix_path(path: &Utf8Path) -> String { - path.as_str().replace('\\', "/") -} diff --git a/build/runner/src/pyenv.rs b/build/runner/src/pyenv.rs index 934b88bbc..0bd5ec662 100644 --- a/build/runner/src/pyenv.rs +++ b/build/runner/src/pyenv.rs @@ -1,6 +1,7 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +use std::fs; use std::process::Command; use camino::Utf8Path; @@ -10,12 +11,10 @@ use crate::run::run_command; #[derive(Args)] pub struct PyenvArgs { - python_bin: String, + uv_bin: String, pyenv_folder: String, - initial_reqs: String, - reqs: Vec, - #[arg(long, allow_hyphen_values(true))] - venv_args: Vec, + #[arg(trailing_var_arg = true)] + extra_args: Vec, } /// Set up a venv if one doesn't already exist, and then sync packages with @@ -23,42 +22,23 @@ pub struct PyenvArgs { pub fn setup_pyenv(args: PyenvArgs) { let pyenv_folder = Utf8Path::new(&args.pyenv_folder); - let pyenv_bin_folder = pyenv_folder.join(if cfg!(windows) { "scripts" } else { "bin" }); - let pyenv_python = pyenv_bin_folder.join("python"); - let pip_sync = pyenv_bin_folder.join("pip-sync"); - - // Ensure the venv gets recreated properly if it was created by our uv branch - let cache_tag = pyenv_folder.join("CACHEDIR.TAG"); - if cache_tag.exists() { - println!("Cleaning up uv pyenv..."); - std::fs::remove_dir_all(pyenv_folder).expect("Failed to remove pyenv folder"); - } - - if !pyenv_python.exists() { - run_command( - Command::new(&args.python_bin) - .args(["-m", "venv"]) - .args(args.venv_args) - .arg(pyenv_folder), - ); - - if cfg!(windows) { - // the first install on Windows throws an error the first time pip is upgraded, - // so we install it twice and swallow the first error - let _output = Command::new(&pyenv_python) - .args(["-m", "pip", "install", "-r", &args.initial_reqs]) - .output() - .unwrap(); + // On first run, ninja creates an empty bin/ folder which breaks the initial + // install. But we don't want to indiscriminately remove the folder, or + // macOS Gatekeeper needs to rescan the files each time. + if pyenv_folder.exists() { + let cache_tag = pyenv_folder.join("CACHEDIR.TAG"); + if !cache_tag.exists() { + fs::remove_dir_all(pyenv_folder).expect("Failed to remove existing pyenv folder"); } - - run_command(Command::new(pyenv_python).args([ - "-m", - "pip", - "install", - "-r", - &args.initial_reqs, - ])); } - run_command(Command::new(pip_sync).args(&args.reqs)); + run_command( + Command::new(args.uv_bin) + .env("UV_PROJECT_ENVIRONMENT", args.pyenv_folder.clone()) + .args(["sync", "--frozen"]) + .args(args.extra_args), + ); + + // Write empty stamp file + fs::write(pyenv_folder.join(".stamp"), "").expect("Failed to write stamp file"); } diff --git a/build/runner/src/run.rs b/build/runner/src/run.rs index 5b60ab80c..bff88eb97 100644 --- a/build/runner/src/run.rs +++ b/build/runner/src/run.rs @@ -1,7 +1,6 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -use std::io::ErrorKind; use std::process::Command; use anki_io::create_dir_all; @@ -44,7 +43,7 @@ fn split_env(s: &str) -> Result<(String, String), std::io::Error> { if let Some((k, v)) = s.split_once('=') { Ok((k.into(), v.into())) } else { - Err(std::io::Error::new(ErrorKind::Other, "invalid env var")) + Err(std::io::Error::other("invalid env var")) } } diff --git a/cargo/licenses.json b/cargo/licenses.json index 0333f7e47..c16b20aa7 100644 --- a/cargo/licenses.json +++ b/cargo/licenses.json @@ -10,7 +10,7 @@ }, { "name": "adler2", - "version": "2.0.0", + "version": "2.0.1", "authors": "Jonas Schievink |oyvindln ", "repository": "https://github.com/oyvindln/adler2", "license": "0BSD OR Apache-2.0 OR MIT", @@ -19,7 +19,7 @@ }, { "name": "ahash", - "version": "0.8.11", + "version": "0.8.12", "authors": "Tom Kaitchuck ", "repository": "https://github.com/tkaitchuck/ahash", "license": "Apache-2.0 OR MIT", @@ -181,7 +181,7 @@ }, { "name": "async-compression", - "version": "0.4.23", + "version": "0.4.24", "authors": "Wim Looman |Allen Bui ", "repository": "https://github.com/Nullus157/async-compression", "license": "Apache-2.0 OR MIT", @@ -244,7 +244,7 @@ }, { "name": "axum", - "version": "0.7.9", + "version": "0.8.4", "authors": null, "repository": "https://github.com/tokio-rs/axum", "license": "MIT", @@ -253,7 +253,7 @@ }, { "name": "axum-client-ip", - "version": "0.6.1", + "version": "1.1.3", "authors": null, "repository": "https://github.com/imbolc/axum-client-ip", "license": "MIT", @@ -262,7 +262,7 @@ }, { "name": "axum-core", - "version": "0.4.5", + "version": "0.5.2", "authors": null, "repository": "https://github.com/tokio-rs/axum", "license": "MIT", @@ -271,7 +271,7 @@ }, { "name": "axum-extra", - "version": "0.9.6", + "version": "0.10.1", "authors": null, "repository": "https://github.com/tokio-rs/axum", "license": "MIT", @@ -280,7 +280,7 @@ }, { "name": "axum-macros", - "version": "0.4.2", + "version": "0.5.0", "authors": null, "repository": "https://github.com/tokio-rs/axum", "license": "MIT", @@ -289,22 +289,13 @@ }, { "name": "backtrace", - "version": "0.3.74", + "version": "0.3.75", "authors": "The Rust Project Developers", "repository": "https://github.com/rust-lang/backtrace-rs", "license": "Apache-2.0 OR MIT", "license_file": null, "description": "A library to acquire a stack trace (backtrace) at runtime in a Rust program." }, - { - "name": "base64", - "version": "0.21.7", - "authors": "Alice Maz |Marshall Pierce ", - "repository": "https://github.com/marshallpierce/rust-base64", - "license": "Apache-2.0 OR MIT", - "license_file": null, - "description": "encodes and decodes base64 as bytes or utf8" - }, { "name": "base64", "version": "0.22.1", @@ -316,7 +307,7 @@ }, { "name": "base64ct", - "version": "1.7.3", + "version": "1.8.0", "authors": "RustCrypto Developers", "repository": "https://github.com/RustCrypto/formats", "license": "Apache-2.0 OR MIT", @@ -361,7 +352,7 @@ }, { "name": "bitflags", - "version": "2.9.0", + "version": "2.9.1", "authors": "The Rust Project Developers", "repository": "https://github.com/bitflags/bitflags", "license": "Apache-2.0 OR MIT", @@ -397,7 +388,7 @@ }, { "name": "bumpalo", - "version": "3.17.0", + "version": "3.18.1", "authors": "Nick Fitzgerald ", "repository": "https://github.com/fitzgen/bumpalo", "license": "Apache-2.0 OR MIT", @@ -550,7 +541,7 @@ }, { "name": "bytemuck", - "version": "1.22.0", + "version": "1.23.1", "authors": "Lokathor ", "repository": "https://github.com/Lokathor/bytemuck", "license": "Apache-2.0 OR MIT OR Zlib", @@ -595,7 +586,7 @@ }, { "name": "camino", - "version": "1.1.9", + "version": "1.1.10", "authors": "Without Boats |Ashley Williams |Steve Klabnik |Rain ", "repository": "https://github.com/camino-rs/camino", "license": "Apache-2.0 OR MIT", @@ -613,7 +604,7 @@ }, { "name": "cc", - "version": "1.2.20", + "version": "1.2.27", "authors": "Alex Crichton ", "repository": "https://github.com/rust-lang/cc-rs", "license": "Apache-2.0 OR MIT", @@ -622,9 +613,9 @@ }, { "name": "cfg-if", - "version": "1.0.0", + "version": "1.0.1", "authors": "Alex Crichton ", - "repository": "https://github.com/alexcrichton/cfg-if", + "repository": "https://github.com/rust-lang/cfg-if", "license": "Apache-2.0 OR MIT", "license_file": null, "description": "A macro to ergonomically define an item depending on a large number of #[cfg] parameters. Structured like an if-else chain, the first matching branch is the item that gets emitted." @@ -640,13 +631,22 @@ }, { "name": "chrono", - "version": "0.4.40", + "version": "0.4.41", "authors": null, "repository": "https://github.com/chronotope/chrono", "license": "Apache-2.0 OR MIT", "license_file": null, "description": "Date and time library for Rust" }, + { + "name": "client-ip", + "version": "0.1.1", + "authors": null, + "repository": "https://github.com/imbolc/client-ip", + "license": "MIT", + "license_file": null, + "description": "HTTP client IP address extractors" + }, { "name": "coarsetime", "version": "0.1.36", @@ -694,8 +694,8 @@ }, { "name": "convert_case", - "version": "0.6.0", - "authors": "Rutrum ", + "version": "0.8.0", + "authors": "rutrum ", "repository": "https://github.com/rutrum/convert-case", "license": "MIT", "license_file": null, @@ -712,7 +712,7 @@ }, { "name": "core-foundation", - "version": "0.10.0", + "version": "0.10.1", "authors": "The Servo Project Developers", "repository": "https://github.com/servo/core-foundation-rs", "license": "Apache-2.0 OR MIT", @@ -901,7 +901,7 @@ }, { "name": "cubecl-hip-sys", - "version": "6.4.0", + "version": "6.4.4348200", "authors": "Tracel Technologies Inc.", "repository": "https://github.com/tracel-ai/cubecl-hip/tree/main/crates/cubecl-hip-sys", "license": "Apache-2.0 OR MIT", @@ -1261,7 +1261,7 @@ }, { "name": "errno", - "version": "0.3.11", + "version": "0.3.12", "authors": "Chris Wong |Dan Gohman ", "repository": "https://github.com/lambda-fairy/rust-errno", "license": "Apache-2.0 OR MIT", @@ -1324,7 +1324,7 @@ }, { "name": "flate2", - "version": "1.1.1", + "version": "1.1.2", "authors": "Alex Crichton |Josh Triplett ", "repository": "https://github.com/rust-lang/flate2-rs", "license": "Apache-2.0 OR MIT", @@ -1342,21 +1342,21 @@ }, { "name": "fluent", - "version": "0.16.1", - "authors": "Zibi Braniecki |Staś Małolepszy ", + "version": "0.17.0", + "authors": "Caleb Maclennan |Bruce Mitchener |Staś Małolepszy ", "repository": "https://github.com/projectfluent/fluent-rs", "license": "Apache-2.0 OR MIT", "license_file": null, - "description": "A localization system designed to unleash the entire expressive power of natural language translations." + "description": "An umbrella crate exposing the combined features of fluent-rs crates with additional convenience macros for Project Fluent, a localization system designed to unleash the entire expressive power of natural language translations." }, { "name": "fluent-bundle", - "version": "0.15.3", - "authors": "Zibi Braniecki |Staś Małolepszy ", + "version": "0.16.0", + "authors": "Caleb Maclennan |Bruce Mitchener |Staś Małolepszy ", "repository": "https://github.com/projectfluent/fluent-rs", "license": "Apache-2.0 OR MIT", "license_file": null, - "description": "A localization system designed to unleash the entire expressive power of natural language translations." + "description": "A low-level implementation of a collection of localization messages for a single locale for Project Fluent, a localization system designed to unleash the entire expressive power of natural language translations." }, { "name": "fluent-langneg", @@ -1369,12 +1369,12 @@ }, { "name": "fluent-syntax", - "version": "0.11.1", - "authors": "Zibi Braniecki |Staś Małolepszy ", + "version": "0.12.0", + "authors": "Caleb Maclennan |Bruce Mitchener |Staś Małolepszy ", "repository": "https://github.com/projectfluent/fluent-rs", "license": "Apache-2.0 OR MIT", "license_file": null, - "description": "Parser/Serializer tools for Fluent Syntax." + "description": "A low-level parser, AST, and serializer API for the syntax used by Project Fluent, a localization system designed to unleash the entire expressive power of natural language translations." }, { "name": "fnv", @@ -1448,18 +1448,9 @@ "license_file": null, "description": "Parser and serializer for the application/x-www-form-urlencoded syntax, as used by HTML forms." }, - { - "name": "forwarded-header-value", - "version": "0.1.1", - "authors": "James Brown ", - "repository": "https://github.com/EasyPost/rust-forwarded-header-value", - "license": "ISC", - "license_file": null, - "description": "Parser for values from the Forwarded header (RFC 7239)" - }, { "name": "fsrs", - "version": "4.0.0", + "version": "4.1.1", "authors": "Open Spaced Repetition", "repository": "https://github.com/open-spaced-repetition/fsrs-rs", "license": "BSD-3-Clause", @@ -1711,12 +1702,12 @@ }, { "name": "getopts", - "version": "0.2.21", + "version": "0.2.23", "authors": "The Rust Project Developers", "repository": "https://github.com/rust-lang/getopts", "license": "Apache-2.0 OR MIT", "license_file": null, - "description": "getopts-like option parsing." + "description": "getopts-like option parsing" }, { "name": "getrandom", @@ -1729,7 +1720,7 @@ }, { "name": "getrandom", - "version": "0.3.2", + "version": "0.3.3", "authors": "The Rand Project Developers", "repository": "https://github.com/rust-random/getrandom", "license": "Apache-2.0 OR MIT", @@ -1810,7 +1801,7 @@ }, { "name": "gpu-descriptor", - "version": "0.3.1", + "version": "0.3.2", "authors": "Zakarum ", "repository": "https://github.com/zakarumych/gpu-descriptor", "license": "Apache-2.0 OR MIT", @@ -1828,7 +1819,7 @@ }, { "name": "h2", - "version": "0.4.9", + "version": "0.4.10", "authors": "Carl Lerche |Sean McArthur ", "repository": "https://github.com/hyperium/h2", "license": "MIT", @@ -1864,7 +1855,7 @@ }, { "name": "hashbrown", - "version": "0.15.2", + "version": "0.15.4", "authors": "Amanieu d'Antras ", "repository": "https://github.com/rust-lang/hashbrown", "license": "Apache-2.0 OR MIT", @@ -1873,7 +1864,7 @@ }, { "name": "hashlink", - "version": "0.8.4", + "version": "0.10.0", "authors": "kyren ", "repository": "https://github.com/kyren/hashlink", "license": "Apache-2.0 OR MIT", @@ -1882,7 +1873,7 @@ }, { "name": "headers", - "version": "0.4.0", + "version": "0.4.1", "authors": "Sean McArthur ", "repository": "https://github.com/hyperium/headers", "license": "MIT", @@ -1909,7 +1900,7 @@ }, { "name": "hermit-abi", - "version": "0.3.9", + "version": "0.5.2", "authors": "Stefan Lankes", "repository": "https://github.com/hermit-os/hermit-rs", "license": "Apache-2.0 OR MIT", @@ -2017,7 +2008,7 @@ }, { "name": "hyper-rustls", - "version": "0.27.5", + "version": "0.27.7", "authors": null, "repository": "https://github.com/rustls/hyper-rustls", "license": "Apache-2.0 OR ISC OR MIT", @@ -2035,7 +2026,7 @@ }, { "name": "hyper-util", - "version": "0.1.11", + "version": "0.1.14", "authors": "Sean McArthur ", "repository": "https://github.com/hyperium/hyper-util", "license": "MIT", @@ -2062,7 +2053,7 @@ }, { "name": "icu_collections", - "version": "1.5.0", + "version": "2.0.0", "authors": "The ICU4X Project Developers", "repository": "https://github.com/unicode-org/icu4x", "license": "Unicode-3.0", @@ -2070,35 +2061,17 @@ "description": "Collection of API for use in ICU libraries." }, { - "name": "icu_locid", - "version": "1.5.0", + "name": "icu_locale_core", + "version": "2.0.0", "authors": "The ICU4X Project Developers", "repository": "https://github.com/unicode-org/icu4x", "license": "Unicode-3.0", "license_file": null, "description": "API for managing Unicode Language and Locale Identifiers" }, - { - "name": "icu_locid_transform", - "version": "1.5.0", - "authors": "The ICU4X Project Developers", - "repository": "https://github.com/unicode-org/icu4x", - "license": "Unicode-3.0", - "license_file": null, - "description": "API for Unicode Language and Locale Identifiers canonicalization" - }, - { - "name": "icu_locid_transform_data", - "version": "1.5.1", - "authors": "The ICU4X Project Developers", - "repository": "https://github.com/unicode-org/icu4x", - "license": "Unicode-3.0", - "license_file": null, - "description": "Data for the icu_locid_transform crate" - }, { "name": "icu_normalizer", - "version": "1.5.0", + "version": "2.0.0", "authors": "The ICU4X Project Developers", "repository": "https://github.com/unicode-org/icu4x", "license": "Unicode-3.0", @@ -2107,7 +2080,7 @@ }, { "name": "icu_normalizer_data", - "version": "1.5.1", + "version": "2.0.0", "authors": "The ICU4X Project Developers", "repository": "https://github.com/unicode-org/icu4x", "license": "Unicode-3.0", @@ -2116,7 +2089,7 @@ }, { "name": "icu_properties", - "version": "1.5.1", + "version": "2.0.1", "authors": "The ICU4X Project Developers", "repository": "https://github.com/unicode-org/icu4x", "license": "Unicode-3.0", @@ -2125,7 +2098,7 @@ }, { "name": "icu_properties_data", - "version": "1.5.1", + "version": "2.0.1", "authors": "The ICU4X Project Developers", "repository": "https://github.com/unicode-org/icu4x", "license": "Unicode-3.0", @@ -2134,22 +2107,13 @@ }, { "name": "icu_provider", - "version": "1.5.0", + "version": "2.0.0", "authors": "The ICU4X Project Developers", "repository": "https://github.com/unicode-org/icu4x", "license": "Unicode-3.0", "license_file": null, "description": "Trait and struct definitions for the ICU data provider" }, - { - "name": "icu_provider_macros", - "version": "1.5.0", - "authors": "The ICU4X Project Developers", - "repository": "https://github.com/unicode-org/icu4x", - "license": "Unicode-3.0", - "license_file": null, - "description": "Proc macros for ICU data providers" - }, { "name": "id_tree", "version": "1.8.0", @@ -2179,7 +2143,7 @@ }, { "name": "idna_adapter", - "version": "1.2.0", + "version": "1.2.1", "authors": "The rust-url developers", "repository": "https://github.com/hsivonen/idna_adapter", "license": "Apache-2.0 OR MIT", @@ -2206,12 +2170,12 @@ }, { "name": "intl-memoizer", - "version": "0.5.2", - "authors": "Zibi Braniecki |Manish Goregaokar ", + "version": "0.5.3", + "authors": "Caleb Maclennan |Bruce Mitchener |Staś Małolepszy ", "repository": "https://github.com/projectfluent/fluent-rs", "license": "Apache-2.0 OR MIT", "license_file": null, - "description": "A memoizer specifically tailored for storing lazy-initialized intl formatters." + "description": "A memoizer specifically tailored for storing lazy-initialized intl formatters for Project Fluent, a localization system designed to unleash the entire expressive power of natural language translations." }, { "name": "intl_pluralrules", @@ -2232,13 +2196,13 @@ "description": "Provides types and useful methods for working with IPv4 and IPv6 network addresses, commonly called IP prefixes. The new `IpNet`, `Ipv4Net`, and `Ipv6Net` types build on the existing `IpAddr`, `Ipv4Addr`, and `Ipv6Addr` types already provided in Rust's standard library and align to their design to stay consistent. The module also provides useful traits that extend `Ipv4Addr` and `Ipv6Addr` with methods for `Add`, `Sub`, `BitAnd`, and `BitOr` operations. The module only uses stable feature so it is guaranteed to compile using the stable toolchain." }, { - "name": "itertools", - "version": "0.13.0", - "authors": "bluss", - "repository": "https://github.com/rust-itertools/itertools", + "name": "iri-string", + "version": "0.7.8", + "authors": "YOSHIOKA Takuma ", + "repository": "https://github.com/lo48576/iri-string", "license": "Apache-2.0 OR MIT", "license_file": null, - "description": "Extra iterator adaptors, iterator methods, free functions, and macros." + "description": "IRI as string types" }, { "name": "itertools", @@ -2314,7 +2278,7 @@ }, { "name": "libc", - "version": "0.2.172", + "version": "0.2.173", "authors": "The Rust Project Developers", "repository": "https://github.com/rust-lang/libc", "license": "Apache-2.0 OR MIT", @@ -2323,7 +2287,7 @@ }, { "name": "libloading", - "version": "0.8.6", + "version": "0.8.8", "authors": "Simonas Kazlauskas ", "repository": "https://github.com/nagisa/rust_libloading/", "license": "ISC", @@ -2332,7 +2296,7 @@ }, { "name": "libm", - "version": "0.2.13", + "version": "0.2.15", "authors": "Jorge Aparicio ", "repository": "https://github.com/rust-lang/compiler-builtins", "license": "MIT", @@ -2350,13 +2314,22 @@ }, { "name": "libsqlite3-sys", - "version": "0.27.0", + "version": "0.34.0", "authors": "The rusqlite developers", "repository": "https://github.com/rusqlite/rusqlite", "license": "MIT", "license_file": null, "description": "Native bindings to the libsqlite3 library" }, + { + "name": "libz-rs-sys", + "version": "0.5.1", + "authors": null, + "repository": "https://github.com/trifectatechfoundation/zlib-rs", + "license": "Zlib", + "license_file": null, + "description": "A memory-safe zlib implementation written in rust" + }, { "name": "linux-raw-sys", "version": "0.9.4", @@ -2368,7 +2341,7 @@ }, { "name": "litemap", - "version": "0.7.5", + "version": "0.8.0", "authors": "The ICU4X Project Developers", "repository": "https://github.com/unicode-org/icu4x", "license": "Unicode-3.0", @@ -2386,7 +2359,7 @@ }, { "name": "lock_api", - "version": "0.4.12", + "version": "0.4.13", "authors": "Amanieu d'Antras ", "repository": "https://github.com/Amanieu/parking_lot", "license": "Apache-2.0 OR MIT", @@ -2402,6 +2375,15 @@ "license_file": null, "description": "A lightweight logging facade for Rust" }, + { + "name": "lru-slab", + "version": "0.1.2", + "authors": "Benjamin Saunders ", + "repository": "https://github.com/Ralith/lru-slab", + "license": "Apache-2.0 OR MIT OR Zlib", + "license_file": null, + "description": "Pre-allocated storage with constant-time LRU tracking" + }, { "name": "mac", "version": "0.1.1", @@ -2413,7 +2395,7 @@ }, { "name": "macerator", - "version": "0.2.6", + "version": "0.2.8", "authors": "Genna Wingert", "repository": "https://github.com/wingertge/macerator", "license": "Apache-2.0 OR MIT", @@ -2422,7 +2404,7 @@ }, { "name": "macerator-macros", - "version": "0.1.1", + "version": "0.1.2", "authors": "Genna Wingert", "repository": "https://github.com/wingertge/macerator", "license": "Apache-2.0 OR MIT", @@ -2485,7 +2467,7 @@ }, { "name": "matchit", - "version": "0.7.3", + "version": "0.8.4", "authors": "Ibraheem Ahmed ", "repository": "https://github.com/ibraheemdev/matchit", "license": "MIT AND BSD-3-Clause", @@ -2494,7 +2476,7 @@ }, { "name": "matrixmultiply", - "version": "0.3.9", + "version": "0.3.10", "authors": "bluss|R. Janis Goldschmidt", "repository": "https://github.com/bluss/matrixmultiply/", "license": "Apache-2.0 OR MIT", @@ -2512,7 +2494,7 @@ }, { "name": "memchr", - "version": "2.7.4", + "version": "2.7.5", "authors": "Andrew Gallant |bluss", "repository": "https://github.com/BurntSushi/memchr", "license": "MIT OR Unlicense", @@ -2566,7 +2548,7 @@ }, { "name": "miniz_oxide", - "version": "0.8.8", + "version": "0.8.9", "authors": "Frommi |oyvindln |Rich Geldreich richgel99@gmail.com", "repository": "https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide", "license": "Apache-2.0 OR MIT OR Zlib", @@ -2575,13 +2557,22 @@ }, { "name": "mio", - "version": "1.0.3", + "version": "1.0.4", "authors": "Carl Lerche |Thomas de Zeeuw |Tokio Contributors ", "repository": "https://github.com/tokio-rs/mio", "license": "MIT", "license_file": null, "description": "Lightweight non-blocking I/O." }, + { + "name": "moddef", + "version": "0.2.6", + "authors": null, + "repository": "https://github.com/sigurd4/moddef", + "license": "MIT", + "license_file": null, + "description": "Macro for convenient module declaration. Each module can be put in a group, and visibility can be applied to the whole group with ease." + }, { "name": "multer", "version": "3.1.0", @@ -2593,7 +2584,7 @@ }, { "name": "multimap", - "version": "0.10.0", + "version": "0.10.1", "authors": "Håvar Nøvik ", "repository": "https://github.com/havarnov/multimap", "license": "Apache-2.0 OR MIT", @@ -2627,15 +2618,6 @@ "license_file": null, "description": "An n-dimensional array for general elements and for numerics. Lightweight array views and slicing; views support chunking and splitting." }, - { - "name": "ndarray-rand", - "version": "0.15.0", - "authors": "bluss", - "repository": "https://github.com/rust-ndarray/ndarray", - "license": "Apache-2.0 OR MIT", - "license_file": null, - "description": "Constructors for randomized arrays. `rand` integration for `ndarray`." - }, { "name": "ndk-sys", "version": "0.5.0+25.2.9519653", @@ -2663,15 +2645,6 @@ "license_file": null, "description": "A byte-oriented, zero-copy, parser combinators library" }, - { - "name": "nonempty", - "version": "0.7.0", - "authors": "Alexis Sellier ", - "repository": "https://github.com/cloudhead/nonempty", - "license": "MIT", - "license_file": null, - "description": "Correct by construction non-empty vector" - }, { "name": "ntapi", "version": "0.4.1", @@ -2773,7 +2746,7 @@ }, { "name": "num_cpus", - "version": "1.16.0", + "version": "1.17.0", "authors": "Sean McArthur ", "repository": "https://github.com/seanmonstar/num_cpus", "license": "Apache-2.0 OR MIT", @@ -2845,7 +2818,7 @@ }, { "name": "openssl", - "version": "0.10.72", + "version": "0.10.73", "authors": "Steven Fackler ", "repository": "https://github.com/sfackler/rust-openssl", "license": "Apache-2.0", @@ -2872,7 +2845,7 @@ }, { "name": "openssl-sys", - "version": "0.9.107", + "version": "0.9.109", "authors": "Alex Crichton |Steven Fackler ", "repository": "https://github.com/sfackler/rust-openssl", "license": "MIT", @@ -2926,7 +2899,7 @@ }, { "name": "parking_lot", - "version": "0.12.3", + "version": "0.12.4", "authors": "Amanieu d'Antras ", "repository": "https://github.com/Amanieu/parking_lot", "license": "Apache-2.0 OR MIT", @@ -2935,7 +2908,7 @@ }, { "name": "parking_lot_core", - "version": "0.9.10", + "version": "0.9.11", "authors": "Amanieu d'Antras ", "repository": "https://github.com/Amanieu/parking_lot", "license": "Apache-2.0 OR MIT", @@ -3088,7 +3061,7 @@ }, { "name": "portable-atomic", - "version": "1.11.0", + "version": "1.11.1", "authors": null, "repository": "https://github.com/taiki-e/portable-atomic", "license": "Apache-2.0 OR MIT", @@ -3104,6 +3077,15 @@ "license_file": null, "description": "Synchronization primitives built with portable-atomic." }, + { + "name": "potential_utf", + "version": "0.1.2", + "authors": "The ICU4X Project Developers", + "repository": "https://github.com/unicode-org/icu4x", + "license": "Unicode-3.0", + "license_file": null, + "description": "Unvalidated string and character types" + }, { "name": "powerfmt", "version": "0.2.0", @@ -3142,7 +3124,7 @@ }, { "name": "prettyplease", - "version": "0.2.32", + "version": "0.2.34", "authors": "David Tolnay ", "repository": "https://github.com/dtolnay/prettyplease", "license": "Apache-2.0 OR MIT", @@ -3241,13 +3223,22 @@ }, { "name": "pulldown-cmark", - "version": "0.9.6", + "version": "0.13.0", "authors": "Raph Levien |Marcus Klaas de Vries ", "repository": "https://github.com/raphlinus/pulldown-cmark", "license": "MIT", "license_file": null, "description": "A pull parser for CommonMark" }, + { + "name": "pulldown-cmark-escape", + "version": "0.11.0", + "authors": "Raph Levien |Marcus Klaas de Vries ", + "repository": "https://github.com/raphlinus/pulldown-cmark", + "license": "MIT", + "license_file": null, + "description": "An escape library for HTML created in the pulldown-cmark project" + }, { "name": "pulp", "version": "0.18.22", @@ -3259,7 +3250,7 @@ }, { "name": "pulp", - "version": "0.21.4", + "version": "0.21.5", "authors": "sarah <>", "repository": "https://github.com/sarah-ek/pulp/", "license": "MIT", @@ -3268,7 +3259,7 @@ }, { "name": "quinn", - "version": "0.11.7", + "version": "0.11.8", "authors": null, "repository": "https://github.com/quinn-rs/quinn", "license": "Apache-2.0 OR MIT", @@ -3277,7 +3268,7 @@ }, { "name": "quinn-proto", - "version": "0.11.11", + "version": "0.11.12", "authors": null, "repository": "https://github.com/quinn-rs/quinn", "license": "Apache-2.0 OR MIT", @@ -3286,7 +3277,7 @@ }, { "name": "quinn-udp", - "version": "0.5.11", + "version": "0.5.12", "authors": null, "repository": "https://github.com/quinn-rs/quinn", "license": "Apache-2.0 OR MIT", @@ -3365,15 +3356,6 @@ "license_file": null, "description": "Core random number generator traits and tools for implementation." }, - { - "name": "rand_distr", - "version": "0.4.3", - "authors": "The Rand Project Developers", - "repository": "https://github.com/rust-random/rand", - "license": "Apache-2.0 OR MIT", - "license_file": null, - "description": "Sampling from random number distributions" - }, { "name": "rand_distr", "version": "0.5.1", @@ -3457,7 +3439,7 @@ }, { "name": "redox_syscall", - "version": "0.5.11", + "version": "0.5.13", "authors": "Jeremy Soller ", "repository": "https://gitlab.redox-os.org/redox-os/syscall", "license": "MIT", @@ -3547,7 +3529,7 @@ }, { "name": "reqwest", - "version": "0.12.15", + "version": "0.12.20", "authors": "Sean McArthur ", "repository": "https://github.com/seanmonstar/reqwest", "license": "Apache-2.0 OR MIT", @@ -3601,7 +3583,7 @@ }, { "name": "rusqlite", - "version": "0.30.0", + "version": "0.36.0", "authors": "The rusqlite developers", "repository": "https://github.com/rusqlite/rusqlite", "license": "MIT", @@ -3610,7 +3592,7 @@ }, { "name": "rustc-demangle", - "version": "0.1.24", + "version": "0.1.25", "authors": "Alex Crichton ", "repository": "https://github.com/rust-lang/rustc-demangle", "license": "Apache-2.0 OR MIT", @@ -3646,7 +3628,7 @@ }, { "name": "rustix", - "version": "1.0.5", + "version": "1.0.7", "authors": "Dan Gohman |Jakub Konka ", "repository": "https://github.com/bytecodealliance/rustix", "license": "Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT", @@ -3655,7 +3637,7 @@ }, { "name": "rustls", - "version": "0.23.26", + "version": "0.23.28", "authors": null, "repository": "https://github.com/rustls/rustls", "license": "Apache-2.0 OR ISC OR MIT", @@ -3682,7 +3664,7 @@ }, { "name": "rustls-pki-types", - "version": "1.11.0", + "version": "1.12.0", "authors": null, "repository": "https://github.com/rustls/pki-types", "license": "Apache-2.0 OR MIT", @@ -3691,7 +3673,7 @@ }, { "name": "rustls-webpki", - "version": "0.103.1", + "version": "0.103.3", "authors": null, "repository": "https://github.com/rustls/webpki", "license": "ISC", @@ -3700,7 +3682,7 @@ }, { "name": "rustversion", - "version": "1.0.20", + "version": "1.0.21", "authors": "David Tolnay ", "repository": "https://github.com/dtolnay/rustversion", "license": "Apache-2.0 OR MIT", @@ -3797,15 +3779,6 @@ "license_file": null, "description": "Apple `Security.framework` low-level FFI bindings" }, - { - "name": "self_cell", - "version": "0.10.3", - "authors": "Lukas Bergdoll ", - "repository": "https://github.com/Voultapher/self_cell", - "license": "Apache-2.0", - "license_file": null, - "description": "Safe-to-use proc-macro-free self-referential structs in stable Rust." - }, { "name": "self_cell", "version": "1.2.0", @@ -3905,9 +3878,18 @@ "license_file": null, "description": "Derive Serialize and Deserialize that delegates to the underlying repr of a C-like enum." }, + { + "name": "serde_spanned", + "version": "0.6.9", + "authors": null, + "repository": "https://github.com/toml-rs/toml", + "license": "Apache-2.0 OR MIT", + "license_file": null, + "description": "Serde-compatible spanned Value" + }, { "name": "serde_tuple", - "version": "0.5.0", + "version": "1.1.0", "authors": "Jacob Brown ", "repository": "https://github.com/kardeiz/serde_tuple", "license": "MIT", @@ -3916,12 +3898,12 @@ }, { "name": "serde_tuple_macros", - "version": "0.5.0", + "version": "1.0.1", "authors": "Jacob Brown ", "repository": "https://github.com/kardeiz/serde_tuple", "license": "MIT", "license_file": null, - "description": "De/serialize structs with named fields as array of values" + "description": "Internal proc-macro crate for serde_tuple" }, { "name": "serde_urlencoded", @@ -3943,7 +3925,7 @@ }, { "name": "sha2", - "version": "0.10.8", + "version": "0.10.9", "authors": "RustCrypto Developers", "repository": "https://github.com/RustCrypto/hashes", "license": "Apache-2.0 OR MIT", @@ -3977,6 +3959,15 @@ "license_file": null, "description": "Backend crate for signal-hook" }, + { + "name": "simd-adler32", + "version": "0.3.7", + "authors": "Marvin Countryman ", + "repository": "https://github.com/mcountryman/simd-adler32", + "license": "MIT", + "license_file": null, + "description": "A SIMD-accelerated Adler-32 hash algorithm implementation." + }, { "name": "siphasher", "version": "1.0.1", @@ -3988,7 +3979,7 @@ }, { "name": "slab", - "version": "0.4.9", + "version": "0.4.10", "authors": "Carl Lerche ", "repository": "https://github.com/tokio-rs/slab", "license": "MIT", @@ -4006,7 +3997,7 @@ }, { "name": "smallvec", - "version": "1.15.0", + "version": "1.15.1", "authors": "The Servo Project Developers", "repository": "https://github.com/servo/rust-smallvec", "license": "Apache-2.0 OR MIT", @@ -4042,7 +4033,7 @@ }, { "name": "socket2", - "version": "0.5.9", + "version": "0.5.10", "authors": "Alex Crichton |Thomas de Zeeuw ", "repository": "https://github.com/rust-lang/socket2", "license": "Apache-2.0 OR MIT", @@ -4177,7 +4168,7 @@ }, { "name": "syn", - "version": "2.0.101", + "version": "2.0.103", "authors": "David Tolnay ", "repository": "https://github.com/dtolnay/syn", "license": "Apache-2.0 OR MIT", @@ -4195,7 +4186,7 @@ }, { "name": "synstructure", - "version": "0.13.1", + "version": "0.13.2", "authors": "Nika Layzell ", "repository": "https://github.com/mystor/synstructure", "license": "MIT", @@ -4240,7 +4231,7 @@ }, { "name": "tempfile", - "version": "3.19.1", + "version": "3.20.0", "authors": "Steven Allen |The Rust Project Developers|Ashley Mannix |Jason White ", "repository": "https://github.com/Stebalien/tempfile", "license": "Apache-2.0 OR MIT", @@ -4321,7 +4312,7 @@ }, { "name": "thread_local", - "version": "1.1.8", + "version": "1.1.9", "authors": "Amanieu d'Antras ", "repository": "https://github.com/Amanieu/thread_local-rs", "license": "Apache-2.0 OR MIT", @@ -4357,7 +4348,7 @@ }, { "name": "tinystr", - "version": "0.7.6", + "version": "0.8.1", "authors": "The ICU4X Project Developers", "repository": "https://github.com/unicode-org/icu4x", "license": "Unicode-3.0", @@ -4384,7 +4375,7 @@ }, { "name": "tokio", - "version": "1.44.2", + "version": "1.45.1", "authors": "Tokio Contributors ", "repository": "https://github.com/tokio-rs/tokio", "license": "MIT", @@ -4418,15 +4409,6 @@ "license_file": null, "description": "Asynchronous TLS/SSL streams for Tokio using Rustls." }, - { - "name": "tokio-socks", - "version": "0.5.2", - "authors": "Yilin Chen ", - "repository": "https://github.com/sticnarf/tokio-socks", - "license": "MIT", - "license_file": null, - "description": "Asynchronous SOCKS proxy support for Rust." - }, { "name": "tokio-util", "version": "0.7.15", @@ -4438,8 +4420,8 @@ }, { "name": "toml_datetime", - "version": "0.6.9", - "authors": "Alex Crichton ", + "version": "0.6.11", + "authors": null, "repository": "https://github.com/toml-rs/toml", "license": "Apache-2.0 OR MIT", "license_file": null, @@ -4447,13 +4429,22 @@ }, { "name": "toml_edit", - "version": "0.22.25", - "authors": "Andronik Ordian |Ed Page ", + "version": "0.22.27", + "authors": null, "repository": "https://github.com/toml-rs/toml", "license": "Apache-2.0 OR MIT", "license_file": null, "description": "Yet another format-preserving TOML parser." }, + { + "name": "toml_write", + "version": "0.1.2", + "authors": null, + "repository": "https://github.com/toml-rs/toml", + "license": "Apache-2.0 OR MIT", + "license_file": null, + "description": "A low-level interface for writing out TOML" + }, { "name": "tower", "version": "0.5.2", @@ -4465,7 +4456,7 @@ }, { "name": "tower-http", - "version": "0.5.2", + "version": "0.6.6", "authors": "Tower Maintainers ", "repository": "https://github.com/tower-rs/tower-http", "license": "MIT", @@ -4510,7 +4501,7 @@ }, { "name": "tracing-attributes", - "version": "0.1.28", + "version": "0.1.29", "authors": "Tokio Contributors |Eliza Weisman |David Barsky ", "repository": "https://github.com/tokio-rs/tracing", "license": "MIT", @@ -4519,7 +4510,7 @@ }, { "name": "tracing-core", - "version": "0.1.33", + "version": "0.1.34", "authors": "Tokio Contributors ", "repository": "https://github.com/tokio-rs/tracing", "license": "MIT", @@ -4555,7 +4546,7 @@ }, { "name": "type-map", - "version": "0.5.0", + "version": "0.5.1", "authors": "Jacob Brown ", "repository": "https://github.com/kardeiz/type-map", "license": "Apache-2.0 OR MIT", @@ -4609,7 +4600,7 @@ }, { "name": "unic-langid", - "version": "0.9.5", + "version": "0.9.6", "authors": "Zibi Braniecki ", "repository": "https://github.com/zbraniecki/unic-locale", "license": "Apache-2.0 OR MIT", @@ -4618,7 +4609,7 @@ }, { "name": "unic-langid-impl", - "version": "0.9.5", + "version": "0.9.6", "authors": "Zibi Braniecki ", "repository": "https://github.com/zbraniecki/unic-locale", "license": "Apache-2.0 OR MIT", @@ -4627,7 +4618,7 @@ }, { "name": "unic-langid-macros", - "version": "0.9.5", + "version": "0.9.6", "authors": "Zibi Braniecki ", "repository": "https://github.com/zbraniecki/unic-locale", "license": "Apache-2.0 OR MIT", @@ -4636,7 +4627,7 @@ }, { "name": "unic-langid-macros-impl", - "version": "0.9.5", + "version": "0.9.6", "authors": "Zibi Braniecki ", "repository": "https://github.com/zbraniecki/unic-locale", "license": "Apache-2.0 OR MIT", @@ -4699,16 +4690,7 @@ }, { "name": "unicode-width", - "version": "0.1.14", - "authors": "kwantam |Manish Goregaokar ", - "repository": "https://github.com/unicode-rs/unicode-width", - "license": "Apache-2.0 OR MIT", - "license_file": null, - "description": "Determine displayed width of `char` and `str` types according to Unicode Standard Annex #11 rules." - }, - { - "name": "unicode-width", - "version": "0.2.0", + "version": "0.2.1", "authors": "kwantam |Manish Goregaokar ", "repository": "https://github.com/unicode-rs/unicode-width", "license": "Apache-2.0 OR MIT", @@ -4760,15 +4742,6 @@ "license_file": null, "description": "Incremental, zero-copy UTF-8 decoding with error handling" }, - { - "name": "utf16_iter", - "version": "1.0.5", - "authors": "Henri Sivonen ", - "repository": "https://github.com/hsivonen/utf16_iter", - "license": "Apache-2.0 OR MIT", - "license_file": null, - "description": "Iterator by char over potentially-invalid UTF-16 in &[u16]" - }, { "name": "utf8_iter", "version": "1.0.4", @@ -4780,7 +4753,7 @@ }, { "name": "uuid", - "version": "1.16.0", + "version": "1.17.0", "authors": "Ashley Mannix|Dylan DPC|Hunar Roop Kahlon", "repository": "https://github.com/uuid-rs/uuid", "license": "Apache-2.0 OR MIT", @@ -4843,7 +4816,7 @@ }, { "name": "wasi", - "version": "0.11.0+wasi-snapshot-preview1", + "version": "0.11.1+wasi-snapshot-preview1", "authors": "The Cranelift Project Developers", "repository": "https://github.com/bytecodealliance/wasi", "license": "Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT", @@ -4951,7 +4924,7 @@ }, { "name": "web_atoms", - "version": "0.1.0", + "version": "0.1.3", "authors": "The html5ever Project Developers", "repository": "https://github.com/servo/html5ever", "license": "Apache-2.0 OR MIT", @@ -4960,16 +4933,16 @@ }, { "name": "webpki-roots", - "version": "0.26.8", + "version": "1.0.0", "authors": null, "repository": "https://github.com/rustls/webpki-roots", - "license": "MPL-2.0", + "license": "CDLA-Permissive-2.0", "license_file": null, "description": "Mozilla's CA root certificates for use with webpki" }, { "name": "wgpu", - "version": "25.0.0", + "version": "25.0.2", "authors": "gfx-rs developers", "repository": "https://github.com/gfx-rs/wgpu", "license": "Apache-2.0 OR MIT", @@ -4978,7 +4951,7 @@ }, { "name": "wgpu-core", - "version": "25.0.1", + "version": "25.0.2", "authors": "gfx-rs developers", "repository": "https://github.com/gfx-rs/wgpu", "license": "Apache-2.0 OR MIT", @@ -5014,7 +4987,7 @@ }, { "name": "wgpu-hal", - "version": "25.0.1", + "version": "25.0.2", "authors": "gfx-rs developers", "repository": "https://github.com/gfx-rs/wgpu", "license": "Apache-2.0 OR MIT", @@ -5077,7 +5050,7 @@ }, { "name": "windows", - "version": "0.57.0", + "version": "0.58.0", "authors": "Microsoft", "repository": "https://github.com/microsoft/windows-rs", "license": "Apache-2.0 OR MIT", @@ -5086,13 +5059,22 @@ }, { "name": "windows", - "version": "0.58.0", + "version": "0.61.3", "authors": "Microsoft", "repository": "https://github.com/microsoft/windows-rs", "license": "Apache-2.0 OR MIT", "license_file": null, "description": "Rust for Windows" }, + { + "name": "windows-collections", + "version": "0.2.0", + "authors": null, + "repository": "https://github.com/microsoft/windows-rs", + "license": "Apache-2.0 OR MIT", + "license_file": null, + "description": "Windows collection types" + }, { "name": "windows-core", "version": "0.56.0", @@ -5102,15 +5084,6 @@ "license_file": null, "description": "Rust for Windows" }, - { - "name": "windows-core", - "version": "0.57.0", - "authors": "Microsoft", - "repository": "https://github.com/microsoft/windows-rs", - "license": "Apache-2.0 OR MIT", - "license_file": null, - "description": "Rust for Windows" - }, { "name": "windows-core", "version": "0.58.0", @@ -5122,7 +5095,7 @@ }, { "name": "windows-core", - "version": "0.61.0", + "version": "0.61.2", "authors": "Microsoft", "repository": "https://github.com/microsoft/windows-rs", "license": "Apache-2.0 OR MIT", @@ -5130,17 +5103,17 @@ "description": "Core type support for COM and Windows" }, { - "name": "windows-implement", - "version": "0.56.0", - "authors": "Microsoft", + "name": "windows-future", + "version": "0.2.1", + "authors": null, "repository": "https://github.com/microsoft/windows-rs", "license": "Apache-2.0 OR MIT", "license_file": null, - "description": "The implement macro for the windows crate" + "description": "Windows async types" }, { "name": "windows-implement", - "version": "0.57.0", + "version": "0.56.0", "authors": "Microsoft", "repository": "https://github.com/microsoft/windows-rs", "license": "Apache-2.0 OR MIT", @@ -5174,15 +5147,6 @@ "license_file": null, "description": "The interface macro for the windows crate" }, - { - "name": "windows-interface", - "version": "0.57.0", - "authors": "Microsoft", - "repository": "https://github.com/microsoft/windows-rs", - "license": "Apache-2.0 OR MIT", - "license_file": null, - "description": "The interface macro for the windows crate" - }, { "name": "windows-interface", "version": "0.58.0", @@ -5203,7 +5167,7 @@ }, { "name": "windows-link", - "version": "0.1.1", + "version": "0.1.3", "authors": "Microsoft", "repository": "https://github.com/microsoft/windows-rs", "license": "Apache-2.0 OR MIT", @@ -5211,13 +5175,13 @@ "description": "Linking for Windows" }, { - "name": "windows-registry", - "version": "0.4.0", - "authors": "Microsoft", + "name": "windows-numerics", + "version": "0.2.0", + "authors": null, "repository": "https://github.com/microsoft/windows-rs", "license": "Apache-2.0 OR MIT", "license_file": null, - "description": "Windows registry" + "description": "Windows numeric types" }, { "name": "windows-result", @@ -5239,7 +5203,7 @@ }, { "name": "windows-result", - "version": "0.3.2", + "version": "0.3.4", "authors": "Microsoft", "repository": "https://github.com/microsoft/windows-rs", "license": "Apache-2.0 OR MIT", @@ -5257,16 +5221,7 @@ }, { "name": "windows-strings", - "version": "0.3.1", - "authors": "Microsoft", - "repository": "https://github.com/microsoft/windows-rs", - "license": "Apache-2.0 OR MIT", - "license_file": null, - "description": "Windows string types" - }, - { - "name": "windows-strings", - "version": "0.4.0", + "version": "0.4.2", "authors": "Microsoft", "repository": "https://github.com/microsoft/windows-rs", "license": "Apache-2.0 OR MIT", @@ -5300,6 +5255,15 @@ "license_file": null, "description": "Rust for Windows" }, + { + "name": "windows-sys", + "version": "0.60.2", + "authors": "Microsoft", + "repository": "https://github.com/microsoft/windows-rs", + "license": "Apache-2.0 OR MIT", + "license_file": null, + "description": "Rust for Windows" + }, { "name": "windows-targets", "version": "0.48.5", @@ -5320,13 +5284,22 @@ }, { "name": "windows-targets", - "version": "0.53.0", + "version": "0.53.2", "authors": "Microsoft", "repository": "https://github.com/microsoft/windows-rs", "license": "Apache-2.0 OR MIT", "license_file": null, "description": "Import libs for Windows" }, + { + "name": "windows-threading", + "version": "0.1.0", + "authors": "Microsoft", + "repository": "https://github.com/microsoft/windows-rs", + "license": "Apache-2.0 OR MIT", + "license_file": null, + "description": "Windows threading" + }, { "name": "windows_aarch64_gnullvm", "version": "0.48.5", @@ -5536,7 +5509,7 @@ }, { "name": "winnow", - "version": "0.7.7", + "version": "0.7.11", "authors": null, "repository": "https://github.com/winnow-rs/winnow", "license": "MIT", @@ -5570,18 +5543,9 @@ "license_file": null, "description": "Derive macro for nvml-wrapper, not for general use" }, - { - "name": "write16", - "version": "1.0.0", - "authors": null, - "repository": "https://github.com/hsivonen/write16", - "license": "Apache-2.0 OR MIT", - "license_file": null, - "description": "A UTF-16 analog of the Write trait" - }, { "name": "writeable", - "version": "0.5.5", + "version": "0.6.1", "authors": "The ICU4X Project Developers", "repository": "https://github.com/unicode-org/icu4x", "license": "Unicode-3.0", @@ -5606,6 +5570,15 @@ "license_file": null, "description": "Abstraction allowing borrowed data to be carried along with the backing data it borrows from" }, + { + "name": "yoke", + "version": "0.8.0", + "authors": "Manish Goregaokar ", + "repository": "https://github.com/unicode-org/icu4x", + "license": "Unicode-3.0", + "license_file": null, + "description": "Abstraction allowing borrowed data to be carried along with the backing data it borrows from" + }, { "name": "yoke-derive", "version": "0.7.5", @@ -5616,13 +5589,13 @@ "description": "Custom derive for the yoke crate" }, { - "name": "zerocopy", - "version": "0.7.35", - "authors": "Joshua Liebow-Feeser ", - "repository": "https://github.com/google/zerocopy", - "license": "Apache-2.0 OR BSD-2-Clause OR MIT", + "name": "yoke-derive", + "version": "0.8.0", + "authors": "Manish Goregaokar ", + "repository": "https://github.com/unicode-org/icu4x", + "license": "Unicode-3.0", "license_file": null, - "description": "Utilities for zero-copy parsing and serialization" + "description": "Custom derive for the yoke crate" }, { "name": "zerocopy", @@ -5633,15 +5606,6 @@ "license_file": null, "description": "Zerocopy makes zero-cost memory manipulation effortless. We write \"unsafe\" so you don't have to." }, - { - "name": "zerocopy-derive", - "version": "0.7.35", - "authors": "Joshua Liebow-Feeser ", - "repository": "https://github.com/google/zerocopy", - "license": "Apache-2.0 OR BSD-2-Clause OR MIT", - "license_file": null, - "description": "Custom derive for traits from the zerocopy crate" - }, { "name": "zerocopy-derive", "version": "0.8.25", @@ -5678,9 +5642,18 @@ "license_file": null, "description": "Securely clear secrets from memory with a simple trait built on stable Rust primitives which guarantee memory is zeroed using an operation will not be 'optimized away' by the compiler. Uses a portable pure Rust implementation that works everywhere, even WASM!" }, + { + "name": "zerotrie", + "version": "0.2.2", + "authors": "The ICU4X Project Developers", + "repository": "https://github.com/unicode-org/icu4x", + "license": "Unicode-3.0", + "license_file": null, + "description": "A data structure that efficiently maps strings to integers" + }, { "name": "zerovec", - "version": "0.10.4", + "version": "0.11.2", "authors": "The ICU4X Project Developers", "repository": "https://github.com/unicode-org/icu4x", "license": "Unicode-3.0", @@ -5689,22 +5662,13 @@ }, { "name": "zerovec-derive", - "version": "0.10.3", + "version": "0.11.1", "authors": "Manish Goregaokar ", "repository": "https://github.com/unicode-org/icu4x", "license": "Unicode-3.0", "license_file": null, "description": "Custom derive for the zerovec crate" }, - { - "name": "zip", - "version": "0.6.6", - "authors": "Mathijs van de Nes |Marli Frost |Ryan Levick ", - "repository": "https://github.com/zip-rs/zip.git", - "license": "MIT", - "license_file": null, - "description": "Library to support the reading and writing of zip files." - }, { "name": "zip", "version": "1.1.4", @@ -5714,6 +5678,33 @@ "license_file": null, "description": "Library to support the reading and writing of zip files." }, + { + "name": "zip", + "version": "4.1.0", + "authors": "Mathijs van de Nes |Marli Frost |Ryan Levick |Chris Hennick ", + "repository": "https://github.com/zip-rs/zip2.git", + "license": "MIT", + "license_file": null, + "description": "Library to support the reading and writing of zip files." + }, + { + "name": "zlib-rs", + "version": "0.5.1", + "authors": null, + "repository": "https://github.com/trifectatechfoundation/zlib-rs", + "license": "Zlib", + "license_file": null, + "description": "A memory-safe zlib implementation written in rust" + }, + { + "name": "zopfli", + "version": "0.8.2", + "authors": null, + "repository": "https://github.com/zopfli-rs/zopfli", + "license": "Apache-2.0", + "license_file": null, + "description": "A Rust implementation of the Zopfli compression algorithm." + }, { "name": "zstd", "version": "0.13.3", diff --git a/docs/development.md b/docs/development.md index a057b5c10..c963aec02 100644 --- a/docs/development.md +++ b/docs/development.md @@ -190,13 +190,10 @@ in the collection2.log file will also be printed on stdout. If ANKI_PROFILE_CODE is set, Python profiling data will be written on exit. -# Binary Bundles +# Installer/launcher -Anki's official binary packages are created with `./ninja bundle`. The bundling -process was created specifically for the official builds, and is provided as-is; -we are unfortunately not able to provide assistance with any issues you may run -into when using it. You'll need to run -`git submodule update --checkout qt/bundle/PyOxidizer` first. +- The anki-release package is created/published with the scripts in qt/release. +- The installer/launcher is created with the build scripts in qt/launcher/{platform}. ## Mixing development and study diff --git a/docs/windows.md b/docs/windows.md index aae9f6869..12f4e7c39 100644 --- a/docs/windows.md +++ b/docs/windows.md @@ -9,7 +9,12 @@ You must be running 64 bit Windows 10, version 1703 or newer. **Rustup**: As mentioned in development.md, rustup must be installed. If you're on -ARM Windows, you must set the default target to x86_64-pc-windows-msvc. +ARM Windows and install the ARM64 version of rust-up, from this project folder, +run + +``` +rustup target add x86_64-pc-windows-msvc +``` **Visual Studio**: diff --git a/ftl/extract-strings.py b/ftl/extract-strings.py deleted file mode 100644 index 7c3a63b3f..000000000 --- a/ftl/extract-strings.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python3 -# Copyright: Ankitects Pty Ltd and contributors -# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -""" -Tool to extract core strings and keys from .ftl files. -""" - -import glob -import json -import os - -from fluent.syntax import parse -from fluent.syntax.ast import Junk, Message -from fluent.syntax.serializer import serialize_element - -root = ".." -ftl_files = glob.glob(os.path.join(root, "ftl", "core", "*.ftl"), recursive=True) -keys_by_value: dict[str, list[str]] = {} - -for path in ftl_files: - obj = parse(open(path, encoding="utf8").read(), with_spans=False) - for ent in obj.body: - if isinstance(ent, Junk): - raise Exception(f"file had junk! {path} {ent}") - if isinstance(ent, Message): - key = ent.id.name - val = "".join(serialize_element(elem) for elem in ent.value.elements) - if val in keys_by_value: - print("duplicate found:", keys_by_value[val], key) - keys_by_value.setdefault(val, []).append(key) - -json.dump( - keys_by_value, open(os.path.join(root, "keys_by_value.json"), "w", encoding="utf8") -) -print("keys:", len(keys_by_value)) diff --git a/ftl/format.py b/ftl/format.py deleted file mode 100644 index a18aa5cfa..000000000 --- a/ftl/format.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env python3 -# Copyright: Ankitects Pty Ltd and contributors -# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -""" -Parse and re-serialize ftl files to get them in a consistent form. -""" - -import difflib -import glob -import os -from typing import List - -from compare_locales import parser -from compare_locales.checks.fluent import ReferenceMessageVisitor -from compare_locales.paths import File -from fluent.syntax import parse, serialize -from fluent.syntax.ast import Junk - - -def check_missing_terms(path: str) -> bool: - "True if file is ok." - file = File(path, os.path.basename(path)) - content = open(path, "rb").read() - p = parser.getParser(file.file) - p.readContents(content) - refList = p.parse() - - p.readContents(content) - for e in p.parse(): - ref_data = ReferenceMessageVisitor() - ref_data.visit(e.entry) - - for attr_or_val, refs in ref_data.entry_refs.items(): - for ref, ref_type in refs.items(): - if ref not in refList: - print(f"In {path}:{e}, missing '{ref}'") - return False - - return True - - -def check_file(path: str, fix: bool) -> bool: - "True if file is ok." - orig_text = open(path, encoding="utf8").read() - obj = parse(orig_text, with_spans=False) - # make sure there's no junk - for ent in obj.body: - if isinstance(ent, Junk): - raise Exception(f"file had junk! {path} {ent}") - # serialize - new_text = serialize(obj) - # make sure serializing did not introduce new junk - obj = parse(new_text, with_spans=False) - for ent in obj.body: - if isinstance(ent, Junk): - raise Exception(f"file introduced junk! {path} {ent}") - - if new_text == orig_text: - return check_missing_terms(path) - - if fix: - print(f"Fixing {path}") - open(path, "w", newline="\n", encoding="utf8").write(new_text) - return True - else: - print(f"Bad formatting in {path}") - print( - "\n".join( - difflib.unified_diff( - orig_text.splitlines(), - new_text.splitlines(), - fromfile="bad", - tofile="good", - lineterm="", - ) - ) - ) - return False - - -def check_files(files: List[str], fix: bool) -> bool: - "True if files ok." - - found_bad = False - for path in files: - ok = check_file(path, fix) - if not ok: - found_bad = True - return not found_bad - - -if __name__ == "__main__": - template_root = os.environ["BUILD_WORKSPACE_DIRECTORY"] - template_files = glob.glob( - os.path.join(template_root, "ftl", "*", "*.ftl"), recursive=True - ) - - check_files(template_files, fix=True) diff --git a/ftl/format_check.py b/ftl/format_check.py deleted file mode 100644 index 7faeacd2c..000000000 --- a/ftl/format_check.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright: Ankitects Pty Ltd and contributors -# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -import glob -import os -import sys - -import format - -template_root = os.path.dirname(sys.argv[1]) -template_files = glob.glob(os.path.join(template_root, "*", "*.ftl"), recursive=True) - -if not format.check_files(template_files, fix=False): - sys.exit(1) diff --git a/ninja b/ninja index 5feee474b..c44f8c330 100755 --- a/ninja +++ b/ninja @@ -8,7 +8,7 @@ else out="$BUILD_ROOT" fi export CARGO_TARGET_DIR=$out/rust -export RECONFIGURE_KEY="${MAC_X86};${SOURCEMAP};${HMR}" +export RECONFIGURE_KEY="${MAC_X86};${LIN_ARM64};${SOURCEMAP};${HMR}" if [ "$SKIP_RUNNER_BUILD" = "1" ]; then echo "Runner not rebuilt." diff --git a/pylib/anki/foreign_data/__init__.py b/pylib/anki/foreign_data/__init__.py index afcaf685e..8aac2cc42 100644 --- a/pylib/anki/foreign_data/__init__.py +++ b/pylib/anki/foreign_data/__init__.py @@ -1,8 +1,7 @@ # Copyright: Ankitects Pty Ltd and contributors # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -"""Helpers for serializing third-party collections to a common JSON form. -""" +"""Helpers for serializing third-party collections to a common JSON form.""" from __future__ import annotations diff --git a/pylib/anki/importing/noteimp.py b/pylib/anki/importing/noteimp.py index f69696ef8..f827a525a 100644 --- a/pylib/anki/importing/noteimp.py +++ b/pylib/anki/importing/noteimp.py @@ -167,9 +167,9 @@ class NoteImporter(Importer): firsts[fld0] = True # already exists? found = False - if csum in csums: + if csum in csums: # type: ignore[comparison-overlap] # csum is not a guarantee; have to check - for id in csums[csum]: + for id in csums[csum]: # type: ignore[index] flds = self.col.db.scalar("select flds from notes where id = ?", id) sflds = split_fields(flds) if fld0 == sflds[0]: diff --git a/pylib/anki/lang.py b/pylib/anki/lang.py index ca61f28bd..a0a6bf757 100644 --- a/pylib/anki/lang.py +++ b/pylib/anki/lang.py @@ -198,7 +198,9 @@ def get_def_lang(user_lang: str | None = None) -> tuple[int, str]: # getdefaultlocale() is deprecated since Python 3.11, but we need to keep using it as getlocale() behaves differently: https://bugs.python.org/issue38805 with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) - (sys_lang, enc) = locale.getdefaultlocale() + (sys_lang, enc) = ( + locale.getdefaultlocale() # pylint: disable=deprecated-method + ) except AttributeError: # this will return a different format on Windows (e.g. Italian_Italy), resulting in us falling back to en_US # further below diff --git a/pylib/anki/scheduler/base.py b/pylib/anki/scheduler/base.py index 83ef9d393..ffe4a6ef9 100644 --- a/pylib/anki/scheduler/base.py +++ b/pylib/anki/scheduler/base.py @@ -42,6 +42,7 @@ from anki.utils import ids2str, int_time class SchedulerBase(DeprecatedNamesMixin): "Actions shared between schedulers." + version = 0 def __init__(self, col: anki.collection.Collection) -> None: diff --git a/pylib/anki/stats.py b/pylib/anki/stats.py index 2f7de2e04..e6ca1cb97 100644 --- a/pylib/anki/stats.py +++ b/pylib/anki/stats.py @@ -174,7 +174,7 @@ from revlog where type != {REVLOG_RESCHED} and id > ? """ cards=cards, seconds=float(thetime) ) # again/pass count - b += "
" + "Again count: %s" % bold(failed) + b += "
" + "Again count: %s" % bold(str(failed)) if cards: b += " " + "(%s correct)" % bold( "%0.1f%%" % ((1 - failed / float(cards)) * 100) @@ -182,7 +182,10 @@ from revlog where type != {REVLOG_RESCHED} and id > ? """ # type breakdown b += "
" b += "Learn: %(a)s, Review: %(b)s, Relearn: %(c)s, Filtered: %(d)s" % dict( - a=bold(lrn), b=bold(rev), c=bold(relrn), d=bold(filt) + a=bold(str(lrn)), + b=bold(str(rev)), + c=bold(str(relrn)), + d=bold(str(filt)), ) # mature today mcnt, msum = self.col.db.first( diff --git a/pylib/anki/template.py b/pylib/anki/template.py index 118a23c6b..bc507f0f6 100644 --- a/pylib/anki/template.py +++ b/pylib/anki/template.py @@ -279,6 +279,7 @@ class TemplateRenderContext: @dataclass class TemplateRenderOutput: "Stores the rendered templates and extracted AV tags." + question_text: str answer_text: str question_av_tags: list[AVTag] diff --git a/pylib/anki/utils.py b/pylib/anki/utils.py index 1b4212620..46daa3b97 100644 --- a/pylib/anki/utils.py +++ b/pylib/anki/utils.py @@ -244,8 +244,8 @@ def call(argv: list[str], wait: bool = True, **kwargs: Any) -> int: # OS helpers ############################################################################## -is_mac = sys.platform.startswith("darwin") -is_win = sys.platform.startswith("win32") +is_mac = sys.platform == "darwin" +is_win = sys.platform == "win32" # also covers *BSD is_lin = not is_mac and not is_win is_gnome = ( diff --git a/pylib/hatch_build.py b/pylib/hatch_build.py new file mode 100644 index 000000000..c3539da56 --- /dev/null +++ b/pylib/hatch_build.py @@ -0,0 +1,42 @@ +# Copyright: Ankitects Pty Ltd and contributors +# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +import os +import platform +import sys +from pathlib import Path +from typing import Any, Dict + +from hatchling.builders.hooks.plugin.interface import BuildHookInterface + + +class CustomBuildHook(BuildHookInterface): + """Build hook to include compiled rsbridge from out/pylib.""" + + PLUGIN_NAME = "custom" + + def initialize(self, version: str, build_data: Dict[str, Any]) -> None: + """Initialize the build hook.""" + force_include = build_data.setdefault("force_include", {}) + + # Set platform-specific wheel tag + if not (platform_tag := os.environ.get("ANKI_WHEEL_TAG")): + # On Windows, uv invokes this build hook during the initial uv sync, + # when the tag has not been declared by our build script. + return + build_data.setdefault("tag", platform_tag) + + # Mark as non-pure Python since we include compiled extension + build_data["pure_python"] = False + + # Look for generated files in out/pylib/anki + project_root = Path(self.root).parent + generated_root = project_root / "out" / "pylib" / "anki" + + assert generated_root.exists(), "you should build with --wheel" + for path in generated_root.rglob("*"): + if path.is_file(): + relative_path = path.relative_to(generated_root) + # Place files under anki/ in the distribution + dist_path = "anki" / relative_path + force_include[str(path)] = str(dist_path) diff --git a/pylib/pyproject.toml b/pylib/pyproject.toml new file mode 100644 index 000000000..a12c6848b --- /dev/null +++ b/pylib/pyproject.toml @@ -0,0 +1,35 @@ +[project] +name = "anki" +# dynamic = ["version"] +version = "0.1.2" +requires-python = ">=3.9" +license = "AGPL-3.0-or-later" +dependencies = [ + "beautifulsoup4", + "decorator", + "markdown", + "orjson", + "protobuf>=4.21", + "requests[socks]", + "typing_extensions", + "types-protobuf", + "types-requests", + "types-orjson", + # platform-specific dependencies + "distro; sys_platform != 'darwin' and sys_platform != 'win32'", + "psutil; sys_platform == 'win32'", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["anki"] + +[tool.hatch.version] +source = "code" +path = "../python/version.py" + +[tool.hatch.build.hooks.custom] +path = "hatch_build.py" diff --git a/pylib/rsbridge/build.rs b/pylib/rsbridge/build.rs index d9809be73..2940563cb 100644 --- a/pylib/rsbridge/build.rs +++ b/pylib/rsbridge/build.rs @@ -1,21 +1,33 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -use std::path::Path; - fn main() { // macOS needs special link flags for PyO3 if cfg!(target_os = "macos") { println!("cargo:rustc-link-arg=-undefined"); println!("cargo:rustc-link-arg=dynamic_lookup"); - println!("cargo:rustc-link-arg=-mmacosx-version-min=10.13"); + println!("cargo:rustc-link-arg=-mmacosx-version-min=11"); } // On Windows, we need to be able to link with python3.lib if cfg!(windows) { - let lib_path = Path::new("../../out/extracted/python/libs") - .canonicalize() - .expect("libs"); - println!("cargo:rustc-link-search={}", lib_path.display()); + use std::process::Command; + + // Run Python to get sysconfig paths + let output = Command::new("../../out/pyenv/scripts/python") + .args([ + "-c", + "import sysconfig; print(sysconfig.get_paths()['stdlib'])", + ]) + .output() + .expect("Failed to execute Python"); + + let stdlib_path = String::from_utf8(output.stdout) + .expect("Failed to parse Python output") + .trim() + .to_string(); + + let libs_path = stdlib_path + "s"; + println!("cargo:rustc-link-search={}", libs_path); } } diff --git a/pyproject.toml b/pyproject.toml index d88101a1e..f5443e229 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,36 @@ -[tool.black] -target-version = ["py39", "py310", "py311", "py312"] -extend-exclude = "qt/bundle" +[project] +name = "anki-dev" +version = "0.0.0" +description = "Local-only environment" +requires-python = ">=3.9" +classifiers = ["Private :: Do Not Upload"] -[tool.pyright] -include = ["pylib/anki", "qt/aqt"] -stubPath = "" -pythonVersion = "3.9" +[dependency-groups] +dev = [ + "black", + "isort", + "mypy", + "mypy-protobuf", + "pylint", + "pytest", + "PyChromeDevTools", + "colorama", # for isort --color + "wheel", + "hatchling", # for type checking hatch_build.py files +] + +[project.optional-dependencies] +sphinx = [ + "sphinx", + "sphinx_rtd_theme", + "sphinx-autoapi", +] + +[tool.uv.workspace] +members = ["pylib", "qt"] + +[[tool.uv.index]] +name = "testpypi" +url = "https://test.pypi.org/simple/" +publish-url = "https://test.pypi.org/legacy/" +explicit = true diff --git a/python/README.md b/python/README.md deleted file mode 100644 index 574c231e9..000000000 --- a/python/README.md +++ /dev/null @@ -1,3 +0,0 @@ -- To achieve reproducible builds we use pip-tools to lock packages to a particular version - see - update_python_deps.sh -- write_wheel.py is used to generate our wheels. diff --git a/python/licenses.json b/python/licenses.json deleted file mode 100644 index 67098b005..000000000 --- a/python/licenses.json +++ /dev/null @@ -1,152 +0,0 @@ -[ - { - "License": "BSD License", - "Name": "Flask", - "Version": "1.1.2" - }, - { - "License": "MIT License", - "Name": "Flask-Cors", - "Version": "3.0.9" - }, - { - "License": "BSD License", - "Name": "Jinja2", - "Version": "2.11.2" - }, - { - "License": "BSD License", - "Name": "Markdown", - "Version": "3.3.3" - }, - { - "License": "BSD License", - "Name": "MarkupSafe", - "Version": "1.1.1" - }, - { - "License": "GPL v3", - "Name": "PyQt5", - "Version": "5.15.1" - }, - { - "License": "SIP", - "Name": "PyQt5-sip", - "Version": "12.8.1" - }, - { - "License": "GPL v3", - "Name": "PyQtWebEngine", - "Version": "5.15.1" - }, - { - "License": "BSD", - "Name": "PySocks", - "Version": "1.7.1" - }, - { - "License": "BSD License", - "Name": "Send2Trash", - "Version": "1.5.0" - }, - { - "License": "BSD License", - "Name": "Werkzeug", - "Version": "1.0.1" - }, - { - "License": "MIT License", - "Name": "attrs", - "Version": "20.3.0" - }, - { - "License": "MIT License", - "Name": "beautifulsoup4", - "Version": "4.9.3" - }, - { - "License": "Mozilla Public License 2.0 (MPL 2.0)", - "Name": "certifi", - "Version": "2020.11.8" - }, - { - "License": "GNU Library or Lesser General Public License (LGPL)", - "Name": "chardet", - "Version": "3.0.4" - }, - { - "License": "BSD License", - "Name": "click", - "Version": "7.1.2" - }, - { - "License": "BSD License", - "Name": "decorator", - "Version": "4.4.2" - }, - { - "License": "BSD License", - "Name": "idna", - "Version": "2.10" - }, - { - "License": "BSD License", - "Name": "itsdangerous", - "Version": "1.1.0" - }, - { - "License": "MIT License", - "Name": "jsonschema", - "Version": "3.2.0" - }, - { - "License": "Apache Software License, MIT License", - "Name": "orjson", - "Version": "3.4.3" - }, - { - "License": "3-Clause BSD License", - "Name": "protobuf", - "Version": "3.13.0" - }, - { - "License": "BSD License", - "Name": "psutil", - "Version": "5.7.3" - }, - { - "License": "MIT License", - "Name": "pyrsistent", - "Version": "0.17.3" - }, - { - "License": "Python Software Foundation License", - "Name": "pywin32", - "Version": "228" - }, - { - "License": "Apache Software License", - "Name": "requests", - "Version": "2.25.0" - }, - { - "License": "MIT License", - "Name": "six", - "Version": "1.15.0" - }, - { - "License": "MIT License", - "Name": "soupsieve", - "Version": "2.0.1" - }, - { - "License": "MIT License", - "Name": "urllib3", - "Version": "1.26.1" - }, - { - "License": "Zope Public License", - "Name": "waitress", - "Version": "1.4.4" - } -] diff --git a/python/licenses.sh b/python/licenses.sh deleted file mode 100755 index a7985a429..000000000 --- a/python/licenses.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# -# Install runtime requirements into a venv and extract their licenses. -# As Windows currently uses extra deps, running this on Windows should -# capture all packages. -# Run with 'bash licenses.sh' to update 'license.json' - -set -e - -# setup venv -python -m venv venv - -# build wheels -../bazel.bat --output_base=/c/bazel/anki/base build //pylib/anki:wheel //qt/aqt:wheel - -# install wheels, bound to constrained versions -venv/tools/pip install -c requirements.txt ../bazel-bin/pylib/anki/*.whl ../bazel-bin/qt/aqt/*.whl pip-licenses - -# dump licenses - ptable is a pip-licenses dep -venv/tools/pip-licenses --format=json --ignore-packages anki aqt pip-license PTable > licenses.json - -# clean up -rm -rf venv diff --git a/python/requirements.anki.in b/python/requirements.anki.in deleted file mode 100644 index 45796e6fe..000000000 --- a/python/requirements.anki.in +++ /dev/null @@ -1,9 +0,0 @@ -beautifulsoup4 -decorator -markdown -orjson -protobuf>=4.21 -requests[socks] -distro; sys_platform != "darwin" and sys_platform != "win32" -psutil; sys_platform == "win32" -typing_extensions diff --git a/python/requirements.aqt.in b/python/requirements.aqt.in deleted file mode 100644 index f18e65513..000000000 --- a/python/requirements.aqt.in +++ /dev/null @@ -1,10 +0,0 @@ -beautifulsoup4 -flask -flask_cors -jsonschema -requests -send2trash -waitress>=2.0.0 -psutil; sys.platform == "win32" -pywin32; sys.platform == "win32" -pip-system-certs diff --git a/python/requirements.base.in b/python/requirements.base.in deleted file mode 100644 index 705e50e83..000000000 --- a/python/requirements.base.in +++ /dev/null @@ -1,2 +0,0 @@ -pip-tools -colorama # required on windows diff --git a/python/requirements.base.txt b/python/requirements.base.txt deleted file mode 100644 index 24e3870f9..000000000 --- a/python/requirements.base.txt +++ /dev/null @@ -1,54 +0,0 @@ -build==1.2.1 \ - --hash=sha256:526263f4870c26f26c433545579475377b2b7588b6f1eac76a001e873ae3e19d \ - --hash=sha256:75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4 - # via pip-tools -click==8.1.7 \ - --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ - --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de - # via pip-tools -colorama==0.4.6 \ - --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ - --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 - # via -r requirements.base.in -importlib-metadata==8.4.0 \ - --hash=sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1 \ - --hash=sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5 - # via build -packaging==24.1 \ - --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \ - --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 - # via build -pip-tools==7.4.1 \ - --hash=sha256:4c690e5fbae2f21e87843e89c26191f0d9454f362d8acdbd695716493ec8b3a9 \ - --hash=sha256:864826f5073864450e24dbeeb85ce3920cdfb09848a3d69ebf537b521f14bcc9 - # via -r requirements.base.in -pyproject-hooks==1.1.0 \ - --hash=sha256:4b37730834edbd6bd37f26ece6b44802fb1c1ee2ece0e54ddff8bfc06db86965 \ - --hash=sha256:7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2 - # via - # build - # pip-tools -tomli==2.0.1 \ - --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ - --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f - # via - # build - # pip-tools -wheel==0.44.0 \ - --hash=sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f \ - --hash=sha256:a29c3f2817e95ab89aa4660681ad547c0e9547f20e75b0562fe7723c9a2a9d49 - # via pip-tools -zipp==3.20.1 \ - --hash=sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064 \ - --hash=sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b - # via importlib-metadata - -# The following packages are considered to be unsafe in a requirements file: -pip==24.2 \ - --hash=sha256:2cd581cf58ab7fcfca4ce8efa6dcacd0de5bf8d0a3eb9ec927e07405f4d9e2a2 \ - --hash=sha256:5b5e490b5e9cb275c879595064adce9ebd31b854e3e803740b72f9ccf34a45b8 - # via pip-tools -setuptools==74.1.1 \ - --hash=sha256:2353af060c06388be1cecbf5953dcdb1f38362f87a2356c480b6b4d5fcfc8847 \ - --hash=sha256:fc91b5f89e392ef5b77fe143b17e32f65d3024744fba66dc3afe07201684d766 - # via pip-tools diff --git a/python/requirements.bundle.in b/python/requirements.bundle.in deleted file mode 100644 index 80835ce6f..000000000 --- a/python/requirements.bundle.in +++ /dev/null @@ -1,8 +0,0 @@ -# currently broken in pyoxidizer -jsonschema<4.2 -setuptools<70 - --r requirements.base.in --r requirements.anki.in --r requirements.aqt.in - diff --git a/python/requirements.bundle.txt b/python/requirements.bundle.txt deleted file mode 100644 index f1af449ac..000000000 --- a/python/requirements.bundle.txt +++ /dev/null @@ -1,494 +0,0 @@ -attrs==24.2.0 \ - --hash=sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346 \ - --hash=sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2 - # via jsonschema -beautifulsoup4==4.12.3 \ - --hash=sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051 \ - --hash=sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed - # via - # -r requirements.anki.in - # -r requirements.aqt.in -blinker==1.8.2 \ - --hash=sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01 \ - --hash=sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83 - # via flask -build==1.2.1 \ - --hash=sha256:526263f4870c26f26c433545579475377b2b7588b6f1eac76a001e873ae3e19d \ - --hash=sha256:75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4 - # via pip-tools -certifi==2024.8.30 \ - --hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8 \ - --hash=sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9 - # via requests -charset-normalizer==3.3.2 \ - --hash=sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027 \ - --hash=sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087 \ - --hash=sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786 \ - --hash=sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8 \ - --hash=sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09 \ - --hash=sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185 \ - --hash=sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574 \ - --hash=sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e \ - --hash=sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519 \ - --hash=sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898 \ - --hash=sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269 \ - --hash=sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3 \ - --hash=sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f \ - --hash=sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6 \ - --hash=sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8 \ - --hash=sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a \ - --hash=sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73 \ - --hash=sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc \ - --hash=sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714 \ - --hash=sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2 \ - --hash=sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc \ - --hash=sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce \ - --hash=sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d \ - --hash=sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e \ - --hash=sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6 \ - --hash=sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269 \ - --hash=sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96 \ - --hash=sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d \ - --hash=sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a \ - --hash=sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4 \ - --hash=sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77 \ - --hash=sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d \ - --hash=sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0 \ - --hash=sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed \ - --hash=sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068 \ - --hash=sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac \ - --hash=sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25 \ - --hash=sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8 \ - --hash=sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab \ - --hash=sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26 \ - --hash=sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2 \ - --hash=sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db \ - --hash=sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f \ - --hash=sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5 \ - --hash=sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99 \ - --hash=sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c \ - --hash=sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d \ - --hash=sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811 \ - --hash=sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa \ - --hash=sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a \ - --hash=sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03 \ - --hash=sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b \ - --hash=sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04 \ - --hash=sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c \ - --hash=sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001 \ - --hash=sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458 \ - --hash=sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389 \ - --hash=sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99 \ - --hash=sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985 \ - --hash=sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537 \ - --hash=sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238 \ - --hash=sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f \ - --hash=sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d \ - --hash=sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796 \ - --hash=sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a \ - --hash=sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143 \ - --hash=sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8 \ - --hash=sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c \ - --hash=sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5 \ - --hash=sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5 \ - --hash=sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711 \ - --hash=sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4 \ - --hash=sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6 \ - --hash=sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c \ - --hash=sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7 \ - --hash=sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4 \ - --hash=sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b \ - --hash=sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae \ - --hash=sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12 \ - --hash=sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c \ - --hash=sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae \ - --hash=sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8 \ - --hash=sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887 \ - --hash=sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b \ - --hash=sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4 \ - --hash=sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f \ - --hash=sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5 \ - --hash=sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33 \ - --hash=sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519 \ - --hash=sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561 - # via requests -click==8.1.7 \ - --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ - --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de - # via - # flask - # pip-tools -colorama==0.4.6 \ - --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ - --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 - # via -r requirements.base.in -decorator==5.1.1 \ - --hash=sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330 \ - --hash=sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186 - # via -r requirements.anki.in -distro==1.9.0 ; sys_platform != "darwin" and sys_platform != "win32" \ - --hash=sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed \ - --hash=sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2 - # via -r requirements.anki.in -flask==3.0.3 \ - --hash=sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3 \ - --hash=sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842 - # via - # -r requirements.aqt.in - # flask-cors -flask-cors==6.0.0 \ - --hash=sha256:4592c1570246bf7beee96b74bc0adbbfcb1b0318f6ba05c412e8909eceec3393 \ - --hash=sha256:6332073356452343a8ccddbfec7befdc3fdd040141fe776ec9b94c262f058657 - # via -r requirements.aqt.in -idna==3.8 \ - --hash=sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac \ - --hash=sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603 - # via requests -importlib-metadata==8.4.0 \ - --hash=sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1 \ - --hash=sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5 - # via - # build - # flask - # markdown -itsdangerous==2.2.0 \ - --hash=sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef \ - --hash=sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173 - # via flask -jinja2==3.1.5 \ - --hash=sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb \ - --hash=sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb - # via flask -jsonschema==4.1.2 \ - --hash=sha256:166870c8ab27bd712a8627e0598de4685bd8d199c4d7bd7cacc3d941ba0c6ca0 \ - --hash=sha256:5c1a282ee6b74235057421fd0f766ac5f2972f77440927f6471c9e8493632fac - # via - # -r requirements.aqt.in - # -r requirements.bundle.in -markdown==3.7 \ - --hash=sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2 \ - --hash=sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803 - # via -r requirements.anki.in -markupsafe==2.1.5 \ - --hash=sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf \ - --hash=sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff \ - --hash=sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f \ - --hash=sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3 \ - --hash=sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532 \ - --hash=sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f \ - --hash=sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617 \ - --hash=sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df \ - --hash=sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4 \ - --hash=sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906 \ - --hash=sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f \ - --hash=sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4 \ - --hash=sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8 \ - --hash=sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371 \ - --hash=sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2 \ - --hash=sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465 \ - --hash=sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52 \ - --hash=sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6 \ - --hash=sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169 \ - --hash=sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad \ - --hash=sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2 \ - --hash=sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0 \ - --hash=sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029 \ - --hash=sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f \ - --hash=sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a \ - --hash=sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced \ - --hash=sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5 \ - --hash=sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c \ - --hash=sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf \ - --hash=sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9 \ - --hash=sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb \ - --hash=sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad \ - --hash=sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3 \ - --hash=sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1 \ - --hash=sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46 \ - --hash=sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc \ - --hash=sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a \ - --hash=sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee \ - --hash=sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900 \ - --hash=sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5 \ - --hash=sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea \ - --hash=sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f \ - --hash=sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5 \ - --hash=sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e \ - --hash=sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a \ - --hash=sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f \ - --hash=sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50 \ - --hash=sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a \ - --hash=sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b \ - --hash=sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4 \ - --hash=sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff \ - --hash=sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2 \ - --hash=sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46 \ - --hash=sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b \ - --hash=sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf \ - --hash=sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5 \ - --hash=sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5 \ - --hash=sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab \ - --hash=sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd \ - --hash=sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68 - # via - # jinja2 - # werkzeug -orjson==3.10.7 \ - --hash=sha256:084e537806b458911137f76097e53ce7bf5806dda33ddf6aaa66a028f8d43a23 \ - --hash=sha256:09b2d92fd95ad2402188cf51573acde57eb269eddabaa60f69ea0d733e789fe9 \ - --hash=sha256:0fa5886854673222618638c6df7718ea7fe2f3f2384c452c9ccedc70b4a510a5 \ - --hash=sha256:11748c135f281203f4ee695b7f80bb1358a82a63905f9f0b794769483ea854ad \ - --hash=sha256:1193b2416cbad1a769f868b1749535d5da47626ac29445803dae7cc64b3f5c98 \ - --hash=sha256:144888c76f8520e39bfa121b31fd637e18d4cc2f115727865fdf9fa325b10412 \ - --hash=sha256:1d9c0e733e02ada3ed6098a10a8ee0052dd55774de3d9110d29868d24b17faa1 \ - --hash=sha256:23820a1563a1d386414fef15c249040042b8e5d07b40ab3fe3efbfbbcbcb8864 \ - --hash=sha256:33cfb96c24034a878d83d1a9415799a73dc77480e6c40417e5dda0710d559ee6 \ - --hash=sha256:348bdd16b32556cf8d7257b17cf2bdb7ab7976af4af41ebe79f9796c218f7e91 \ - --hash=sha256:34a566f22c28222b08875b18b0dfbf8a947e69df21a9ed5c51a6bf91cfb944ac \ - --hash=sha256:3dcfbede6737fdbef3ce9c37af3fb6142e8e1ebc10336daa05872bfb1d87839c \ - --hash=sha256:430ee4d85841e1483d487e7b81401785a5dfd69db5de01314538f31f8fbf7ee1 \ - --hash=sha256:44a96f2d4c3af51bfac6bc4ef7b182aa33f2f054fd7f34cc0ee9a320d051d41f \ - --hash=sha256:479fd0844ddc3ca77e0fd99644c7fe2de8e8be1efcd57705b5c92e5186e8a250 \ - --hash=sha256:480f455222cb7a1dea35c57a67578848537d2602b46c464472c995297117fa09 \ - --hash=sha256:4829cf2195838e3f93b70fd3b4292156fc5e097aac3739859ac0dcc722b27ac0 \ - --hash=sha256:4b6146e439af4c2472c56f8540d799a67a81226e11992008cb47e1267a9b3225 \ - --hash=sha256:4e6c3da13e5a57e4b3dca2de059f243ebec705857522f188f0180ae88badd354 \ - --hash=sha256:5b24a579123fa884f3a3caadaed7b75eb5715ee2b17ab5c66ac97d29b18fe57f \ - --hash=sha256:6b0dd04483499d1de9c8f6203f8975caf17a6000b9c0c54630cef02e44ee624e \ - --hash=sha256:6ea2b2258eff652c82652d5e0f02bd5e0463a6a52abb78e49ac288827aaa1469 \ - --hash=sha256:7122a99831f9e7fe977dc45784d3b2edc821c172d545e6420c375e5a935f5a1c \ - --hash=sha256:74f4544f5a6405b90da8ea724d15ac9c36da4d72a738c64685003337401f5c12 \ - --hash=sha256:75ef0640403f945f3a1f9f6400686560dbfb0fb5b16589ad62cd477043c4eee3 \ - --hash=sha256:76ac14cd57df0572453543f8f2575e2d01ae9e790c21f57627803f5e79b0d3c3 \ - --hash=sha256:77d325ed866876c0fa6492598ec01fe30e803272a6e8b10e992288b009cbe149 \ - --hash=sha256:7c4c17f8157bd520cdb7195f75ddbd31671997cbe10aee559c2d613592e7d7eb \ - --hash=sha256:7db8539039698ddfb9a524b4dd19508256107568cdad24f3682d5773e60504a2 \ - --hash=sha256:8272527d08450ab16eb405f47e0f4ef0e5ff5981c3d82afe0efd25dcbef2bcd2 \ - --hash=sha256:82763b46053727a7168d29c772ed5c870fdae2f61aa8a25994c7984a19b1021f \ - --hash=sha256:8a9c9b168b3a19e37fe2778c0003359f07822c90fdff8f98d9d2a91b3144d8e0 \ - --hash=sha256:8de062de550f63185e4c1c54151bdddfc5625e37daf0aa1e75d2a1293e3b7d9a \ - --hash=sha256:974683d4618c0c7dbf4f69c95a979734bf183d0658611760017f6e70a145af58 \ - --hash=sha256:9ea2c232deedcb605e853ae1db2cc94f7390ac776743b699b50b071b02bea6fe \ - --hash=sha256:a0c6a008e91d10a2564edbb6ee5069a9e66df3fbe11c9a005cb411f441fd2c09 \ - --hash=sha256:a763bc0e58504cc803739e7df040685816145a6f3c8a589787084b54ebc9f16e \ - --hash=sha256:a7e19150d215c7a13f39eb787d84db274298d3f83d85463e61d277bbd7f401d2 \ - --hash=sha256:ac7cf6222b29fbda9e3a472b41e6a5538b48f2c8f99261eecd60aafbdb60690c \ - --hash=sha256:b48b3db6bb6e0a08fa8c83b47bc169623f801e5cc4f24442ab2b6617da3b5313 \ - --hash=sha256:b58d3795dafa334fc8fd46f7c5dc013e6ad06fd5b9a4cc98cb1456e7d3558bd6 \ - --hash=sha256:bdbb61dcc365dd9be94e8f7df91975edc9364d6a78c8f7adb69c1cdff318ec93 \ - --hash=sha256:bf6ba8ebc8ef5792e2337fb0419f8009729335bb400ece005606336b7fd7bab7 \ - --hash=sha256:c31008598424dfbe52ce8c5b47e0752dca918a4fdc4a2a32004efd9fab41d866 \ - --hash=sha256:cb61938aec8b0ffb6eef484d480188a1777e67b05d58e41b435c74b9d84e0b9c \ - --hash=sha256:d2d9f990623f15c0ae7ac608103c33dfe1486d2ed974ac3f40b693bad1a22a7b \ - --hash=sha256:d352ee8ac1926d6193f602cbe36b1643bbd1bbcb25e3c1a657a4390f3000c9a5 \ - --hash=sha256:d374d36726746c81a49f3ff8daa2898dccab6596864ebe43d50733275c629175 \ - --hash=sha256:de817e2f5fc75a9e7dd350c4b0f54617b280e26d1631811a43e7e968fa71e3e9 \ - --hash=sha256:e724cebe1fadc2b23c6f7415bad5ee6239e00a69f30ee423f319c6af70e2a5c0 \ - --hash=sha256:e72591bcfe7512353bd609875ab38050efe3d55e18934e2f18950c108334b4ff \ - --hash=sha256:e76be12658a6fa376fcd331b1ea4e58f5a06fd0220653450f0d415b8fd0fbe20 \ - --hash=sha256:eb8d384a24778abf29afb8e41d68fdd9a156cf6e5390c04cc07bbc24b89e98b5 \ - --hash=sha256:ed350d6978d28b92939bfeb1a0570c523f6170efc3f0a0ef1f1df287cd4f4960 \ - --hash=sha256:eef44224729e9525d5261cc8d28d6b11cafc90e6bd0be2157bde69a52ec83024 \ - --hash=sha256:f4db56635b58cd1a200b0a23744ff44206ee6aa428185e2b6c4a65b3197abdcd \ - --hash=sha256:fdf5197a21dd660cf19dfd2a3ce79574588f8f5e2dbf21bda9ee2d2b46924d84 - # via -r requirements.anki.in -packaging==24.1 \ - --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \ - --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 - # via build -pip-system-certs==4.0 \ - --hash=sha256:47202b9403a6f40783a9674bbc8873f5fc86544ec01a49348fa913e99e2ff68b \ - --hash=sha256:db8e6a31388d9795ec9139957df1a89fa5274fb66164456fd091a5d3e94c350c - # via -r requirements.aqt.in -pip-tools==7.4.1 \ - --hash=sha256:4c690e5fbae2f21e87843e89c26191f0d9454f362d8acdbd695716493ec8b3a9 \ - --hash=sha256:864826f5073864450e24dbeeb85ce3920cdfb09848a3d69ebf537b521f14bcc9 - # via -r requirements.base.in -protobuf==5.28.2 \ - --hash=sha256:2c69461a7fcc8e24be697624c09a839976d82ae75062b11a0972e41fd2cd9132 \ - --hash=sha256:35cfcb15f213449af7ff6198d6eb5f739c37d7e4f1c09b5d0641babf2cc0c68f \ - --hash=sha256:52235802093bd8a2811abbe8bf0ab9c5f54cca0a751fdd3f6ac2a21438bffece \ - --hash=sha256:59379674ff119717404f7454647913787034f03fe7049cbef1d74a97bb4593f0 \ - --hash=sha256:5e8a95246d581eef20471b5d5ba010d55f66740942b95ba9b872d918c459452f \ - --hash=sha256:87317e9bcda04a32f2ee82089a204d3a2f0d3c8aeed16568c7daf4756e4f1fe0 \ - --hash=sha256:8ddc60bf374785fb7cb12510b267f59067fa10087325b8e1855b898a0d81d276 \ - --hash=sha256:a8b9403fc70764b08d2f593ce44f1d2920c5077bf7d311fefec999f8c40f78b7 \ - --hash=sha256:c0ea0123dac3399a2eeb1a1443d82b7afc9ff40241433296769f7da42d142ec3 \ - --hash=sha256:ca53faf29896c526863366a52a8f4d88e69cd04ec9571ed6082fa117fac3ab36 \ - --hash=sha256:eeea10f3dc0ac7e6b4933d32db20662902b4ab81bf28df12218aa389e9c2102d - # via -r requirements.anki.in -pyproject-hooks==1.1.0 \ - --hash=sha256:4b37730834edbd6bd37f26ece6b44802fb1c1ee2ece0e54ddff8bfc06db86965 \ - --hash=sha256:7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2 - # via - # build - # pip-tools -pyrsistent==0.20.0 \ - --hash=sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f \ - --hash=sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e \ - --hash=sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958 \ - --hash=sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34 \ - --hash=sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca \ - --hash=sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d \ - --hash=sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d \ - --hash=sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4 \ - --hash=sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714 \ - --hash=sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf \ - --hash=sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee \ - --hash=sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8 \ - --hash=sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224 \ - --hash=sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d \ - --hash=sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054 \ - --hash=sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656 \ - --hash=sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7 \ - --hash=sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423 \ - --hash=sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce \ - --hash=sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e \ - --hash=sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3 \ - --hash=sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0 \ - --hash=sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f \ - --hash=sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b \ - --hash=sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce \ - --hash=sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a \ - --hash=sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174 \ - --hash=sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86 \ - --hash=sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f \ - --hash=sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b \ - --hash=sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98 \ - --hash=sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022 - # via jsonschema -pysocks==1.7.1 \ - --hash=sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299 \ - --hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \ - --hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0 - # via requests -requests==2.32.4 \ - --hash=sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c \ - --hash=sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422 - # via - # -r requirements.anki.in - # -r requirements.aqt.in -send2trash==1.8.3 \ - --hash=sha256:0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9 \ - --hash=sha256:b18e7a3966d99871aefeb00cfbcfdced55ce4871194810fc71f4aa484b953abf - # via -r requirements.aqt.in -soupsieve==2.6 \ - --hash=sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb \ - --hash=sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9 - # via beautifulsoup4 -tomli==2.0.1 \ - --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ - --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f - # via - # build - # pip-tools -typing-extensions==4.13.2 \ - --hash=sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c \ - --hash=sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef - # via -r requirements.anki.in -urllib3==2.2.2 \ - --hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \ - --hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168 - # via requests -waitress==3.0.1 \ - --hash=sha256:26cdbc593093a15119351690752c99adc13cbc6786d75f7b6341d1234a3730ac \ - --hash=sha256:ef0c1f020d9f12a515c4ec65c07920a702613afcad1dbfdc3bcec256b6c072b3 - # via -r requirements.aqt.in -werkzeug==3.0.6 \ - --hash=sha256:1bc0c2310d2fbb07b1dd1105eba2f7af72f322e1e455f2f93c993bee8c8a5f17 \ - --hash=sha256:a8dd59d4de28ca70471a34cba79bed5f7ef2e036a76b3ab0835474246eb41f8d - # via - # flask - # flask-cors -wheel==0.44.0 \ - --hash=sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f \ - --hash=sha256:a29c3f2817e95ab89aa4660681ad547c0e9547f20e75b0562fe7723c9a2a9d49 - # via pip-tools -wrapt==1.16.0 \ - --hash=sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc \ - --hash=sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81 \ - --hash=sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09 \ - --hash=sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e \ - --hash=sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca \ - --hash=sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0 \ - --hash=sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb \ - --hash=sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487 \ - --hash=sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40 \ - --hash=sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c \ - --hash=sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060 \ - --hash=sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202 \ - --hash=sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41 \ - --hash=sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9 \ - --hash=sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b \ - --hash=sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664 \ - --hash=sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d \ - --hash=sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362 \ - --hash=sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00 \ - --hash=sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc \ - --hash=sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1 \ - --hash=sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267 \ - --hash=sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956 \ - --hash=sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966 \ - --hash=sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1 \ - --hash=sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228 \ - --hash=sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72 \ - --hash=sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d \ - --hash=sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292 \ - --hash=sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0 \ - --hash=sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0 \ - --hash=sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36 \ - --hash=sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c \ - --hash=sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5 \ - --hash=sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f \ - --hash=sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73 \ - --hash=sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b \ - --hash=sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2 \ - --hash=sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593 \ - --hash=sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39 \ - --hash=sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389 \ - --hash=sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf \ - --hash=sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf \ - --hash=sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89 \ - --hash=sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c \ - --hash=sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c \ - --hash=sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f \ - --hash=sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440 \ - --hash=sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465 \ - --hash=sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136 \ - --hash=sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b \ - --hash=sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8 \ - --hash=sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3 \ - --hash=sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8 \ - --hash=sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6 \ - --hash=sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e \ - --hash=sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f \ - --hash=sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c \ - --hash=sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e \ - --hash=sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8 \ - --hash=sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2 \ - --hash=sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020 \ - --hash=sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35 \ - --hash=sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d \ - --hash=sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3 \ - --hash=sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537 \ - --hash=sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809 \ - --hash=sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d \ - --hash=sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a \ - --hash=sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4 - # via pip-system-certs -zipp==3.20.1 \ - --hash=sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064 \ - --hash=sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b - # via importlib-metadata - -# The following packages are considered to be unsafe in a requirements file: -pip==24.2 \ - --hash=sha256:2cd581cf58ab7fcfca4ce8efa6dcacd0de5bf8d0a3eb9ec927e07405f4d9e2a2 \ - --hash=sha256:5b5e490b5e9cb275c879595064adce9ebd31b854e3e803740b72f9ccf34a45b8 - # via pip-tools -setuptools==69.5.1 \ - --hash=sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987 \ - --hash=sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32 - # via - # -r requirements.bundle.in - # pip-tools diff --git a/python/requirements.dev.in b/python/requirements.dev.in deleted file mode 100644 index c981f127c..000000000 --- a/python/requirements.dev.in +++ /dev/null @@ -1,27 +0,0 @@ --r requirements.base.in --r requirements.anki.in --r requirements.aqt.in - -black -compare-locales -isort -mock -mypy -mypy-protobuf -pip-tools -pylint -pytest -PyChromeDevTools -fluent.syntax -types-decorator -types-flask -types-flask-cors -types-markdown -types-orjson -types-protobuf -types-requests -types-waitress - -# transitive windows dependencies -atomicwrites -colorama diff --git a/python/requirements.dev.txt b/python/requirements.dev.txt deleted file mode 100644 index 9b96a1cd4..000000000 --- a/python/requirements.dev.txt +++ /dev/null @@ -1,715 +0,0 @@ -astroid==3.2.4 \ - --hash=sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a \ - --hash=sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25 - # via pylint -atomicwrites==1.4.1 \ - --hash=sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11 - # via -r requirements.dev.in -attrs==24.2.0 \ - --hash=sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346 \ - --hash=sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2 - # via jsonschema -beautifulsoup4==4.12.3 \ - --hash=sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051 \ - --hash=sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed - # via - # -r requirements.anki.in - # -r requirements.aqt.in -black==24.8.0 \ - --hash=sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6 \ - --hash=sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e \ - --hash=sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f \ - --hash=sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018 \ - --hash=sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e \ - --hash=sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd \ - --hash=sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4 \ - --hash=sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed \ - --hash=sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2 \ - --hash=sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42 \ - --hash=sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af \ - --hash=sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb \ - --hash=sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368 \ - --hash=sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb \ - --hash=sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af \ - --hash=sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed \ - --hash=sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47 \ - --hash=sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2 \ - --hash=sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a \ - --hash=sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c \ - --hash=sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920 \ - --hash=sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1 - # via -r requirements.dev.in -blinker==1.8.2 \ - --hash=sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01 \ - --hash=sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83 - # via flask -build==1.2.1 \ - --hash=sha256:526263f4870c26f26c433545579475377b2b7588b6f1eac76a001e873ae3e19d \ - --hash=sha256:75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4 - # via pip-tools -certifi==2024.8.30 \ - --hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8 \ - --hash=sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9 - # via requests -charset-normalizer==3.3.2 \ - --hash=sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027 \ - --hash=sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087 \ - --hash=sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786 \ - --hash=sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8 \ - --hash=sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09 \ - --hash=sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185 \ - --hash=sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574 \ - --hash=sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e \ - --hash=sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519 \ - --hash=sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898 \ - --hash=sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269 \ - --hash=sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3 \ - --hash=sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f \ - --hash=sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6 \ - --hash=sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8 \ - --hash=sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a \ - --hash=sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73 \ - --hash=sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc \ - --hash=sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714 \ - --hash=sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2 \ - --hash=sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc \ - --hash=sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce \ - --hash=sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d \ - --hash=sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e \ - --hash=sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6 \ - --hash=sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269 \ - --hash=sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96 \ - --hash=sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d \ - --hash=sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a \ - --hash=sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4 \ - --hash=sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77 \ - --hash=sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d \ - --hash=sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0 \ - --hash=sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed \ - --hash=sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068 \ - --hash=sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac \ - --hash=sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25 \ - --hash=sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8 \ - --hash=sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab \ - --hash=sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26 \ - --hash=sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2 \ - --hash=sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db \ - --hash=sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f \ - --hash=sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5 \ - --hash=sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99 \ - --hash=sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c \ - --hash=sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d \ - --hash=sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811 \ - --hash=sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa \ - --hash=sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a \ - --hash=sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03 \ - --hash=sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b \ - --hash=sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04 \ - --hash=sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c \ - --hash=sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001 \ - --hash=sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458 \ - --hash=sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389 \ - --hash=sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99 \ - --hash=sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985 \ - --hash=sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537 \ - --hash=sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238 \ - --hash=sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f \ - --hash=sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d \ - --hash=sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796 \ - --hash=sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a \ - --hash=sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143 \ - --hash=sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8 \ - --hash=sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c \ - --hash=sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5 \ - --hash=sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5 \ - --hash=sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711 \ - --hash=sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4 \ - --hash=sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6 \ - --hash=sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c \ - --hash=sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7 \ - --hash=sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4 \ - --hash=sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b \ - --hash=sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae \ - --hash=sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12 \ - --hash=sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c \ - --hash=sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae \ - --hash=sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8 \ - --hash=sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887 \ - --hash=sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b \ - --hash=sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4 \ - --hash=sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f \ - --hash=sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5 \ - --hash=sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33 \ - --hash=sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519 \ - --hash=sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561 - # via requests -click==8.1.7 \ - --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ - --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de - # via - # black - # flask - # pip-tools -colorama==0.4.6 \ - --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ - --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 - # via - # -r requirements.base.in - # -r requirements.dev.in -compare-locales==9.0.4 \ - --hash=sha256:73d0d384aefa0bc96f5fd8521c08c8bb89b16a37316701323a77960accabd551 \ - --hash=sha256:933d2b6e20f460d3ac2d3176295684505a42085b25e6c31944fcafbaf52f1cc0 - # via -r requirements.dev.in -decorator==5.1.1 \ - --hash=sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330 \ - --hash=sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186 - # via -r requirements.anki.in -dill==0.3.8 \ - --hash=sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca \ - --hash=sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7 - # via pylint -distro==1.9.0 ; sys_platform != "darwin" and sys_platform != "win32" \ - --hash=sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed \ - --hash=sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2 - # via -r requirements.anki.in -exceptiongroup==1.2.2 \ - --hash=sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b \ - --hash=sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc - # via pytest -flask==3.0.3 \ - --hash=sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3 \ - --hash=sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842 - # via - # -r requirements.aqt.in - # flask-cors - # types-flask-cors -flask-cors==6.0.0 \ - --hash=sha256:4592c1570246bf7beee96b74bc0adbbfcb1b0318f6ba05c412e8909eceec3393 \ - --hash=sha256:6332073356452343a8ccddbfec7befdc3fdd040141fe776ec9b94c262f058657 - # via -r requirements.aqt.in -fluent-syntax==0.19.0 \ - --hash=sha256:920326d7f46864b9758f0044e9968e3112198bc826acee16ddd8f11d359004fd \ - --hash=sha256:b352b3475fac6c6ed5f06527921f432aac073d764445508ee5218aeccc7cc5c4 - # via - # -r requirements.dev.in - # compare-locales -idna==3.8 \ - --hash=sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac \ - --hash=sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603 - # via requests -importlib-metadata==8.4.0 \ - --hash=sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1 \ - --hash=sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5 - # via - # build - # flask - # markdown -iniconfig==2.0.0 \ - --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ - --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 - # via pytest -isort==5.13.2 \ - --hash=sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109 \ - --hash=sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6 - # via - # -r requirements.dev.in - # pylint -itsdangerous==2.2.0 \ - --hash=sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef \ - --hash=sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173 - # via flask -jinja2==3.1.5 \ - --hash=sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb \ - --hash=sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb - # via flask -jsonschema==4.1.2 \ - --hash=sha256:166870c8ab27bd712a8627e0598de4685bd8d199c4d7bd7cacc3d941ba0c6ca0 \ - --hash=sha256:5c1a282ee6b74235057421fd0f766ac5f2972f77440927f6471c9e8493632fac - # via -r requirements.aqt.in -markdown==3.7 \ - --hash=sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2 \ - --hash=sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803 - # via -r requirements.anki.in -markupsafe==2.1.5 \ - --hash=sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf \ - --hash=sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff \ - --hash=sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f \ - --hash=sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3 \ - --hash=sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532 \ - --hash=sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f \ - --hash=sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617 \ - --hash=sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df \ - --hash=sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4 \ - --hash=sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906 \ - --hash=sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f \ - --hash=sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4 \ - --hash=sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8 \ - --hash=sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371 \ - --hash=sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2 \ - --hash=sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465 \ - --hash=sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52 \ - --hash=sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6 \ - --hash=sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169 \ - --hash=sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad \ - --hash=sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2 \ - --hash=sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0 \ - --hash=sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029 \ - --hash=sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f \ - --hash=sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a \ - --hash=sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced \ - --hash=sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5 \ - --hash=sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c \ - --hash=sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf \ - --hash=sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9 \ - --hash=sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb \ - --hash=sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad \ - --hash=sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3 \ - --hash=sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1 \ - --hash=sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46 \ - --hash=sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc \ - --hash=sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a \ - --hash=sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee \ - --hash=sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900 \ - --hash=sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5 \ - --hash=sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea \ - --hash=sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f \ - --hash=sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5 \ - --hash=sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e \ - --hash=sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a \ - --hash=sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f \ - --hash=sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50 \ - --hash=sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a \ - --hash=sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b \ - --hash=sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4 \ - --hash=sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff \ - --hash=sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2 \ - --hash=sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46 \ - --hash=sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b \ - --hash=sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf \ - --hash=sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5 \ - --hash=sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5 \ - --hash=sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab \ - --hash=sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd \ - --hash=sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68 - # via - # jinja2 - # werkzeug -mccabe==0.7.0 \ - --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \ - --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e - # via pylint -mock==5.1.0 \ - --hash=sha256:18c694e5ae8a208cdb3d2c20a993ca1a7b0efa258c247a1e565150f477f83744 \ - --hash=sha256:5e96aad5ccda4718e0a229ed94b2024df75cc2d55575ba5762d31f5767b8767d - # via -r requirements.dev.in -mypy==1.11.2 \ - --hash=sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36 \ - --hash=sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce \ - --hash=sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6 \ - --hash=sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b \ - --hash=sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca \ - --hash=sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24 \ - --hash=sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383 \ - --hash=sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7 \ - --hash=sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86 \ - --hash=sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d \ - --hash=sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4 \ - --hash=sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8 \ - --hash=sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987 \ - --hash=sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385 \ - --hash=sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79 \ - --hash=sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef \ - --hash=sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6 \ - --hash=sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70 \ - --hash=sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca \ - --hash=sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70 \ - --hash=sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12 \ - --hash=sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104 \ - --hash=sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a \ - --hash=sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318 \ - --hash=sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1 \ - --hash=sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b \ - --hash=sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d - # via -r requirements.dev.in -mypy-extensions==1.0.0 \ - --hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \ - --hash=sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782 - # via - # black - # mypy -mypy-protobuf==3.6.0 \ - --hash=sha256:02f242eb3409f66889f2b1a3aa58356ec4d909cdd0f93115622e9e70366eca3c \ - --hash=sha256:56176e4d569070e7350ea620262478b49b7efceba4103d468448f1d21492fd6c - # via -r requirements.dev.in -orjson==3.10.7 \ - --hash=sha256:084e537806b458911137f76097e53ce7bf5806dda33ddf6aaa66a028f8d43a23 \ - --hash=sha256:09b2d92fd95ad2402188cf51573acde57eb269eddabaa60f69ea0d733e789fe9 \ - --hash=sha256:0fa5886854673222618638c6df7718ea7fe2f3f2384c452c9ccedc70b4a510a5 \ - --hash=sha256:11748c135f281203f4ee695b7f80bb1358a82a63905f9f0b794769483ea854ad \ - --hash=sha256:1193b2416cbad1a769f868b1749535d5da47626ac29445803dae7cc64b3f5c98 \ - --hash=sha256:144888c76f8520e39bfa121b31fd637e18d4cc2f115727865fdf9fa325b10412 \ - --hash=sha256:1d9c0e733e02ada3ed6098a10a8ee0052dd55774de3d9110d29868d24b17faa1 \ - --hash=sha256:23820a1563a1d386414fef15c249040042b8e5d07b40ab3fe3efbfbbcbcb8864 \ - --hash=sha256:33cfb96c24034a878d83d1a9415799a73dc77480e6c40417e5dda0710d559ee6 \ - --hash=sha256:348bdd16b32556cf8d7257b17cf2bdb7ab7976af4af41ebe79f9796c218f7e91 \ - --hash=sha256:34a566f22c28222b08875b18b0dfbf8a947e69df21a9ed5c51a6bf91cfb944ac \ - --hash=sha256:3dcfbede6737fdbef3ce9c37af3fb6142e8e1ebc10336daa05872bfb1d87839c \ - --hash=sha256:430ee4d85841e1483d487e7b81401785a5dfd69db5de01314538f31f8fbf7ee1 \ - --hash=sha256:44a96f2d4c3af51bfac6bc4ef7b182aa33f2f054fd7f34cc0ee9a320d051d41f \ - --hash=sha256:479fd0844ddc3ca77e0fd99644c7fe2de8e8be1efcd57705b5c92e5186e8a250 \ - --hash=sha256:480f455222cb7a1dea35c57a67578848537d2602b46c464472c995297117fa09 \ - --hash=sha256:4829cf2195838e3f93b70fd3b4292156fc5e097aac3739859ac0dcc722b27ac0 \ - --hash=sha256:4b6146e439af4c2472c56f8540d799a67a81226e11992008cb47e1267a9b3225 \ - --hash=sha256:4e6c3da13e5a57e4b3dca2de059f243ebec705857522f188f0180ae88badd354 \ - --hash=sha256:5b24a579123fa884f3a3caadaed7b75eb5715ee2b17ab5c66ac97d29b18fe57f \ - --hash=sha256:6b0dd04483499d1de9c8f6203f8975caf17a6000b9c0c54630cef02e44ee624e \ - --hash=sha256:6ea2b2258eff652c82652d5e0f02bd5e0463a6a52abb78e49ac288827aaa1469 \ - --hash=sha256:7122a99831f9e7fe977dc45784d3b2edc821c172d545e6420c375e5a935f5a1c \ - --hash=sha256:74f4544f5a6405b90da8ea724d15ac9c36da4d72a738c64685003337401f5c12 \ - --hash=sha256:75ef0640403f945f3a1f9f6400686560dbfb0fb5b16589ad62cd477043c4eee3 \ - --hash=sha256:76ac14cd57df0572453543f8f2575e2d01ae9e790c21f57627803f5e79b0d3c3 \ - --hash=sha256:77d325ed866876c0fa6492598ec01fe30e803272a6e8b10e992288b009cbe149 \ - --hash=sha256:7c4c17f8157bd520cdb7195f75ddbd31671997cbe10aee559c2d613592e7d7eb \ - --hash=sha256:7db8539039698ddfb9a524b4dd19508256107568cdad24f3682d5773e60504a2 \ - --hash=sha256:8272527d08450ab16eb405f47e0f4ef0e5ff5981c3d82afe0efd25dcbef2bcd2 \ - --hash=sha256:82763b46053727a7168d29c772ed5c870fdae2f61aa8a25994c7984a19b1021f \ - --hash=sha256:8a9c9b168b3a19e37fe2778c0003359f07822c90fdff8f98d9d2a91b3144d8e0 \ - --hash=sha256:8de062de550f63185e4c1c54151bdddfc5625e37daf0aa1e75d2a1293e3b7d9a \ - --hash=sha256:974683d4618c0c7dbf4f69c95a979734bf183d0658611760017f6e70a145af58 \ - --hash=sha256:9ea2c232deedcb605e853ae1db2cc94f7390ac776743b699b50b071b02bea6fe \ - --hash=sha256:a0c6a008e91d10a2564edbb6ee5069a9e66df3fbe11c9a005cb411f441fd2c09 \ - --hash=sha256:a763bc0e58504cc803739e7df040685816145a6f3c8a589787084b54ebc9f16e \ - --hash=sha256:a7e19150d215c7a13f39eb787d84db274298d3f83d85463e61d277bbd7f401d2 \ - --hash=sha256:ac7cf6222b29fbda9e3a472b41e6a5538b48f2c8f99261eecd60aafbdb60690c \ - --hash=sha256:b48b3db6bb6e0a08fa8c83b47bc169623f801e5cc4f24442ab2b6617da3b5313 \ - --hash=sha256:b58d3795dafa334fc8fd46f7c5dc013e6ad06fd5b9a4cc98cb1456e7d3558bd6 \ - --hash=sha256:bdbb61dcc365dd9be94e8f7df91975edc9364d6a78c8f7adb69c1cdff318ec93 \ - --hash=sha256:bf6ba8ebc8ef5792e2337fb0419f8009729335bb400ece005606336b7fd7bab7 \ - --hash=sha256:c31008598424dfbe52ce8c5b47e0752dca918a4fdc4a2a32004efd9fab41d866 \ - --hash=sha256:cb61938aec8b0ffb6eef484d480188a1777e67b05d58e41b435c74b9d84e0b9c \ - --hash=sha256:d2d9f990623f15c0ae7ac608103c33dfe1486d2ed974ac3f40b693bad1a22a7b \ - --hash=sha256:d352ee8ac1926d6193f602cbe36b1643bbd1bbcb25e3c1a657a4390f3000c9a5 \ - --hash=sha256:d374d36726746c81a49f3ff8daa2898dccab6596864ebe43d50733275c629175 \ - --hash=sha256:de817e2f5fc75a9e7dd350c4b0f54617b280e26d1631811a43e7e968fa71e3e9 \ - --hash=sha256:e724cebe1fadc2b23c6f7415bad5ee6239e00a69f30ee423f319c6af70e2a5c0 \ - --hash=sha256:e72591bcfe7512353bd609875ab38050efe3d55e18934e2f18950c108334b4ff \ - --hash=sha256:e76be12658a6fa376fcd331b1ea4e58f5a06fd0220653450f0d415b8fd0fbe20 \ - --hash=sha256:eb8d384a24778abf29afb8e41d68fdd9a156cf6e5390c04cc07bbc24b89e98b5 \ - --hash=sha256:ed350d6978d28b92939bfeb1a0570c523f6170efc3f0a0ef1f1df287cd4f4960 \ - --hash=sha256:eef44224729e9525d5261cc8d28d6b11cafc90e6bd0be2157bde69a52ec83024 \ - --hash=sha256:f4db56635b58cd1a200b0a23744ff44206ee6aa428185e2b6c4a65b3197abdcd \ - --hash=sha256:fdf5197a21dd660cf19dfd2a3ce79574588f8f5e2dbf21bda9ee2d2b46924d84 - # via -r requirements.anki.in -packaging==24.1 \ - --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \ - --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 - # via - # black - # build - # pytest -pathspec==0.12.1 \ - --hash=sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08 \ - --hash=sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712 - # via black -pip-system-certs==4.0 \ - --hash=sha256:47202b9403a6f40783a9674bbc8873f5fc86544ec01a49348fa913e99e2ff68b \ - --hash=sha256:db8e6a31388d9795ec9139957df1a89fa5274fb66164456fd091a5d3e94c350c - # via -r requirements.aqt.in -pip-tools==7.4.1 \ - --hash=sha256:4c690e5fbae2f21e87843e89c26191f0d9454f362d8acdbd695716493ec8b3a9 \ - --hash=sha256:864826f5073864450e24dbeeb85ce3920cdfb09848a3d69ebf537b521f14bcc9 - # via - # -r requirements.base.in - # -r requirements.dev.in -platformdirs==4.2.2 \ - --hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \ - --hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3 - # via - # black - # pylint -pluggy==1.5.0 \ - --hash=sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1 \ - --hash=sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669 - # via pytest -protobuf==5.28.2 \ - --hash=sha256:2c69461a7fcc8e24be697624c09a839976d82ae75062b11a0972e41fd2cd9132 \ - --hash=sha256:35cfcb15f213449af7ff6198d6eb5f739c37d7e4f1c09b5d0641babf2cc0c68f \ - --hash=sha256:52235802093bd8a2811abbe8bf0ab9c5f54cca0a751fdd3f6ac2a21438bffece \ - --hash=sha256:59379674ff119717404f7454647913787034f03fe7049cbef1d74a97bb4593f0 \ - --hash=sha256:5e8a95246d581eef20471b5d5ba010d55f66740942b95ba9b872d918c459452f \ - --hash=sha256:87317e9bcda04a32f2ee82089a204d3a2f0d3c8aeed16568c7daf4756e4f1fe0 \ - --hash=sha256:8ddc60bf374785fb7cb12510b267f59067fa10087325b8e1855b898a0d81d276 \ - --hash=sha256:a8b9403fc70764b08d2f593ce44f1d2920c5077bf7d311fefec999f8c40f78b7 \ - --hash=sha256:c0ea0123dac3399a2eeb1a1443d82b7afc9ff40241433296769f7da42d142ec3 \ - --hash=sha256:ca53faf29896c526863366a52a8f4d88e69cd04ec9571ed6082fa117fac3ab36 \ - --hash=sha256:eeea10f3dc0ac7e6b4933d32db20662902b4ab81bf28df12218aa389e9c2102d - # via - # -r requirements.anki.in - # mypy-protobuf -pychromedevtools==1.0.3 \ - --hash=sha256:a429968bb18d34322da4ed1b727980d35fbd8104d4e764f6d1850b4ffc6e563b - # via -r requirements.dev.in -pylint==3.2.7 \ - --hash=sha256:02f4aedeac91be69fb3b4bea997ce580a4ac68ce58b89eaefeaf06749df73f4b \ - --hash=sha256:1b7a721b575eaeaa7d39db076b6e7743c993ea44f57979127c517c6c572c803e - # via -r requirements.dev.in -pyproject-hooks==1.1.0 \ - --hash=sha256:4b37730834edbd6bd37f26ece6b44802fb1c1ee2ece0e54ddff8bfc06db86965 \ - --hash=sha256:7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2 - # via - # build - # pip-tools -pyrsistent==0.20.0 \ - --hash=sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f \ - --hash=sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e \ - --hash=sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958 \ - --hash=sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34 \ - --hash=sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca \ - --hash=sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d \ - --hash=sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d \ - --hash=sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4 \ - --hash=sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714 \ - --hash=sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf \ - --hash=sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee \ - --hash=sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8 \ - --hash=sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224 \ - --hash=sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d \ - --hash=sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054 \ - --hash=sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656 \ - --hash=sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7 \ - --hash=sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423 \ - --hash=sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce \ - --hash=sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e \ - --hash=sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3 \ - --hash=sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0 \ - --hash=sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f \ - --hash=sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b \ - --hash=sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce \ - --hash=sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a \ - --hash=sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174 \ - --hash=sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86 \ - --hash=sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f \ - --hash=sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b \ - --hash=sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98 \ - --hash=sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022 - # via jsonschema -pysocks==1.7.1 \ - --hash=sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299 \ - --hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \ - --hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0 - # via requests -pytest==8.3.2 \ - --hash=sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5 \ - --hash=sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce - # via -r requirements.dev.in -requests==2.32.4 \ - --hash=sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c \ - --hash=sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422 - # via - # -r requirements.anki.in - # -r requirements.aqt.in - # pychromedevtools -send2trash==1.8.3 \ - --hash=sha256:0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9 \ - --hash=sha256:b18e7a3966d99871aefeb00cfbcfdced55ce4871194810fc71f4aa484b953abf - # via -r requirements.aqt.in -six==1.16.0 \ - --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ - --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 - # via compare-locales -soupsieve==2.6 \ - --hash=sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb \ - --hash=sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9 - # via beautifulsoup4 -toml==0.10.2 \ - --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ - --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f - # via compare-locales -tomli==2.0.1 \ - --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ - --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f - # via - # black - # build - # mypy - # pip-tools - # pylint - # pytest -tomlkit==0.13.2 \ - --hash=sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde \ - --hash=sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79 - # via pylint -types-click==7.1.8 \ - --hash=sha256:8cb030a669e2e927461be9827375f83c16b8178c365852c060a34e24871e7e81 \ - --hash=sha256:b6604968be6401dc516311ca50708a0a28baa7a0cb840efd7412f0dbbff4e092 - # via types-flask -types-decorator==5.1.8.20240310 \ - --hash=sha256:3af75dc38f5baf65b9b53ea6661ce2056c5ca7d70d620d0b1f620285c1242757 \ - --hash=sha256:52e316b03783886a8a2abdc228f7071680ba65894545cd2085ebe3cf88684a0e - # via -r requirements.dev.in -types-flask==1.1.6 \ - --hash=sha256:6ab8a9a5e258b76539d652f6341408867298550b19b81f0e41e916825fc39087 \ - --hash=sha256:aac777b3abfff9436e6b01f6d08171cf23ea6e5be71cbf773aaabb1c5763e9cf - # via -r requirements.dev.in -types-flask-cors==5.0.0.20240902 \ - --hash=sha256:595e5f36056cd128ab905832e055f2e5d116fbdc685356eea4490bc77df82137 \ - --hash=sha256:8921b273bf7cd9636df136b66408efcfa6338a935e5c8f53f5eff1cee03f3394 - # via -r requirements.dev.in -types-jinja2==2.11.9 \ - --hash=sha256:60a1e21e8296979db32f9374d8a239af4cb541ff66447bb915d8ad398f9c63b2 \ - --hash=sha256:dbdc74a40aba7aed520b7e4d89e8f0fe4286518494208b35123bcf084d4b8c81 - # via types-flask -types-markdown==3.7.0.20240822 \ - --hash=sha256:183557c9f4f865bdefd8f5f96a38145c31819271cde111d35557c3bd2069e78d \ - --hash=sha256:bec91c410aaf2470ffdb103e38438fbcc53689b00133f19e64869eb138432ad7 - # via -r requirements.dev.in -types-markupsafe==1.1.10 \ - --hash=sha256:85b3a872683d02aea3a5ac2a8ef590193c344092032f58457287fbf8e06711b1 \ - --hash=sha256:ca2bee0f4faafc45250602567ef38d533e877d2ddca13003b319c551ff5b3cc5 - # via types-jinja2 -types-orjson==3.6.2 \ - --hash=sha256:22ee9a79236b6b0bfb35a0684eded62ad930a88a56797fa3c449b026cf7dbfe4 \ - --hash=sha256:cf9afcc79a86325c7aff251790338109ed6f6b1bab09d2d4262dd18c85a3c638 - # via -r requirements.dev.in -types-protobuf==5.27.0.20240626 \ - --hash=sha256:683ba14043bade6785e3f937a7498f243b37881a91ac8d81b9202ecf8b191e9c \ - --hash=sha256:688e8f7e8d9295db26bc560df01fb731b27a25b77cbe4c1ce945647f7024f5c1 - # via - # -r requirements.dev.in - # mypy-protobuf -types-pywin32==306.0.0.20240822 \ - --hash=sha256:31a16f7eaf711166e8aec50ee1ddf0f16b4512e19ecc92a019ae7a0860b64bad \ - --hash=sha256:34d22b58aaa2cc86fe585b6e2e1eda88a60b010badea0e0e4a410ebe28744645 - # via -r requirements.dev.in -types-requests==2.32.0.20240712 \ - --hash=sha256:90c079ff05e549f6bf50e02e910210b98b8ff1ebdd18e19c873cd237737c1358 \ - --hash=sha256:f754283e152c752e46e70942fa2a146b5bc70393522257bb85bd1ef7e019dcc3 - # via -r requirements.dev.in -types-waitress==3.0.0.20240423 \ - --hash=sha256:7e9f77a3bc3c20436b9b7ef93da88c8fe0d1e2205d5891ae7526cbd93554f5a4 \ - --hash=sha256:ec3af592b5868ccf151645afc74d2e606cd5dec3ed326c9fd0259691b39430fe - # via -r requirements.dev.in -types-werkzeug==1.0.9 \ - --hash=sha256:194bd5715a13c598f05c63e8a739328657590943bce941e8a3619a6b5d4a54ec \ - --hash=sha256:5cc269604c400133d452a40cee6397655f878fc460e03fde291b9e3a5eaa518c - # via types-flask -typing-extensions==4.12.2 \ - --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \ - --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8 - # via - # -r requirements.anki.in - # astroid - # black - # fluent-syntax - # mypy - # pylint -urllib3==2.2.2 \ - --hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \ - --hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168 - # via - # requests - # types-requests -waitress==3.0.1 \ - --hash=sha256:26cdbc593093a15119351690752c99adc13cbc6786d75f7b6341d1234a3730ac \ - --hash=sha256:ef0c1f020d9f12a515c4ec65c07920a702613afcad1dbfdc3bcec256b6c072b3 - # via -r requirements.aqt.in -websocket-client==1.8.0 \ - --hash=sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526 \ - --hash=sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da - # via pychromedevtools -werkzeug==3.0.6 \ - --hash=sha256:1bc0c2310d2fbb07b1dd1105eba2f7af72f322e1e455f2f93c993bee8c8a5f17 \ - --hash=sha256:a8dd59d4de28ca70471a34cba79bed5f7ef2e036a76b3ab0835474246eb41f8d - # via - # flask - # flask-cors -wheel==0.44.0 \ - --hash=sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f \ - --hash=sha256:a29c3f2817e95ab89aa4660681ad547c0e9547f20e75b0562fe7723c9a2a9d49 - # via pip-tools -wrapt==1.16.0 \ - --hash=sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc \ - --hash=sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81 \ - --hash=sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09 \ - --hash=sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e \ - --hash=sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca \ - --hash=sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0 \ - --hash=sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb \ - --hash=sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487 \ - --hash=sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40 \ - --hash=sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c \ - --hash=sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060 \ - --hash=sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202 \ - --hash=sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41 \ - --hash=sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9 \ - --hash=sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b \ - --hash=sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664 \ - --hash=sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d \ - --hash=sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362 \ - --hash=sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00 \ - --hash=sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc \ - --hash=sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1 \ - --hash=sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267 \ - --hash=sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956 \ - --hash=sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966 \ - --hash=sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1 \ - --hash=sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228 \ - --hash=sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72 \ - --hash=sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d \ - --hash=sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292 \ - --hash=sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0 \ - --hash=sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0 \ - --hash=sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36 \ - --hash=sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c \ - --hash=sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5 \ - --hash=sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f \ - --hash=sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73 \ - --hash=sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b \ - --hash=sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2 \ - --hash=sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593 \ - --hash=sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39 \ - --hash=sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389 \ - --hash=sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf \ - --hash=sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf \ - --hash=sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89 \ - --hash=sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c \ - --hash=sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c \ - --hash=sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f \ - --hash=sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440 \ - --hash=sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465 \ - --hash=sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136 \ - --hash=sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b \ - --hash=sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8 \ - --hash=sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3 \ - --hash=sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8 \ - --hash=sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6 \ - --hash=sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e \ - --hash=sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f \ - --hash=sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c \ - --hash=sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e \ - --hash=sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8 \ - --hash=sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2 \ - --hash=sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020 \ - --hash=sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35 \ - --hash=sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d \ - --hash=sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3 \ - --hash=sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537 \ - --hash=sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809 \ - --hash=sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d \ - --hash=sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a \ - --hash=sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4 - # via pip-system-certs -zipp==3.20.1 \ - --hash=sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064 \ - --hash=sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b - # via importlib-metadata - -# The following packages are considered to be unsafe in a requirements file: -pip==24.2 \ - --hash=sha256:2cd581cf58ab7fcfca4ce8efa6dcacd0de5bf8d0a3eb9ec927e07405f4d9e2a2 \ - --hash=sha256:5b5e490b5e9cb275c879595064adce9ebd31b854e3e803740b72f9ccf34a45b8 - # via pip-tools -setuptools==75.1.0 \ - --hash=sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2 \ - --hash=sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538 - # via pip-tools diff --git a/python/requirements.qt5_14.in b/python/requirements.qt5_14.in deleted file mode 100644 index 0d2b2be9d..000000000 --- a/python/requirements.qt5_14.in +++ /dev/null @@ -1,3 +0,0 @@ -pyqt5==5.14.1 -pyqtwebengine==5.14.0 -pyqt5_sip==12.8.1 diff --git a/python/requirements.qt5_14.txt b/python/requirements.qt5_14.txt deleted file mode 100644 index 6e609f027..000000000 --- a/python/requirements.qt5_14.txt +++ /dev/null @@ -1,42 +0,0 @@ -pyqt5==5.14.1 \ - --hash=sha256:2d94ec761fb656707050c68b41958e3a9f755bb1df96c064470f4096d2899e32 \ - --hash=sha256:2f230f2dbd767099de7a0cb915abdf0cbc3256a0b5bb910eb09b99117db7a65b \ - --hash=sha256:31b142a868152d60c6323e0527edb692fdf05fd7cb4fe2fe9ce07d1ce560221a \ - --hash=sha256:713b9a201f5e7b2fca8691373e5d5c8c2552a51d87ca9ffbb1461e34e3241211 \ - --hash=sha256:a0bfe9fd718bca4de3e33000347e048f73126b6dc46530eb020b0251a638ee9d - # via - # -r requirements.in - # pyqtwebengine -pyqt5-sip==12.8.1 \ - --hash=sha256:0304ca9114b9817a270f67f421355075b78ff9fc25ac58ffd72c2601109d2194 \ - --hash=sha256:0cd969be528c27bbd4755bd323dff4a79a8fdda28215364e6ce3e069cb56c2a9 \ - --hash=sha256:2f35e82fd7ec1e1f6716e9154721c7594956a4f5bd4f826d8c6a6453833cc2f0 \ - --hash=sha256:30e944db9abee9cc757aea16906d4198129558533eb7fadbe48c5da2bd18e0bd \ - --hash=sha256:34dcd29be47553d5f016ff86e89e24cbc5eebae92eb2f96fb32d2d7ba028c43c \ - --hash=sha256:5a011aeff89660622a6d5c3388d55a9d76932f3b82c95e82fc31abd8b1d2990d \ - --hash=sha256:6c1ebee60f1d2b3c70aff866b7933d8d8d7646011f7c32f9321ee88c290aa4f9 \ - --hash=sha256:7b81382ce188d63890a0e35abe0f9bb946cabc873a31873b73583b0fc84ac115 \ - --hash=sha256:832fd60a264de4134c2824d393320838f3ab648180c9c357ec58a74524d24507 \ - --hash=sha256:84ba7746762bd223bed22428e8561aa267a229c28344c2d28c5d5d3f8970cffb \ - --hash=sha256:9312ec47cac4e33c11503bc1cbeeb0bdae619620472f38e2078c5a51020a930f \ - --hash=sha256:a1b8ef013086e224b8e86c93f880f776d01b59195bdfa2a8e0b23f0480678fec \ - --hash=sha256:a29e2ac399429d3b7738f73e9081e50783e61ac5d29344e0802d0dcd6056c5a2 \ - --hash=sha256:b6d42250baec52a5f77de64e2951d001c5501c3a2df2179f625b241cbaec3369 \ - --hash=sha256:bb5a87b66fc1445915104ee97f7a20a69decb42f52803e3b0795fa17ff88226c \ - --hash=sha256:c317ab1263e6417c498b81f5c970a9b1af7acefab1f80b4cc0f2f8e661f29fc5 \ - --hash=sha256:c9800729badcb247765e4ffe2241549d02da1fa435b9db224845bc37c3e99cb0 \ - --hash=sha256:c9d6d448c29dc6606bb7974696608f81f4316c8234f7c7216396ed110075e777 \ - --hash=sha256:da9c9f1e65b9d09e73bd75befc82961b6b61b5a3b9d0a7c832168e1415f163c6 \ - --hash=sha256:ed897c58acf4a3cdca61469daa31fe6e44c33c6c06a37c3f21fab31780b3b86a \ - --hash=sha256:f168f0a7f32b81bfeffdf003c36f25d81c97dee5eb67072a5183e761fe250f13 - # via - # -r requirements.in - # pyqt5 - # pyqtwebengine -pyqtwebengine==5.14.0 \ - --hash=sha256:01cd7f38ba4efa5f4c0983219ab15dad7747a0ca9378c7832a3077a53988f5ea \ - --hash=sha256:37c4a820c5bcc82a6cb43ad33b8c81eee4c4772fc03e180a8fa37a59f99f6a48 \ - --hash=sha256:3d0cba04f64d4f66087cc92e254ff8b33ec4a4e6c7751417fe2bd53c3ed740a7 \ - --hash=sha256:85e1fac1b2c9bebf0b2e8cd9a75c14a38aad75165a8d8bcb8f6318944b779b25 \ - --hash=sha256:e11595051f8bfbfa49175d899b2c8c2eea3a3deac4141edf4db68c3555221c92 - # via -r requirements.in diff --git a/python/requirements.qt5_15.in b/python/requirements.qt5_15.in deleted file mode 100644 index 66642166f..000000000 --- a/python/requirements.qt5_15.in +++ /dev/null @@ -1,3 +0,0 @@ -pyqt5==5.15.5 -pyqtwebengine==5.15.5 -pyqt5_sip==12.9.0 diff --git a/python/requirements.qt5_15.txt b/python/requirements.qt5_15.txt deleted file mode 100644 index 8ac1e2225..000000000 --- a/python/requirements.qt5_15.txt +++ /dev/null @@ -1,54 +0,0 @@ -pyqt5==5.15.5 \ - --hash=sha256:521130eea1eaac55cc6867b1dc627d292b6468fb8e525ce2a015cdf39028d6e8 \ - --hash=sha256:5966fb291f316f8e35bc8775dda63acf1bb9855baeb5af3e33d3e7c4f1cd98d4 \ - --hash=sha256:85e76b7a96995b9da12083850bf2a9f4f0aeba2b0b99461b3337ad7e44f428c3 \ - --hash=sha256:b411b7a8fa03901c9feb1dcbac7ea1fc3ce20b9ae682762b777cd5398749ca2b \ - --hash=sha256:b8e23c1a3fe1b7749c9106f36fba0bd4676dc77bcacca95304c6b840b782e24d - # via - # -r requirements.in - # pyqtwebengine -pyqt5-qt5==5.15.2 \ - --hash=sha256:1988f364ec8caf87a6ee5d5a3a5210d57539988bf8e84714c7d60972692e2f4a \ - --hash=sha256:750b78e4dba6bdf1607febedc08738e318ea09e9b10aea9ff0d73073f11f6962 \ - --hash=sha256:76980cd3d7ae87e3c7a33bfebfaee84448fd650bad6840471d6cae199b56e154 \ - --hash=sha256:9cc7a768b1921f4b982ebc00a318ccb38578e44e45316c7a4a850e953e1dd327 - # via pyqt5 -pyqt5-sip==12.9.0 \ - --hash=sha256:055581c6fed44ba4302b70eeb82e979ff70400037358908f251cd85cbb3dbd93 \ - --hash=sha256:0fc9aefacf502696710b36cdc9fa2a61487f55ee883dbcf2c2a6477e261546f7 \ - --hash=sha256:42274a501ab4806d2c31659170db14c282b8313d2255458064666d9e70d96206 \ - --hash=sha256:4347bd81d30c8e3181e553b3734f91658cfbdd8f1a19f254777f906870974e6d \ - --hash=sha256:485972daff2fb0311013f471998f8ec8262ea381bded244f9d14edaad5f54271 \ - --hash=sha256:4f8e05fe01d54275877c59018d8e82dcdd0bc5696053a8b830eecea3ce806121 \ - --hash=sha256:69a3ad4259172e2b1aa9060de211efac39ddd734a517b1924d9c6c0cc4f55f96 \ - --hash=sha256:6a8701892a01a5a2a4720872361197cc80fdd5f49c8482d488ddf38c9c84f055 \ - --hash=sha256:6d5bca2fc222d58e8093ee8a81a6e3437067bb22bc3f86d06ec8be721e15e90a \ - --hash=sha256:83c3220b1ca36eb8623ba2eb3766637b19eb0ce9f42336ad8253656d32750c0a \ - --hash=sha256:a25b9843c7da6a1608f310879c38e6434331aab1dc2fe6cb65c14f1ecf33780e \ - --hash=sha256:ac57d796c78117eb39edd1d1d1aea90354651efac9d3590aac67fa4983f99f1f \ - --hash=sha256:b09f4cd36a4831229fb77c424d89635fa937d97765ec90685e2f257e56a2685a \ - --hash=sha256:c446971c360a0a1030282a69375a08c78e8a61d568bfd6dab3dcc5cf8817f644 \ - --hash=sha256:c5216403d4d8d857ec4a61f631d3945e44fa248aa2415e9ee9369ab7c8a4d0c7 \ - --hash=sha256:d3e4489d7c2b0ece9d203ae66e573939f7f60d4d29e089c9f11daa17cfeaae32 \ - --hash=sha256:d59af63120d1475b2bf94fe8062610720a9be1e8940ea146c7f42bb449d49067 \ - --hash=sha256:d85002238b5180bce4b245c13d6face848faa1a7a9e5c6e292025004f2fd619a \ - --hash=sha256:d8b2bdff7bbf45bc975c113a03b14fd669dc0c73e1327f02706666a7dd51a197 \ - --hash=sha256:dd05c768c2b55ffe56a9d49ce6cc77cdf3d53dbfad935258a9e347cbfd9a5850 \ - --hash=sha256:fc43f2d7c438517ee33e929e8ae77132749c15909afab6aeece5fcf4147ffdb5 - # via - # -r requirements.in - # pyqt5 - # pyqtwebengine -pyqtwebengine==5.15.5 \ - --hash=sha256:30cebf455406990d5a0b859eac261ba6b45c32ce18956271733e0e96dbdca9b7 \ - --hash=sha256:5c77f71d88d871bc7400c68ef6433fadc5bd57b86d1a9c4d8094cea42f3607f1 \ - --hash=sha256:782aeee6bc8699bc029fe5c169a045c2bc9533d781cf3f5e9fb424b85a204e68 \ - --hash=sha256:ab47608dccf2b5e4b950d5a3cc704b17711af035024d07a9b71ad29fc103b941 \ - --hash=sha256:b827ad7ba0a65d5cd176797478f0ec8f599df6746b06c548649ff5674482a022 - # via -r requirements.in -pyqtwebengine-qt5==5.15.2 \ - --hash=sha256:24231f19e1595018779977de6722b5c69f3d03f34a5f7574ff21cd1e764ef76d \ - --hash=sha256:9e80b408d8de09d4e708d5d84c3ceaf3603292ff8f5e566ae44bb0320fa59c33 \ - --hash=sha256:bc7b1fd1f4f8138d59b0b0245d601fb2c5c0aa1e1e7e853b713e52a3165d147e \ - --hash=sha256:ec2acb1780c0124ef060c310e00ca701f388d8b6c35bba9127f7a6f0dc536f77 - # via pyqtwebengine diff --git a/python/requirements.qt6_6.in b/python/requirements.qt6_6.in deleted file mode 100644 index af94affd8..000000000 --- a/python/requirements.qt6_6.in +++ /dev/null @@ -1,5 +0,0 @@ -pyqt6==6.6.1 -pyqt6-qt6==6.6.2 -pyqt6-webengine==6.6.0 -pyqt6-webengine-qt6==6.6.2 -pyqt6_sip==13.6.0 diff --git a/python/requirements.qt6_6.txt b/python/requirements.qt6_6.txt deleted file mode 100644 index 5b1a3ba9b..000000000 --- a/python/requirements.qt6_6.txt +++ /dev/null @@ -1,56 +0,0 @@ -pyqt6==6.6.1 \ - --hash=sha256:03a656d5dc5ac31b6a9ad200f7f4f7ef49fa00ad7ce7a991b9bb691617141d12 \ - --hash=sha256:5aa0e833cb5a79b93813f8181d9f145517dd5a46f4374544bcd1e93a8beec537 \ - --hash=sha256:6b43878d0bbbcf8b7de165d305ec0cb87113c8930c92de748a11c473a6db5085 \ - --hash=sha256:9f158aa29d205142c56f0f35d07784b8df0be28378d20a97bcda8bd64ffd0379 - # via - # -r requirements.qt6_6.in - # pyqt6-webengine -pyqt6-qt6==6.6.2 \ - --hash=sha256:5a41fe9d53b9e29e9ec5c23f3c5949dba160f90ca313ee8b96b8ffe6a5059387 \ - --hash=sha256:7ef446d3ffc678a8586ff6dc9f0d27caf4dff05dea02c353540d2f614386faf9 \ - --hash=sha256:8d7f674a4ec43ca00191e14945ca4129acbe37a2172ed9d08214ad58b170bc11 \ - --hash=sha256:b8363d88623342a72ac17da9127dc12f259bb3148796ea029762aa2d499778d9 - # via - # -r requirements.qt6_6.in - # pyqt6 -pyqt6-sip==13.6.0 \ - --hash=sha256:0dfd22cfedd87e96f9d51e0778ca2ba3dc0be83e424e9e0f98f6994d8d9c90f0 \ - --hash=sha256:13885361ca2cb2f5085d50359ba61b3fabd41b139fb58f37332acbe631ef2357 \ - --hash=sha256:24441032a29791e82beb7dfd76878339058def0e97fdb7c1cea517f3a0e6e96b \ - --hash=sha256:2486e1588071943d4f6657ba09096dc9fffd2322ad2c30041e78ea3f037b5778 \ - --hash=sha256:3075d8b325382750829e6cde6971c943352309d35768a4d4da0587459606d562 \ - --hash=sha256:33ea771fe777eb0d1a2c3ef35bcc3f7a286eb3ff09cd5b2fdd3d87d1f392d7e8 \ - --hash=sha256:39854dba35f8e5a4288da26ecb5f40b4c5ec1932efffb3f49d5ea435a7f37fb3 \ - --hash=sha256:3bf03e130fbfd75c9c06e687b86ba375410c7a9e835e4e03285889e61dd4b0c4 \ - --hash=sha256:43fb8551796030aae3d66d6e35e277494071ec6172cd182c9569ab7db268a2f5 \ - --hash=sha256:58f68a48400e0b3d1ccb18090090299bad26e3aed7ccb7057c65887b79b8aeea \ - --hash=sha256:5b9c6b6f9cfccb48cbb78a59603145a698fb4ffd176764d7083e5bf47631d8df \ - --hash=sha256:747f6ca44af81777a2c696bd501bc4815a53ec6fc94d4e25830e10bc1391f8ab \ - --hash=sha256:86a7b67c64436e32bffa9c28c9f21bf14a9faa54991520b12c3f6f435f24df7f \ - --hash=sha256:8c282062125eea5baf830c6998587d98c50be7c3a817a057fb95fef647184012 \ - --hash=sha256:8f9df9f7ccd8a9f0f1d36948c686f03ce1a1281543a3e636b7b7d5e086e1a436 \ - --hash=sha256:98bf954103b087162fa63b3a78f30b0b63da22fd6450b610ec1b851dbb798228 \ - --hash=sha256:9adf672f9114687533a74d5c2d4c03a9a929ad5ad9c3e88098a7da1a440ab916 \ - --hash=sha256:a6ce80bc24618d8a41be8ca51ad9f10e8bc4296dd90ab2809573df30a23ae0e5 \ - --hash=sha256:d6b5f699aaed0ac1fcd23e8fbca70d8a77965831b7c1ce474b81b1678817a49d \ - --hash=sha256:fa759b6339ff7e25f9afe2a6b651b775f0a36bcb3f5fa85e81a90d3b033c83f4 \ - --hash=sha256:fa7b10af7488efc5e53b41dd42c0f421bde6c2865a107af7ae259aff9d841da9 - # via - # -r requirements.qt6_6.in - # pyqt6 - # pyqt6-webengine -pyqt6-webengine==6.6.0 \ - --hash=sha256:9d542738ed6e11c1978ce59035c07627def7c63eef0f59581d327f01209141bc \ - --hash=sha256:cb7793f06525ca054fcc6039afd93e23b82228b880d0b1301ce635f7f3ed2edf \ - --hash=sha256:d50b984c3f85e409e692b156132721522d4e8cf9b6c25e0cf927eea2dfb39487 \ - --hash=sha256:fded35fba636c4916fec84aa7c6840ad2e75d211462feb3e966f9545a59d56e6 - # via -r requirements.qt6_6.in -pyqt6-webengine-qt6==6.6.2 \ - --hash=sha256:27b1b6a6f4ea115b3dd300d2df906d542009d9eb0e62b05e6b7cb85dfe68e9c3 \ - --hash=sha256:3da4db9ddd984b647d0b79fa10fc6cf65364dfe283cd702b12cb7164be2307cd \ - --hash=sha256:5d6f3ae521115cee77fea22b0248e7b219995390b951b51e4d519aef9c304ca8 \ - --hash=sha256:f2364dfa3a6e751ead71b7ba759081be677fcf1c6bbd8a2a2a250eb5f06432e8 - # via - # -r requirements.qt6_6.in - # pyqt6-webengine diff --git a/python/requirements.qt6_8.in b/python/requirements.qt6_8.in deleted file mode 100644 index e16ae92e8..000000000 --- a/python/requirements.qt6_8.in +++ /dev/null @@ -1,5 +0,0 @@ -pyqt6==6.8.0 -pyqt6-qt6==6.8.1 -pyqt6-webengine==6.8.0 -pyqt6-webengine-qt6==6.8.1 -pyqt6_sip==13.9.1 diff --git a/python/requirements.qt6_8.txt b/python/requirements.qt6_8.txt deleted file mode 100644 index 21b567ff7..000000000 --- a/python/requirements.qt6_8.txt +++ /dev/null @@ -1,71 +0,0 @@ -pyqt6==6.8.0 \ - --hash=sha256:3a4354816f11e812b727206a9ea6e79ff3774f1bb7228ad4b9318442d2c64ff9 \ - --hash=sha256:452bae5840077bf0f146c798d7777f70d7bdd0c7dcfa9ee7a415c1daf2d10038 \ - --hash=sha256:48bace7b87676bba5e6114482f3a20ca20be90c7f261b5d340464313f5f2bf5e \ - --hash=sha256:6d8628de4c2a050f0b74462e4c9cb97f839bf6ffabbca91711722ffb281570d9 \ - --hash=sha256:8c5c05f5fdff31a5887dbc29b27615b09df467631238d7b449283809ffca6228 \ - --hash=sha256:a9913d479f1ffee804bf7f232079baea4fb4b221a8f4890117588917a54ea30d \ - --hash=sha256:cf7123caea14e7ecf10bd12cae48e8d9970ef7caf627bc7d7988b0baa209adb3 - # via - # -r requirements.qt6_8.in - # pyqt6-webengine -pyqt6-qt6==6.8.1 \ - --hash=sha256:006d786693d0511fbcf184a862edbd339c6ed1bb3bd9de363d73a19ed4b23dff \ - --hash=sha256:08065d595f1e6fc2dde9f4450eeff89082f4bad26f600a8e9b9cc5966716bfcf \ - --hash=sha256:1eb8460a1fdb38d0b2458c2974c01d471c1e59e4eb19ea63fc447aaba3ad530e \ - --hash=sha256:20843cb86bd94942d1cd99e39bf1aeabb875b241a35a8ab273e4bbbfa63776db \ - --hash=sha256:2f4b8b55b1414b93f340f22e8c88d25550efcdebc4b65a3927dd947b73bd4358 \ - --hash=sha256:98aa99fe38ae68c5318284cd28f3479ba538c40bf6ece293980abae0925c1b24 \ - --hash=sha256:9f3790c4ce4dc576e48b8718d55fb8743057e6cbd53a6ca1dd253ffbac9b7287 \ - --hash=sha256:a8bc2ed4ee5e7c6ff4dd1c7db0b27705d151fee5dc232bbd1bf17618f937f515 \ - --hash=sha256:d6ca5d2b9d2ec0ee4a814b2175f641a5c4299cb80b45e0f5f8356632663f89b3 - # via - # -r requirements.qt6_8.in - # pyqt6 -pyqt6-sip==13.9.1 \ - --hash=sha256:14f95c6352e3b85dc26bf59cfbf77a470ecbd5fcdcf00af4b648f0e1b9eefb9e \ - --hash=sha256:15be741d1ae8c82bb7afe9a61f3cf8c50457f7d61229a1c39c24cd6e8f4d86dc \ - --hash=sha256:1d322ded1d1fea339cc6ac65b768e72c69c486eebb7db6ccde061b5786d74cc5 \ - --hash=sha256:1ec52e962f54137a19208b6e95b6bd9f7a403eb25d7237768a99306cd9db26d1 \ - --hash=sha256:1fb405615970e85b622b13b4cad140ff1e4182eb8334a0b27a4698e6217b89b0 \ - --hash=sha256:22d66256b800f552ade51a463510bf905f3cb318aae00ff4288fae4de5d0e600 \ - --hash=sha256:2ab85aaf155828331399c59ebdd4d3b0358e42c08250e86b43d56d9873df148a \ - --hash=sha256:3c269052c770c09b61fce2f2f9ea934a67dfc65f443d59629b4ccc8f89751890 \ - --hash=sha256:5004514b08b045ad76425cf3618187091a668d972b017677b1b4b193379ef553 \ - --hash=sha256:552ff8fdc41f5769d3eccc661f022ed496f55f6e0a214c20aaf56e56385d61b6 \ - --hash=sha256:5643c92424fe62cb0b33378fef3d28c1525f91ada79e8a15bd9a05414a09503d \ - --hash=sha256:56ce0afb19cd8a8c63ff93ae506dffb74f844b88adaa6673ebc0dec43af48a76 \ - --hash=sha256:57b5312ef13c1766bdf69b317041140b184eb24a51e1e23ce8fc5386ba8dffb2 \ - --hash=sha256:5d7726556d1ca7a7ed78e19ba53285b64a2a8f6ad7ff4cb18a1832efca1a3102 \ - --hash=sha256:69a879cfc94f4984d180321b76f52923861cd5bf4969aa885eef7591ee932517 \ - --hash=sha256:6e6c1e2592187934f4e790c0c099d0033e986dcef7bdd3c06e3895ffa995e9fc \ - --hash=sha256:8b2ac36d6e04db6099614b9c1178a2f87788c7ffc3826571fb63d36ddb4c401d \ - --hash=sha256:8c207528992d59b0801458aa6fcff118e5c099608ef0fc6ff8bccbdc23f29c04 \ - --hash=sha256:976c7758f668806d4df7a8853f390ac123d5d1f73591ed368bdb8963574ff589 \ - --hash=sha256:accab6974b2758296400120fdcc9d1f37785b2ea2591f00656e1776f058ded6c \ - --hash=sha256:c1942e107b0243ced9e510d507e0f27aeea9d6b13e0a1b7c06fd52a62e0d41f7 \ - --hash=sha256:c800db3464481e87b1d2b84523b075df1e8fc7856c6f9623dc243f89be1cb604 \ - --hash=sha256:e996d320744ca8342cad6f9454345330d4f06bce129812d032bda3bad6967c5c \ - --hash=sha256:fa27b51ae4c7013b3700cf0ecf46907d1333ae396fc6511311920485cbce094b - # via - # -r requirements.qt6_8.in - # pyqt6 - # pyqt6-webengine -pyqt6-webengine==6.8.0 \ - --hash=sha256:5b5090dcc71dd36172ca8370db7dcaadfa0a022a8e58f6e172301289036c666b \ - --hash=sha256:5b9231b58014965b72504e49f39a6dbc3ecd05d4d725af011d75e6c8a7e2d5f7 \ - --hash=sha256:64045ea622b6a41882c2b18f55ae9714b8660acff06a54e910eb72822c2f3ff2 \ - --hash=sha256:c549f0f72c285eeea94000f6764dfaebf6bb3b13224580c7169a409bf1bf1bb7 \ - --hash=sha256:c7a5731923112acf23fbf93efad91f7b1545221063572106273e34c15a029fe7 \ - --hash=sha256:d7366809d681bcc096fa565f2a81d0ab040f7da5bb4f12f78e834a2b173c87d1 - # via -r requirements.qt6_8.in -pyqt6-webengine-qt6==6.8.1 \ - --hash=sha256:0405b6ce35f406affb27547c6c3608dc82405568af71505fefae4081c8b4ac39 \ - --hash=sha256:0ced2a10433da2571cfa29ed882698e0e164184d54068d17ba73799c45af5f0f \ - --hash=sha256:79f67a459ecb452f865e04f19122a1d6f30c83d9a1ffd06e7e6f0d652204083a \ - --hash=sha256:8059118591641cc9da6616343d893c77fbd065bef3e0764679543345e2c75123 \ - --hash=sha256:a375dbb34e03707b0ab4830b61e4d77a31dc3ef880421c8936472f2af34a3f80 \ - --hash=sha256:e36574aa55b30633a12aa000835f01e488a0f0c13513fd9a0d50c2281e0a9068 - # via - # -r requirements.qt6_8.in - # pyqt6-webengine diff --git a/python/requirements.win.in b/python/requirements.win.in deleted file mode 100644 index 24fd9cb64..000000000 --- a/python/requirements.win.in +++ /dev/null @@ -1,2 +0,0 @@ -pywin32 - diff --git a/python/requirements.win.txt b/python/requirements.win.txt deleted file mode 100644 index 65c2b6e3e..000000000 --- a/python/requirements.win.txt +++ /dev/null @@ -1,16 +0,0 @@ -pywin32==305 \ - --hash=sha256:109f98980bfb27e78f4df8a51a8198e10b0f347257d1e265bb1a32993d0c973d \ - --hash=sha256:13362cc5aa93c2beaf489c9c9017c793722aeb56d3e5166dadd5ef82da021fe1 \ - --hash=sha256:19ca459cd2e66c0e2cc9a09d589f71d827f26d47fe4a9d09175f6aa0256b51c2 \ - --hash=sha256:326f42ab4cfff56e77e3e595aeaf6c216712bbdd91e464d167c6434b28d65990 \ - --hash=sha256:421f6cd86e84bbb696d54563c48014b12a23ef95a14e0bdba526be756d89f116 \ - --hash=sha256:48d8b1659284f3c17b68587af047d110d8c44837736b8932c034091683e05863 \ - --hash=sha256:4ecd404b2c6eceaca52f8b2e3e91b2187850a1ad3f8b746d0796a98b4cea04db \ - --hash=sha256:50768c6b7c3f0b38b7fb14dd4104da93ebced5f1a50dc0e834594bff6fbe1271 \ - --hash=sha256:56d7a9c6e1a6835f521788f53b5af7912090674bb84ef5611663ee1595860fc7 \ - --hash=sha256:73e819c6bed89f44ff1d690498c0a811948f73777e5f97c494c152b850fad478 \ - --hash=sha256:742eb905ce2187133a29365b428e6c3b9001d79accdc30aa8969afba1d8470f4 \ - --hash=sha256:9d968c677ac4d5cbdaa62fd3014ab241718e619d8e36ef8e11fb930515a1e918 \ - --hash=sha256:9dd98384da775afa009bc04863426cb30596fd78c6f8e4e2e5bbf4edf8029504 \ - --hash=sha256:a55db448124d1c1484df22fa8bbcbc45c64da5e6eae74ab095b9ea62e6d00496 - # via -r requirements.win.in diff --git a/python/update_python_deps.sh b/python/update_python_deps.sh deleted file mode 100755 index 9040e3456..000000000 --- a/python/update_python_deps.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -set -e - -if [ "$1" == "all" ]; then - upgrade="--upgrade" -elif [ "$1" != "" ]; then - upgrade="--upgrade-package $1" -else - upgrade="" -fi - -args="--resolver=backtracking --allow-unsafe --no-header --strip-extras --generate-hashes" - -# initial pyenv bootstrap -../out/pyenv/bin/pip-compile $args $upgrade requirements.base.in - -# during build/development/testing -../out/pyenv/bin/pip-compile $args $upgrade requirements.dev.in - -# during bundle -../out/pyenv/bin/pip-compile $args $upgrade requirements.bundle.in -for i in requirements.{bundle,qt6*}.in; do ../out/pyenv/bin/pip-compile $args $upgrade $i; done - - diff --git a/python/update_win_deps.bat b/python/update_win_deps.bat deleted file mode 100644 index 2cee31f3f..000000000 --- a/python/update_win_deps.bat +++ /dev/null @@ -1 +0,0 @@ -..\out\pyenv\scripts\pip-compile --resolver=backtracking --allow-unsafe --no-header --strip-extras --generate-hashes requirements.win.in diff --git a/python/version.py b/python/version.py new file mode 100644 index 000000000..5cf3c4f12 --- /dev/null +++ b/python/version.py @@ -0,0 +1,10 @@ +# Copyright: Ankitects Pty Ltd and contributors +# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +"""Version helper for wheel builds.""" + +import pathlib + +# Read version from .version file in project root +_version_file = pathlib.Path(__file__).parent.parent / ".version" +__version__ = _version_file.read_text().strip() diff --git a/python/write_wheel.py b/python/write_wheel.py deleted file mode 100644 index 09899e292..000000000 --- a/python/write_wheel.py +++ /dev/null @@ -1,191 +0,0 @@ -# Based on https://github.com/ziglang/zig-pypi/blob/de14cf728fa35c014821f62a4fa9abd9f4bb560e/make_wheels.py -# MIT - -from __future__ import annotations - -import os -import sys -from email.message import EmailMessage -from pathlib import Path -from typing import Sequence -from zipfile import ZIP_DEFLATED, ZipInfo - -from wheel.wheelfile import WheelFile - -def make_message(headers, payload=None): - msg = EmailMessage() - for name, value in headers.items(): - if name == "_dependencies": - for dep in value: - if isinstance(dep, ExtraRequires): - msg["Provides-Extra"] = dep.name - for inner_dep in dep.deps: - msg["Requires-Dist"] = f"{inner_dep}; extra == '{dep.name}'" - else: - msg["Requires-Dist"] = dep - elif isinstance(value, list): - for value_part in value: - msg[name] = value_part - else: - msg[name] = value - if payload: - msg.set_payload(payload) - # EmailMessage wraps the license line, which results in an invalid file - out = bytes(msg) - out = out.replace(b"License v3 or\n later", b"License v3 or later") - return out - - -def write_wheel_file(filename, contents): - with WheelFile(filename, "w") as wheel: - for member_info, member_source in contents.items(): - if not isinstance(member_info, ZipInfo): - member_info = ZipInfo(member_info) - member_info.external_attr = 0o644 << 16 - member_info.file_size = len(member_source) - member_info.compress_type = ZIP_DEFLATED - wheel.writestr(member_info, bytes(member_source)) - return filename - - -def write_wheel( - wheel_path, - *, - name, - version, - tag, - metadata, - description, - contents, - entrypoints: list[str] | None = None, - top_level: list[str] | None = None, -): - dist_info = f"{name}-{version}.dist-info" - extra = {} - if entrypoints: - entrypoints_joined = "\n".join(entrypoints) - text = f"[console_scripts]\n{entrypoints_joined}" - file = f"{dist_info}/entry_points.txt" - extra[file] = text.encode("utf8") - if top_level: - top_level_joined = "\n".join(top_level) + "\n" - file = f"{dist_info}/top_level.txt" - extra[file] = top_level_joined.encode("utf8") - return write_wheel_file( - wheel_path, - { - **contents, - **extra, - f"{dist_info}/METADATA": make_message( - { - "Metadata-Version": "2.1", - "Name": name, - "Version": version, - **metadata, - }, - description, - ), - f"{dist_info}/WHEEL": make_message( - { - "Wheel-Version": "1.0", - "Generator": "anki write_wheel.py", - "Root-Is-Purelib": "false", - "Tag": tag, - } - ), - }, - ) - - -def merge_sources(contents, root, exclude): - root = Path(root) - for path in root.glob("**/*"): - if path.is_dir() or exclude(path): - continue - path_str = str(path.relative_to(root.parent)) - if path_str.endswith(".pyc"): - continue - contents[path_str] = path.read_bytes() - - -def split_wheel_path(path: str): - path2 = Path(path) - components = path2.stem.split("-", maxsplit=2) - return components - - -class ExtraRequires: - def __init__(self, name, deps): - self.name = name - self.deps = deps - - -src_root = sys.argv[1] -generated_root = sys.argv[2] -wheel_path = sys.argv[3] - -name, version, tag = split_wheel_path(wheel_path) - - -def exclude_aqt(path: Path) -> bool: - if path.suffix in [".ui", ".scss", ".map", ".ts"]: - return True - if path.name.startswith("tsconfig"): - return True - if "/aqt/data" in str(path): - return True - return False - - -def exclude_nothing(path: Path) -> bool: - return False - - -def extract_requirements(path: Path) -> list[str]: - return path.read_text().splitlines() - - -if name == "aqt": - exclude = exclude_aqt -else: - exclude = exclude_nothing - -contents: dict[str, str] = {} -merge_sources(contents, src_root, exclude) -merge_sources(contents, generated_root, exclude) -all_requires: Sequence[str | ExtraRequires] - -if name == "anki": - all_requires = extract_requirements(Path("python/requirements.anki.in")) - entrypoints = None - top_level = None -else: - all_requires = extract_requirements(Path("python/requirements.aqt.in")) + [ - "anki==" + version, - "pyqt6>=6.2", - "pyqt6-webengine>=6.2", - ] - entrypoints = ["anki = aqt:run"] - top_level = ["aqt", "_aqt"] - -# reproducible builds -os.environ["SOURCE_DATE_EPOCH"] = "0" - -write_wheel( - wheel_path, - name=name, - version=version, - tag=tag, - metadata={ - "License": "AGPL-3", - "Classifier": [ - "License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)", - ], - "Requires-Python": ">=3.9", - "_dependencies": all_requires, - }, - description="Please see https://apps.ankiweb.net\n\n", - contents=contents, - entrypoints=entrypoints, - top_level=top_level, -) diff --git a/qt/aqt/__init__.py b/qt/aqt/__init__.py index cdbd05ebe..6645e3599 100644 --- a/qt/aqt/__init__.py +++ b/qt/aqt/__init__.py @@ -5,10 +5,16 @@ from __future__ import annotations import atexit import logging +import os import sys from collections.abc import Callable from typing import TYPE_CHECKING, Any, Union, cast +if "ANKI_FIRST_RUN" in os.environ: + from .package import first_run_setup + + first_run_setup() + try: import pip_system_certs.wrapt_requests except ModuleNotFoundError: @@ -32,24 +38,14 @@ if "--syncserver" in sys.argv: from anki.syncserver import run_sync_server from anki.utils import is_mac - from .package import _fix_protobuf_path - - if is_mac and getattr(sys, "frozen", False): - _fix_protobuf_path() - # does not return run_sync_server() -from .package import packaged_build_setup - -packaged_build_setup() - import argparse import builtins import cProfile import getpass import locale -import os import tempfile import traceback from pathlib import Path @@ -270,13 +266,7 @@ def setupLangAndBackend( # load qt translations _qtrans = QTranslator() - if is_mac and getattr(sys, "frozen", False): - qt_dir = os.path.join(sys.prefix, "../Resources/qt_translations") - else: - if qtmajor == 5: - qt_dir = QLibraryInfo.location(QLibraryInfo.TranslationsPath) # type: ignore - else: - qt_dir = QLibraryInfo.path(QLibraryInfo.LibraryPath.TranslationsPath) + qt_dir = QLibraryInfo.path(QLibraryInfo.LibraryPath.TranslationsPath) qt_lang = lang.replace("-", "_") if _qtrans.load(f"qtbase_{qt_lang}", qt_dir): app.installTranslator(_qtrans) @@ -607,14 +597,13 @@ def _run(argv: list[str] | None = None, exec: bool = True) -> AnkiApp | None: profiler = cProfile.Profile() profiler.enable() - packaged = getattr(sys, "frozen", False) x11_available = os.getenv("DISPLAY") wayland_configured = qtmajor > 5 and ( os.getenv("QT_QPA_PLATFORM") == "wayland" or os.getenv("WAYLAND_DISPLAY") ) wayland_forced = os.getenv("ANKI_WAYLAND") - if (packaged or is_gnome) and wayland_configured: + if is_gnome and wayland_configured: if wayland_forced or not x11_available: # Work around broken fractional scaling in Wayland # https://bugreports.qt.io/browse/QTBUG-113574 diff --git a/qt/aqt/_macos_helper.py b/qt/aqt/_macos_helper.py index 859cb4b0a..9328b1c4a 100644 --- a/qt/aqt/_macos_helper.py +++ b/qt/aqt/_macos_helper.py @@ -14,12 +14,7 @@ import aqt.utils class _MacOSHelper: def __init__(self) -> None: - if getattr(sys, "frozen", False): - path = os.path.join(sys.prefix, "libankihelper.dylib") - else: - path = os.path.join( - aqt.utils.aqt_data_folder(), "lib", "libankihelper.dylib" - ) + path = os.path.join(aqt.utils.aqt_data_folder(), "lib", "libankihelper.dylib") self._dll = CDLL(path) self._dll.system_is_dark.restype = c_bool diff --git a/qt/aqt/browser/sidebar/model.py b/qt/aqt/browser/sidebar/model.py index 286811aca..fd27926b5 100644 --- a/qt/aqt/browser/sidebar/model.py +++ b/qt/aqt/browser/sidebar/model.py @@ -2,8 +2,6 @@ # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html from __future__ import annotations -from typing import cast - import aqt import aqt.browser from aqt.browser.sidebar.item import SidebarItem @@ -107,11 +105,11 @@ class SidebarModel(QAbstractItemModel): return self.sidebar._on_rename(index.internalPointer(), text) def supportedDropActions(self) -> Qt.DropAction: - return cast(Qt.DropAction, Qt.DropAction.MoveAction) + return Qt.DropAction.MoveAction def flags(self, index: QModelIndex) -> Qt.ItemFlag: if not index.isValid(): - return cast(Qt.ItemFlag, Qt.ItemFlag.ItemIsEnabled) + return Qt.ItemFlag.ItemIsEnabled flags = ( Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index c1eb14b18..3a980145d 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -1003,17 +1003,20 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too warnings.simplefilter("ignore", UserWarning) doc = BeautifulSoup(html, "html.parser") - tag: bs4.element.Tag if not internal: - for tag in self.removeTags: - for node in doc(tag): + for tag_name in self.removeTags: + for node in doc(tag_name): node.decompose() # convert p tags to divs for node in doc("p"): - node.name = "div" + if hasattr(node, "name"): + node.name = "div" - for tag in doc("img"): + for element in doc("img"): + if not isinstance(element, bs4.Tag): + continue + tag = element try: src = tag["src"] except KeyError: @@ -1023,18 +1026,18 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too # in internal pastes, rewrite mediasrv references to relative if internal: - m = re.match(r"http://127.0.0.1:\d+/(.*)$", src) + m = re.match(r"http://127.0.0.1:\d+/(.*)$", str(src)) if m: tag["src"] = m.group(1) else: # in external pastes, download remote media - if self.isURL(src): + if isinstance(src, str) and self.isURL(src): fname = self._retrieveURL(src) if fname: tag["src"] = fname - elif src.startswith("data:image/"): + elif isinstance(src, str) and src.startswith("data:image/"): # and convert inlined data - tag["src"] = self.inlinedImageToFilename(src) + tag["src"] = self.inlinedImageToFilename(str(src)) html = str(doc) return html diff --git a/qt/aqt/errors.py b/qt/aqt/errors.py index c2b2f2ae6..af1036acd 100644 --- a/qt/aqt/errors.py +++ b/qt/aqt/errors.py @@ -165,6 +165,7 @@ _mbox: QMessageBox | None = None class ErrorHandler(QObject): "Catch stderr and write into buffer." + ivl = 100 fatal_error_encountered = False diff --git a/qt/aqt/mediasrv.py b/qt/aqt/mediasrv.py index a38790728..69ef054ec 100644 --- a/qt/aqt/mediasrv.py +++ b/qt/aqt/mediasrv.py @@ -252,14 +252,8 @@ def _handle_local_file_request(request: LocalFileRequest) -> Response: def _builtin_data(path: str) -> bytes: """Return data from file in aqt/data folder. Path must use forward slash separators.""" - # packaged build? - if getattr(sys, "frozen", False): - reader = aqt.__loader__.get_resource_reader("_aqt") # type: ignore - with reader.open_resource(path) as f: - return f.read() - else: - full_path = aqt_data_path() / ".." / path - return full_path.read_bytes() + full_path = aqt_data_path() / ".." / path + return full_path.read_bytes() def _handle_builtin_file_request(request: BundledFileRequest) -> Response: diff --git a/qt/aqt/mpv.py b/qt/aqt/mpv.py index 74155814c..e86675e41 100644 --- a/qt/aqt/mpv.py +++ b/qt/aqt/mpv.py @@ -177,7 +177,8 @@ class MPVBase: startup. """ start = time.time() - while self.is_running() and time.time() < start + 10: + timeout = 60 if is_mac else 10 + while self.is_running() and time.time() < start + timeout: time.sleep(0.1) if is_win: # named pipe diff --git a/qt/aqt/package.py b/qt/aqt/package.py index f1ee8cd79..a0642fca0 100644 --- a/qt/aqt/package.py +++ b/qt/aqt/package.py @@ -5,93 +5,65 @@ from __future__ import annotations -import os -import sys +import subprocess from pathlib import Path - -def _fix_pywin32() -> None: - # extend sys.path with .pth files - import site - - site.addsitedir(sys.path[0]) - - # use updated sys.path to locate dll folder and add it to path - path = sys.path[-1] - path = path.replace("Pythonwin", "pywin32_system32") - os.environ["PATH"] += ";" + path - - # import Python modules from .dll files - import importlib.machinery - - for name in "pythoncom", "pywintypes": - filename = os.path.join(path, name + "39.dll") - loader = importlib.machinery.ExtensionFileLoader(name, filename) - spec = importlib.machinery.ModuleSpec(name=name, loader=loader, origin=filename) - _mod = importlib._bootstrap._load(spec) # type: ignore +from anki.utils import is_mac -def _patch_pkgutil() -> None: - """Teach pkgutil.get_data() how to read files from in-memory resources. +# pylint: disable=unused-import,import-error +def first_run_setup() -> None: + """Code run the first time after install/upgrade. - This is required for jsonschema.""" - import importlib - import pkgutil + Currently, we just import our main libraries and invoke + mpv/lame on macOS, which is slow on the first run, and doing + it this way shows progress being made. + """ - def get_data_custom(package: str, resource: str) -> bytes | None: - try: - module = importlib.import_module(package) - reader = module.__loader__.get_resource_reader(package) # type: ignore - with reader.open_resource(resource) as f: - return f.read() - except Exception: - return None - - pkgutil.get_data = get_data_custom - - -def _patch_certifi() -> None: - """Tell certifi (and thus requests) to use a file in our package folder. - - By default it creates a copy of the data in a temporary folder, which then gets - cleaned up by macOS's temp file cleaner.""" - import certifi - - def where() -> str: - prefix = Path(sys.prefix) - if sys.platform == "darwin": - path = prefix / "../Resources/certifi/cacert.pem" - else: - path = prefix / "lib" / "certifi" / "cacert.pem" - return str(path) - - certifi.where = where - - -def _fix_protobuf_path() -> None: - sys.path.append(str(Path(sys.prefix) / "../Resources")) - - -def packaged_build_setup() -> None: - if not getattr(sys, "frozen", False): + if not is_mac: return - print("Initial setup...") + def _dot(): + print(".", flush=True, end="") - if sys.platform == "win32": - _fix_pywin32() - elif sys.platform == "darwin": - _fix_protobuf_path() + _dot() + import anki.collection - _patch_pkgutil() - _patch_certifi() + _dot() + import PyQt6.sip - # escape hatch for debugging issues with packaged build startup - if os.getenv("ANKI_STARTUP_REPL"): - # mypy incorrectly thinks this does not exist on Windows - is_tty = os.isatty(sys.stdin.fileno()) # type: ignore - if is_tty: - import code + _dot() + import PyQt6.QtCore - code.InteractiveConsole().interact() - sys.exit(0) + _dot() + import PyQt6.QtGui + + _dot() + import PyQt6.QtNetwork + + _dot() + import PyQt6.QtQuick + + _dot() + import PyQt6.QtWebChannel + + _dot() + import PyQt6.QtWebEngineCore + + _dot() + import PyQt6.QtWebEngineWidgets + + _dot() + import anki_audio + import PyQt6.QtWidgets + + audio_pkg_path = Path(anki_audio.__file__).parent + + # Invoke mpv and lame + cmd = [Path(""), "--version"] + for cmd_name in ["mpv", "lame"]: + _dot() + cmd[0] = audio_pkg_path / cmd_name + subprocess.run([str(cmd[0]), str(cmd[1])], check=True, capture_output=True) + + print() diff --git a/qt/aqt/qt/qt6.py b/qt/aqt/qt/qt6.py index df79d6b1a..2d387aabf 100644 --- a/qt/aqt/qt/qt6.py +++ b/qt/aqt/qt/qt6.py @@ -12,7 +12,7 @@ from PyQt6 import sip from PyQt6.QtCore import * # conflicting Qt and qFuzzyCompare definitions require an ignore -from PyQt6.QtGui import * # type: ignore[misc,assignment] +from PyQt6.QtGui import * # type: ignore[no-redef,assignment] from PyQt6.QtNetwork import QLocalServer, QLocalSocket, QNetworkProxy from PyQt6.QtQuick import * from PyQt6.QtWebChannel import QWebChannel diff --git a/qt/aqt/reviewer.py b/qt/aqt/reviewer.py index 05e9becf4..6e34a7931 100644 --- a/qt/aqt/reviewer.py +++ b/qt/aqt/reviewer.py @@ -18,7 +18,10 @@ import aqt.operations from anki.cards import Card, CardId from anki.collection import Config, OpChanges, OpChangesWithCount from anki.scheduler.base import ScheduleCardsAsNew -from anki.scheduler.v3 import CardAnswer, QueuedCards +from anki.scheduler.v3 import ( + CardAnswer, + QueuedCards, +) from anki.scheduler.v3 import Scheduler as V3Scheduler from anki.scheduler.v3 import ( SchedulingContext, diff --git a/qt/aqt/sound.py b/qt/aqt/sound.py index 386767a30..5753ab234 100644 --- a/qt/aqt/sound.py +++ b/qt/aqt/sound.py @@ -279,12 +279,25 @@ def _packagedCmd(cmd: list[str]) -> tuple[Any, dict[str, str]]: if "LD_LIBRARY_PATH" in env and "SNAP" not in env: del env["LD_LIBRARY_PATH"] - if is_win: - packaged_path = Path(sys.prefix) / (cmd[0] + ".exe") - elif is_mac: - packaged_path = Path(sys.prefix) / ".." / "Resources" / cmd[0] - else: - packaged_path = Path(sys.prefix) / cmd[0] + # Try to find binary in anki-audio package for Windows/Mac + if is_win or is_mac: + try: + import anki_audio + + audio_pkg_path = Path(anki_audio.__file__).parent + if is_win: + packaged_path = audio_pkg_path / (cmd[0] + ".exe") + else: # is_mac + packaged_path = audio_pkg_path / cmd[0] + + if packaged_path.exists(): + cmd[0] = str(packaged_path) + return cmd, env + except ImportError: + # anki-audio not available, fall back to old behavior + pass + + packaged_path = Path(sys.prefix) / cmd[0] if packaged_path.exists(): cmd[0] = str(packaged_path) diff --git a/qt/aqt/stylesheets.py b/qt/aqt/stylesheets.py index 35e47ef0d..0721e76d2 100644 --- a/qt/aqt/stylesheets.py +++ b/qt/aqt/stylesheets.py @@ -120,7 +120,7 @@ class CustomStyles: QLabel:disabled {{ color: {tm.var(colors.FG_DISABLED)}; }} - QToolTip {{ color: {tm.var(colors.FG)}; background-color: {tm.var(colors.CANVAS)}; }} + QToolTip {{ color: {tm.var(colors.FG)}; background-color: {tm.var(colors.CANVAS)}; }} """ def menu(self, tm: ThemeManager) -> str: @@ -404,18 +404,18 @@ class CustomStyles: }; }} QHeaderView::section:first {{ - border-left: 1px solid {tm.var(colors.BORDER_SUBTLE)}; + border-left: 1px solid {tm.var(colors.BORDER_SUBTLE)}; border-top-left-radius: {tm.var(props.BORDER_RADIUS)}; }} QHeaderView::section:!first {{ border-left: none; }} QHeaderView::section:last {{ - border-right: 1px solid {tm.var(colors.BORDER_SUBTLE)}; + border-right: 1px solid {tm.var(colors.BORDER_SUBTLE)}; border-top-right-radius: {tm.var(props.BORDER_RADIUS)}; }} QHeaderView::section:only-one {{ - border-left: 1px solid {tm.var(colors.BORDER_SUBTLE)}; + border-left: 1px solid {tm.var(colors.BORDER_SUBTLE)}; border-right: 1px solid {tm.var(colors.BORDER_SUBTLE)}; border-top-left-radius: {tm.var(props.BORDER_RADIUS)}; border-top-right-radius: {tm.var(props.BORDER_RADIUS)}; @@ -579,19 +579,19 @@ class CustomStyles: }} QScrollBar::handle:pressed {{ background-color: {tm.var(colors.SCROLLBAR_BG_ACTIVE)}; - }} + }} QScrollBar:horizontal {{ height: 12px; }} QScrollBar::handle:horizontal {{ min-width: 60px; - }} + }} QScrollBar:vertical {{ width: 12px; }} QScrollBar::handle:vertical {{ min-height: 60px; - }} + }} QScrollBar::add-line {{ border: none; background: none; diff --git a/qt/aqt/utils.py b/qt/aqt/utils.py index 6ae8bace8..e17550fc0 100644 --- a/qt/aqt/utils.py +++ b/qt/aqt/utils.py @@ -87,24 +87,15 @@ if TYPE_CHECKING: def aqt_data_path() -> Path: - # packaged? - if getattr(sys, "frozen", False): - prefix = Path(sys.prefix) - path = prefix / "lib/_aqt/data" - if path.exists(): - return path - else: - return prefix / "../Resources/_aqt/data" - else: - import _aqt.colors + import _aqt.colors - data_folder = Path(inspect.getfile(_aqt.colors)).with_name("data") - if data_folder.exists(): - return data_folder.absolute() - else: - # should only happen when running unit tests - print("warning, data folder not found") - return Path(".") + data_folder = Path(inspect.getfile(_aqt.colors)).with_name("data") + if data_folder.exists(): + return data_folder.absolute() + else: + # should only happen when running unit tests + print("warning, data folder not found") + return Path(".") def aqt_data_folder() -> str: @@ -1207,12 +1198,11 @@ def supportText() -> str: platname = platform.platform() return """\ -Anki {} {} {} +Anki {} {} Python {} Qt {} PyQt {} Platform: {} """.format( version_with_build(), - "(src)" if not getattr(sys, "frozen", False) else "", "(ao)" if mw.addonManager.dirty else "", platform.python_version(), qVersion(), diff --git a/qt/bundle/Cargo.lock b/qt/bundle/Cargo.lock deleted file mode 100644 index 544276d6f..000000000 --- a/qt/bundle/Cargo.lock +++ /dev/null @@ -1,629 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aho-corasick" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] - -[[package]] -name = "anki" -version = "0.0.0" -dependencies = [ - "embed-resource", - "jemallocator", - "libc", - "libc-stdhandle", - "mimalloc", - "pyembed", - "snmalloc-rs", - "winapi", -] - -[[package]] -name = "anyhow" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base64" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -dependencies = [ - "byteorder", -] - -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "cc" -version = "1.0.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "charset" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f426e64df1c3de26cbf44593c6ffff5dbfd43bbf9de0d075058558126b3fc73" -dependencies = [ - "base64 0.10.1", - "encoding_rs", -] - -[[package]] -name = "cmake" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b858541263efe664aead4a5209a4ae5c5d2811167d4ed4ee0944503f8d2089" -dependencies = [ - "cc", -] - -[[package]] -name = "cty" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" - -[[package]] -name = "dunce" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541" - -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "embed-resource" -version = "1.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85505eb239fc952b300f29f0556d2d884082a83566768d980278d8faf38c780d" -dependencies = [ - "cc", - "vswhom", - "winreg", -] - -[[package]] -name = "encoding_rs" -version = "0.8.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a74ea89a0a1b98f6332de42c95baff457ada66d1cb4030f9ff151b2041a1c746" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "fs_extra" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" - -[[package]] -name = "indoc" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3" - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "itertools" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" -dependencies = [ - "either", -] - -[[package]] -name = "jemalloc-sys" -version = "0.5.2+5.3.0-patched" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134163979b6eed9564c98637b710b40979939ba351f59952708234ea11b5f3f8" -dependencies = [ - "cc", - "fs_extra", - "libc", -] - -[[package]] -name = "jemallocator" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c2514137880c52b0b4822b563fadd38257c1f380858addb74a400889696ea6" -dependencies = [ - "jemalloc-sys", - "libc", -] - -[[package]] -name = "libc" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013" - -[[package]] -name = "libc-stdhandle" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dac2473dc28934c5e0b82250dab231c9d3b94160d91fe9ff483323b05797551" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "libmimalloc-sys" -version = "0.1.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44" -dependencies = [ - "cc", - "cty", - "libc", -] - -[[package]] -name = "lock_api" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "mailparse" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee6e1ca1c8396da58f8128176f6980dd57bec84c8670a479519d3655f2d6734" -dependencies = [ - "base64 0.13.0", - "charset", - "quoted_printable", -] - -[[package]] -name = "memchr" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" - -[[package]] -name = "memmap2" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memory-module-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bbdce2925c681860b08875119254fb5543dbf6337c56ff93afebeed9c686da3" -dependencies = [ - "cc", - "libc", - "winapi", -] - -[[package]] -name = "mimalloc" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633" -dependencies = [ - "libmimalloc-sys", -] - -[[package]] -name = "once_cell" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", -] - -[[package]] -name = "proc-macro2" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "pyembed" -version = "0.24.0-pre" -dependencies = [ - "anyhow", - "dunce", - "jemalloc-sys", - "libc", - "libmimalloc-sys", - "once_cell", - "pyo3", - "pyo3-build-config", - "python-oxidized-importer", - "python-packaging", - "snmalloc-sys", -] - -[[package]] -name = "pyo3" -version = "0.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "268be0c73583c183f2b14052337465768c07726936a260f480f0857cb95ba543" -dependencies = [ - "cfg-if", - "indoc", - "libc", - "memoffset", - "parking_lot", - "pyo3-build-config", - "pyo3-ffi", - "pyo3-macros", - "unindent", -] - -[[package]] -name = "pyo3-build-config" -version = "0.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28fcd1e73f06ec85bf3280c48c67e731d8290ad3d730f8be9dc07946923005c8" -dependencies = [ - "once_cell", - "target-lexicon", -] - -[[package]] -name = "pyo3-ffi" -version = "0.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6cb136e222e49115b3c51c32792886defbfb0adead26a688142b346a0b9ffc" -dependencies = [ - "libc", - "pyo3-build-config", -] - -[[package]] -name = "pyo3-macros" -version = "0.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94144a1266e236b1c932682136dc35a9dee8d3589728f68130c7c3861ef96b28" -dependencies = [ - "proc-macro2", - "pyo3-macros-backend", - "quote", - "syn", -] - -[[package]] -name = "pyo3-macros-backend" -version = "0.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8df9be978a2d2f0cdebabb03206ed73b11314701a5bfe71b0d753b81997777f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "python-oxidized-importer" -version = "0.9.0-pre" -dependencies = [ - "anyhow", - "memmap2", - "memory-module-sys", - "once_cell", - "pyo3", - "python-packaging", - "python-packed-resources", - "simple-file-manifest", - "winapi", -] - -[[package]] -name = "python-packaging" -version = "0.16.0-pre" -dependencies = [ - "anyhow", - "byteorder", - "encoding_rs", - "itertools", - "mailparse", - "once_cell", - "python-packed-resources", - "regex", - "simple-file-manifest", - "spdx", - "walkdir", -] - -[[package]] -name = "python-packed-resources" -version = "0.12.0-pre" -dependencies = [ - "anyhow", - "byteorder", -] - -[[package]] -name = "quote" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "quoted_printable" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1238256b09923649ec89b08104c4dfe9f6cb2fea734a5db5384e44916d59e9c5" - -[[package]] -name = "redox_syscall" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "simple-file-manifest" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd19be0257552dd56d1bb6946f89f193c6e5b9f13cc9327c4bc84a357507c74" - -[[package]] -name = "smallvec" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" - -[[package]] -name = "snmalloc-rs" -version = "0.2.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36acaace2719c972eab3ef6a6b3aee4495f0bf300f59715bb9cff6c5acf4ae20" -dependencies = [ - "snmalloc-sys", -] - -[[package]] -name = "snmalloc-sys" -version = "0.2.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35a7e6e7d5fe756bee058ddedefc7e0a9f9c8dbaa9401b48ed3c17d6578e40b5" -dependencies = [ - "cc", - "cmake", -] - -[[package]] -name = "spdx" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a346909b3fd07776f9b96b98d4a58e3666f831c9a672c279b10f795a34c36425" -dependencies = [ - "smallvec", -] - -[[package]] -name = "syn" -version = "1.0.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "target-lexicon" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "unindent" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" - -[[package]] -name = "vswhom" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" -dependencies = [ - "libc", - "vswhom-sys", -] - -[[package]] -name = "vswhom-sys" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f5402d3d0e79a069714f7b48e3ecc60be7775a2c049cb839457457a239532" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "walkdir" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] diff --git a/qt/bundle/Cargo.toml b/qt/bundle/Cargo.toml deleted file mode 100644 index 920e890d0..000000000 --- a/qt/bundle/Cargo.toml +++ /dev/null @@ -1,60 +0,0 @@ -[package] -name = "anki" -version = "0.0.0" -authors = ["Ankitects Pty Ltd and contributors "] -build = "build.rs" -edition = "2021" -license = "AGPL-3.0-or-later" -publish = false -rust-version = "1.64" - -[dependencies] -pyembed = { path = "./PyOxidizer/pyembed", default-features = false } - -[target.'cfg(windows)'.dependencies] -winapi = { version = "0.3", features = ["wincon"] } -libc = "0.2" -libc-stdhandle = "=0.1.0" - -[dependencies.jemallocator] -version = "0.5" -optional = true - -[dependencies.mimalloc] -version = "0.1" -optional = true -features = ["local_dynamic_tls", "override", "secure"] - -[dependencies.snmalloc-rs] -version = "0.2" -optional = true - -[build-dependencies] -embed-resource = "1.6" - -[features] -default = ["build-mode-standalone"] - -global-allocator-jemalloc = ["jemallocator"] -global-allocator-mimalloc = ["mimalloc"] -global-allocator-snmalloc = ["snmalloc-rs"] - -allocator-jemalloc = ["pyembed/allocator-jemalloc"] -allocator-mimalloc = ["pyembed/allocator-mimalloc"] -allocator-snmalloc = ["pyembed/allocator-snmalloc"] - -# Build this crate in isolation, without using PyOxidizer. -build-mode-standalone = [] - -# Build this crate by executing a `pyoxidizer` executable to build -# required artifacts. -build-mode-pyoxidizer-exe = [] - -# Build this crate by reusing artifacts generated by `pyoxidizer` out-of-band. -# In this mode, the PYOXIDIZER_ARTIFACT_DIR environment variable can refer -# to the directory containing build artifacts produced by `pyoxidizer`. If not -# set, OUT_DIR will be used. -build-mode-prebuilt-artifacts = [] - -[profile.release] -lto = true diff --git a/qt/bundle/PyOxidizer b/qt/bundle/PyOxidizer deleted file mode 160000 index 12a249f68..000000000 --- a/qt/bundle/PyOxidizer +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 12a249f686484c5e212ba800e1e7f18c7c4b1b27 diff --git a/qt/bundle/build.rs b/qt/bundle/build.rs deleted file mode 100644 index 4b0318dbf..000000000 --- a/qt/bundle/build.rs +++ /dev/null @@ -1,95 +0,0 @@ -// Based off PyOxidizer's 'init-rust-project'. -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -use std::path::{Path, PathBuf}; - -use embed_resource; - -const DEFAULT_PYTHON_CONFIG_FILENAME: &str = "default_python_config.rs"; -const DEFAULT_PYTHON_CONFIG: &str = "\ -pub fn default_python_config<'a>() -> pyembed::OxidizedPythonInterpreterConfig<'a> { - pyembed::OxidizedPythonInterpreterConfig::default() -} -"; - -/// Build by calling a `pyoxidizer` executable to generate build artifacts. -fn build_with_pyoxidizer_exe(exe: Option, resolve_target: Option<&str>) { - let pyoxidizer_exe = if let Some(path) = exe { - path - } else { - "pyoxidizer".to_string() - }; - - let mut args = vec!["run-build-script", "build.rs"]; - if let Some(target) = resolve_target { - args.push("--target"); - args.push(target); - } - - match std::process::Command::new(pyoxidizer_exe) - .args(args) - .status() - { - Ok(status) => { - if !status.success() { - panic!("`pyoxidizer run-build-script` failed"); - } - } - Err(e) => panic!("`pyoxidizer run-build-script` failed: {}", e.to_string()), - } -} - -#[allow(clippy::if_same_then_else)] -fn main() { - if std::env::var("CARGO_FEATURE_BUILD_MODE_STANDALONE").is_ok() { - let path = PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR not defined")); - let path = path.join(DEFAULT_PYTHON_CONFIG_FILENAME); - - std::fs::write(&path, DEFAULT_PYTHON_CONFIG.as_bytes()) - .expect("failed to write default python config"); - println!( - "cargo:rustc-env=DEFAULT_PYTHON_CONFIG_RS={}", - path.display() - ); - } else if std::env::var("CARGO_FEATURE_BUILD_MODE_PYOXIDIZER_EXE").is_ok() { - let target = if let Ok(target) = std::env::var("PYOXIDIZER_BUILD_TARGET") { - Some(target) - } else { - None - }; - - build_with_pyoxidizer_exe( - std::env::var("PYOXIDIZER_EXE").ok(), - target.as_ref().map(|target| target.as_ref()), - ); - } else if std::env::var("CARGO_FEATURE_BUILD_MODE_PREBUILT_ARTIFACTS").is_ok() { - // relative to src/ - let artifacts = Path::new("../../../out/bundle/artifacts/"); - let config_rs = artifacts.join("default_python_config.rs"); - println!( - "cargo:rustc-env=DEFAULT_PYTHON_CONFIG_RS={}", - config_rs.display() - ); - let config_txt = artifacts.join("pyo3-build-config-file.txt"); - println!("cargo:rustc-env=PYO3_CONFIG_FILE={}", config_txt.display()); - - let link_arg = if cfg!(target_os = "macos") { - "-rdynamic" - } else { - "-Wl,-export-dynamic" - }; - println!("cargo:rustc-link-arg={link_arg}"); - } else { - panic!("build-mode-* feature not set"); - } - - let target_family = - std::env::var("CARGO_CFG_TARGET_FAMILY").expect("CARGO_CFG_TARGET_FAMILY not defined"); - - // embed manifest and icon - if target_family == "windows" { - embed_resource::compile("win/anki-manifest.rc"); - } -} diff --git a/qt/bundle/mac/Cargo.toml b/qt/bundle/mac/Cargo.toml deleted file mode 100644 index a154b76f7..000000000 --- a/qt/bundle/mac/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "makeapp" -version.workspace = true -authors.workspace = true -edition.workspace = true -license.workspace = true -publish = false -rust-version.workspace = true - -[dependencies] -anyhow.workspace = true -apple-bundles.workspace = true -camino.workspace = true -clap.workspace = true -glob.workspace = true -plist.workspace = true -serde.workspace = true -serde_json.workspace = true -simple-file-manifest.workspace = true -walkdir.workspace = true diff --git a/qt/bundle/mac/src/codesign.rs b/qt/bundle/mac/src/codesign.rs deleted file mode 100644 index fb251521f..000000000 --- a/qt/bundle/mac/src/codesign.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright: Ankitects Pty Ltd and contributors -// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -use std::env; -use std::process::Command; - -use anyhow::bail; -use anyhow::Result; -use camino::Utf8Path; -use camino::Utf8PathBuf; - -const CODESIGN_ARGS: &[&str] = &["-vvvv", "-o", "runtime", "-s", "Developer ID Application:"]; - -pub fn codesign_python_libs(bundle_dir: &Utf8PathBuf) -> Result<()> { - for entry in glob::glob(bundle_dir.join("Contents/MacOS/lib/**/*.so").as_str())? { - let entry = entry?; - let entry = Utf8PathBuf::from_path_buf(entry).unwrap(); - codesign_file(&entry, &[])?; - } - codesign_file(&bundle_dir.join("Contents/MacOS/libankihelper.dylib"), &[]) -} - -pub fn codesign_app(bundle_dir: &Utf8PathBuf) -> Result<()> { - codesign_file( - bundle_dir, - &["--entitlements", "qt/bundle/mac/entitlements.python.xml"], - ) -} - -fn codesign_file(path: &Utf8Path, extra_args: &[&str]) -> Result<()> { - if env::var("ANKI_CODESIGN").is_ok() { - let status = Command::new("codesign") - .args(CODESIGN_ARGS) - .args(extra_args) - .arg(path.as_str()) - .status()?; - if !status.success() { - bail!("codesign failed"); - } - } - - Ok(()) -} diff --git a/qt/bundle/mac/src/dmg.rs b/qt/bundle/mac/src/dmg.rs deleted file mode 100644 index 862150340..000000000 --- a/qt/bundle/mac/src/dmg.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright: Ankitects Pty Ltd and contributors -// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -use std::fs; -use std::process::Command; - -use anyhow::Context; -use anyhow::Result; -use camino::Utf8Path; -use camino::Utf8PathBuf; -use clap::Args; - -use crate::notarize::wait_then_staple_app; - -#[derive(Args)] -pub struct BuildDmgsArgs { - qt6_dmg: Utf8PathBuf, - qt5_dmg: Option, -} - -pub fn make_dmgs(args: BuildDmgsArgs) -> Result<()> { - let root = Utf8Path::new("out/bundle/app"); - let mut variants = vec![("std", args.qt6_dmg)]; - if let Some(alt) = args.qt5_dmg { - variants.push(("alt", alt)); - } - - for (variant, dmg) in variants { - let app = root.join(variant).join("Anki.app"); - if std::env::var("ANKI_CODESIGN").is_ok() { - let uuid = fs::read_to_string(app.with_extension("uuid")).context("read uuid")?; - wait_then_staple_app(&app, uuid)?; - } - - make_dmg(&app, &dmg)?; - } - - Ok(()) -} - -fn make_dmg(app_folder: &Utf8Path, dmg: &Utf8Path) -> Result<()> { - assert!( - Command::new("qt/bundle/mac/dmg/build.sh") - .args([app_folder.parent().unwrap().as_str(), dmg.as_str()]) - .status() - .context("dmg")? - .success(), - "dmg" - ); - Ok(()) -} diff --git a/qt/bundle/mac/src/main.rs b/qt/bundle/mac/src/main.rs deleted file mode 100644 index 2182d808e..000000000 --- a/qt/bundle/mac/src/main.rs +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright: Ankitects Pty Ltd and contributors -// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -#![cfg(unix)] - -//! Munge the output of PyOxidizer into a macOS app bundle, and combine it -//! with our other runtime dependencies. - -mod codesign; -mod dmg; -mod notarize; - -use std::env; -use std::fs; -use std::os::unix::prelude::PermissionsExt; -use std::process::Command; - -use anyhow::bail; -use anyhow::Result; -use apple_bundles::MacOsApplicationBundleBuilder; -use camino::Utf8Path; -use camino::Utf8PathBuf; -use clap::Parser; -use clap::Subcommand; -use clap::ValueEnum; -use codesign::codesign_app; -use codesign::codesign_python_libs; -use dmg::make_dmgs; -use dmg::BuildDmgsArgs; -use notarize::notarize_app; -use plist::Value; -use simple_file_manifest::FileEntry; -use walkdir::WalkDir; - -#[derive(Clone, ValueEnum)] -enum DistKind { - Standard, - Alternate, -} - -impl DistKind { - fn folder_name(&self) -> &'static str { - match self { - DistKind::Standard => "std", - DistKind::Alternate => "alt", - } - } - - fn input_folder(&self) -> Utf8PathBuf { - Utf8Path::new("out/bundle").join(self.folder_name()) - } - - fn output_folder(&self) -> Utf8PathBuf { - Utf8Path::new("out/bundle/app") - .join(self.folder_name()) - .join("Anki.app") - } - - fn macos_min(&self) -> &str { - match self { - DistKind::Standard => { - if env::var("MAC_X86").is_ok() { - "11" - } else { - "12" - } - } - DistKind::Alternate => "10.13.4", - } - } - - fn qt_repo(&self) -> &Utf8Path { - Utf8Path::new(match self { - DistKind::Standard => { - if cfg!(target_arch = "aarch64") && env::var("MAC_X86").is_err() { - "out/extracted/mac_arm_qt6" - } else { - "out/extracted/mac_amd_qt6" - } - } - DistKind::Alternate => "out/extracted/mac_amd_qt5", - }) - } -} - -#[derive(Parser)] -struct Cli { - #[command(subcommand)] - command: Commands, -} - -#[derive(Subcommand)] -enum Commands { - BuildApp { - version: String, - kind: DistKind, - stamp: Utf8PathBuf, - }, - BuildDmgs(BuildDmgsArgs), -} - -fn main() -> Result<()> { - match Cli::parse().command { - Commands::BuildApp { - version, - kind, - stamp, - } => { - let plist = get_plist(&version); - make_app(kind, plist, &stamp) - } - Commands::BuildDmgs(args) => make_dmgs(args), - } -} - -fn make_app(kind: DistKind, mut plist: plist::Dictionary, stamp: &Utf8Path) -> Result<()> { - let input_folder = kind.input_folder(); - let output_folder = kind.output_folder(); - let output_variant = output_folder.parent().unwrap(); - if output_variant.exists() { - fs::remove_dir_all(output_variant)?; - } - fs::create_dir_all(&output_folder)?; - - let mut builder = MacOsApplicationBundleBuilder::new("Anki")?; - plist.insert( - "LSMinimumSystemVersion".into(), - Value::from(kind.macos_min()), - ); - builder.set_info_plist_from_dictionary(plist)?; - builder.add_file_resources("Assets.car", &include_bytes!("../icon/Assets.car")[..])?; - - for entry in WalkDir::new(&input_folder) - .into_iter() - .map(Result::unwrap) - .filter(|e| !e.file_type().is_dir()) - { - let path = entry.path(); - let entry = FileEntry::try_from(path)?; - let relative_path = path.strip_prefix(&input_folder)?; - let path_str = relative_path.to_str().unwrap(); - if path_str.contains("libankihelper") { - builder.add_file_macos("libankihelper.dylib", entry)?; - } else if path_str.contains("aqt/data") - || path_str.contains("certifi") - || path_str.contains("google/protobuf") - { - builder.add_file_resources(relative_path.strip_prefix("lib").unwrap(), entry)?; - } else { - if path_str.contains("__pycache__") { - continue; - } - builder.add_file_macos(relative_path, entry)?; - } - } - - builder.files().materialize_files(&output_folder)?; - fix_rpath(output_folder.join("Contents/MacOS/anki"))?; - codesign_python_libs(&output_folder)?; - copy_in_audio(&output_folder)?; - copy_in_qt(&output_folder, kind)?; - codesign_app(&output_folder)?; - fixup_perms(&output_folder)?; - notarize_app(&output_folder)?; - fs::write(stamp, b"")?; - - Ok(()) -} - -/// The bundle builder writes some files without world read/execute perms, -/// which prevents them from being opened by a non-admin user. -fn fixup_perms(dir: &Utf8Path) -> Result<()> { - let status = Command::new("find") - .arg(dir) - .args(["-not", "-perm", "-a=r", "-exec", "chmod", "a+r", "{}", ";"]) - .status()?; - if !status.success() { - bail!("error setting perms"); - } - fs::set_permissions( - dir.join("Contents/MacOS/anki"), - PermissionsExt::from_mode(0o755), - )?; - Ok(()) -} - -/// Copy everything at the provided path into the Contents/ folder of our app. -fn extend_app_contents(source: &Utf8Path, target_dir: &Utf8Path) -> Result<()> { - let status = Command::new("rsync") - .arg("-a") - .arg(format!("{}/", source.as_str())) - .arg(target_dir) - .status()?; - if !status.success() { - bail!("error syncing {source:?}"); - } - Ok(()) -} - -fn copy_in_audio(bundle_dir: &Utf8Path) -> Result<()> { - println!("Copying in audio..."); - - let src_folder = Utf8Path::new( - if cfg!(target_arch = "aarch64") && env::var("MAC_X86").is_err() { - "out/extracted/mac_arm_audio" - } else { - "out/extracted/mac_amd_audio" - }, - ); - extend_app_contents(src_folder, &bundle_dir.join("Contents/Resources")) -} - -fn copy_in_qt(bundle_dir: &Utf8Path, kind: DistKind) -> Result<()> { - println!("Copying in Qt..."); - extend_app_contents(kind.qt_repo(), &bundle_dir.join("Contents")) -} - -fn fix_rpath(exe_path: Utf8PathBuf) -> Result<()> { - let status = Command::new("install_name_tool") - .arg("-add_rpath") - .arg("@executable_path/../Frameworks") - .arg(exe_path.as_str()) - .status()?; - assert!(status.success()); - Ok(()) -} - -fn get_plist(anki_version: &str) -> plist::Dictionary { - let reader = std::io::Cursor::new(include_bytes!("Info.plist")); - let mut plist = Value::from_reader(reader) - .unwrap() - .into_dictionary() - .unwrap(); - plist.insert( - "CFBundleShortVersionString".into(), - Value::from(anki_version), - ); - plist -} diff --git a/qt/bundle/mac/src/notarize.rs b/qt/bundle/mac/src/notarize.rs deleted file mode 100644 index 0688354f9..000000000 --- a/qt/bundle/mac/src/notarize.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright: Ankitects Pty Ltd and contributors -// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -use std::env; -use std::fs; -use std::process::Command; - -use anyhow::bail; -use anyhow::Context; -use anyhow::Result; -use camino::Utf8Path; -use serde::Deserialize; - -#[derive(Deserialize)] -struct NotarySubmitOutput { - id: String, -} - -pub fn notarize_app(output_folder: &Utf8Path) -> Result<()> { - if env::var("ANKI_CODESIGN").is_err() { - return Ok(()); - } - if env::var("ANKI_NO_NOTARIZE").is_ok() { - return Ok(()); - } - let zip_file = output_folder.with_extension("zip"); - assert!( - Command::new("ditto") - .args([ - "-c", - "-k", - "--keepParent", - output_folder.as_str(), - zip_file.as_str(), - ]) - .status() - .unwrap() - .success(), - "zip build" - ); - let output = Command::new("xcrun") - .args([ - "notarytool", - "submit", - zip_file.as_str(), - "-f", - "json", - "-p", - "default", - ]) - .output() - .expect("notarytool"); - if !output.status.success() { - panic!( - "notarytool submit failed: {} {}", - String::from_utf8_lossy(&output.stderr), - String::from_utf8_lossy(&output.stdout) - ) - } - let output: NotarySubmitOutput = match serde_json::from_slice(&output.stdout) { - Ok(out) => out, - Err(err) => panic!( - "unable to parse notary output: {err} {} {}", - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ), - }; - let uuid_path = output_folder.with_extension("uuid"); - fs::write(uuid_path, output.id).expect("write uuid"); - Ok(()) -} - -#[derive(Deserialize)] -struct NotaryWaitOutput { - status: String, -} - -pub fn wait_then_staple_app(app: &Utf8Path, uuid: String) -> Result<()> { - let output = Command::new("xcrun") - .args(["notarytool", "wait", &uuid, "-p", "default", "-f", "json"]) - .output() - .context("notary wait")?; - let output: NotaryWaitOutput = serde_json::from_slice(&output.stdout) - .with_context(|| String::from_utf8_lossy(&output.stderr).to_string())?; - if output.status != "Accepted" { - bail!("unexpected status: {}", output.status); - } - - assert!( - Command::new("xcrun") - .args(["stapler", "staple", app.as_str()]) - .status() - .context("staple")? - .success(), - "staple" - ); - - // clean up temporary files - fs::remove_file(app.with_extension("zip")).context("app.zip")?; - fs::remove_file(app.with_extension("uuid")).context("app.uuid")?; - - Ok(()) -} diff --git a/qt/bundle/pyoxidizer.bzl b/qt/bundle/pyoxidizer.bzl deleted file mode 100644 index dc4870606..000000000 --- a/qt/bundle/pyoxidizer.bzl +++ /dev/null @@ -1,189 +0,0 @@ -# type: ignore - -set_build_path(VARS.get("build")) - -excluded_source_prefixes = [ - "ctypes.test", - "distutils.tests", - "idlelib", - "lib2to3.tests", - "test", - "tkinter", - "win32comext", - "win32com", - "win32", - "pythonwin", - "PyQt6", - "pip", - "setuptools", - "google" -] - -excluded_resource_suffixes = [ - ".pyi", - ".pyc", - "py.typed", -] - -included_resource_packages = [ - "anki", - "aqt", - "_aqt", - "lib2to3", - "certifi", - "jsonschema", -] - - -def handle_resource(policy, resource): - if type(resource) == "PythonModuleSource": - resource.add_include = True - for prefix in excluded_source_prefixes: - if resource.name.startswith(prefix) and not resource.name.startswith("pip_system_certs"): - resource.add_include = False - - # if resource.add_include: - # print("src", resource.name, resource.add_include) - - elif type(resource) == "PythonExtensionModule": - resource.add_include = True - if resource.name.startswith("win32") or resource.name.startswith("PyQt6"): - resource.add_include = False - - # print("ext", resource.name, resource.add_include) - - elif type(resource) == "PythonPackageResource": - for prefix in included_resource_packages: - if resource.package.startswith(prefix): - resource.add_include = True - if resource.package == "certifi": - resource.add_location = "filesystem-relative:lib" - for suffix in excluded_resource_suffixes: - if resource.name.endswith(suffix): - resource.add_include = False - - # aqt web resources can be stored in binary - if resource.package.endswith("aqt"): - if not resource.name.startswith("data/web"): - resource.add_location = "filesystem-relative:lib" - - # if resource.add_include: - # print("rsrc", resource.package, resource.name, resource.add_include) - - elif type(resource) == "PythonPackageDistributionResource": - # print("dist", resource.package, resource.name, resource.add_include) - pass - - # elif type(resource) == "File": - # print(resource.path) - - elif type(resource) == "File": - if ( - resource.path.startswith("win32") - or resource.path.startswith("pythonwin") - or resource.path.startswith("pywin32") - ): - exclude = ( - "tests" in resource.path - or "benchmark" in resource.path - or "__pycache__" in resource.path - ) - if not exclude: - # print("add", resource.path) - resource.add_include = True - resource.add_location = "filesystem-relative:lib" - - if ".dist-info" in resource.path: - resource.add_include = False - - else: - print("unexpected type", type(resource)) - - -def make_exe(): - if BUILD_TARGET_TRIPLE == "x86_64-unknown-linux-gnu": - dist = PythonDistribution( - url = "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.9.18+20240107-x86_64_v2-unknown-linux-gnu-pgo-full.tar.zst", - sha256 = "7ccdc1b19599a6660040ec2f0ade755b32bb45c897ea75d0b7826236146b78cf", - ) - elif BUILD_TARGET_TRIPLE == "x86_64-apple-darwin": - dist = PythonDistribution( - url = "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.9.18+20240107-x86_64-apple-darwin-pgo-full.tar.zst", - sha256 = "b2f06f0f0ebbbed0eae87a6e8eede2e0d838735386a8b84257d4f02d16b9baec", - ) - elif BUILD_TARGET_TRIPLE == "aarch64-apple-darwin": - dist = PythonDistribution( - url = "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.9.18+20240107-aarch64-apple-darwin-pgo-full.tar.zst", - sha256 = "154dfa7cd6f9a6047a58811f84bef69b019ea459e5b42991c8af63e1285b445f", - ) - elif BUILD_TARGET_TRIPLE == "x86_64-pc-windows-msvc": - dist = PythonDistribution( - url = "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.9.18+20240107-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", - sha256 = "3b9c7d6ed94260b83ed8f44ee9a7b8fce392259ce6591e538601f7353061a884", - ) - else: - fail("unexpected arch") - - policy = dist.make_python_packaging_policy() - - policy.file_scanner_classify_files = True - policy.include_classified_resources = False - - policy.allow_files = True - policy.file_scanner_emit_files = True - policy.include_file_resources = False - - policy.include_distribution_sources = False - policy.include_distribution_resources = False - policy.include_non_distribution_sources = False - policy.include_test = False - - policy.resources_location = "in-memory" - policy.resources_location_fallback = "filesystem-relative:lib" - - policy.register_resource_callback(handle_resource) - - policy.bytecode_optimize_level_zero = False - policy.bytecode_optimize_level_two = True - - python_config = dist.make_python_interpreter_config() - - # detected libs do not need this, but we add extra afterwards - python_config.module_search_paths = ["$ORIGIN/lib"] - python_config.optimization_level = 2 - - python_config.run_command = "import aqt; aqt.run()" - - exe = dist.to_python_executable( - name="anki", - packaging_policy=policy, - config=python_config, - ) - - exe.windows_runtime_dlls_mode = "always" - - # set in main.rs - exe.windows_subsystem = "console" - - resources = exe.read_virtualenv(VARS.get("venv")) - exe.add_python_resources(resources) - - return exe - - -def make_embedded_resources(exe): - return exe.to_embedded_resources() - - -def make_install(exe): - files = FileManifest() - files.add_python_resource(".", exe) - return files - - -register_target("exe", make_exe) -register_target( - "resources", make_embedded_resources, depends=["exe"], default_build_script=True -) -register_target("install", make_install, depends=["exe"], default=True) -resolve_targets() diff --git a/qt/bundle/qt.exclude b/qt/bundle/qt.exclude deleted file mode 100644 index e5a6f252b..000000000 --- a/qt/bundle/qt.exclude +++ /dev/null @@ -1,10 +0,0 @@ -qml -bindings -uic -lupdate -qsci -*.pyc -*.pyi -*.sip -py.typed -__pycache__ diff --git a/qt/bundle/src/anki.rs b/qt/bundle/src/anki.rs deleted file mode 100644 index 3cf960028..000000000 --- a/qt/bundle/src/anki.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright: Ankitects Pty Ltd and contributors -// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -pub(super) fn init() { - #[cfg(target_os = "windows")] - attach_console(); - - println!("Anki starting..."); -} - -/// If parent process has a console (eg cmd.exe), redirect our output there. -#[cfg(target_os = "windows")] -fn attach_console() { - use std::ffi::CString; - - use libc_stdhandle::*; - use winapi::um::wincon; - - let console_attached = unsafe { wincon::AttachConsole(wincon::ATTACH_PARENT_PROCESS) }; - if console_attached == 0 { - return; - } - - let conin = CString::new("CONIN$").unwrap(); - let conout = CString::new("CONOUT$").unwrap(); - let r = CString::new("r").unwrap(); - let w = CString::new("w").unwrap(); - - // Python uses the CRT for I/O, and it requires the descriptors are reopened. - unsafe { - libc::freopen(conin.as_ptr(), r.as_ptr(), stdin()); - libc::freopen(conout.as_ptr(), w.as_ptr(), stdout()); - libc::freopen(conout.as_ptr(), w.as_ptr(), stderr()); - } -} diff --git a/qt/bundle/src/main.rs b/qt/bundle/src/main.rs deleted file mode 100644 index d424d4f26..000000000 --- a/qt/bundle/src/main.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Based off PyOxidizer's 'init-rust-project'. -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#![windows_subsystem = "windows"] - -mod anki; - -use pyembed::{MainPythonInterpreter, OxidizedPythonInterpreterConfig}; - -#[cfg(feature = "global-allocator-jemalloc")] -#[global_allocator] -static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc; - -include!(env!("DEFAULT_PYTHON_CONFIG_RS")); - -fn main() { - anki::init(); - - let exit_code = { - let config: OxidizedPythonInterpreterConfig = default_python_config(); - match MainPythonInterpreter::new(config) { - Ok(interp) => interp.run(), - Err(msg) => { - eprintln!("error instantiating embedded Python interpreter: {}", msg); - 1 - } - } - }; - std::process::exit(exit_code); -} diff --git a/qt/bundle/win/Cargo.toml b/qt/bundle/win/Cargo.toml deleted file mode 100644 index 9c091b55f..000000000 --- a/qt/bundle/win/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "makeexe" -version.workspace = true -authors.workspace = true -edition.workspace = true -license.workspace = true -publish = false -rust-version.workspace = true - -[dependencies] -anyhow.workspace = true -camino.workspace = true -clap.workspace = true -tugger-windows-codesign.workspace = true -walkdir.workspace = true diff --git a/qt/bundle/win/anki.exe.manifest b/qt/bundle/win/anki.exe.manifest deleted file mode 100644 index 6abbdfea9..000000000 --- a/qt/bundle/win/anki.exe.manifest +++ /dev/null @@ -1,9 +0,0 @@ - - - - - true - UTF-8 - - - diff --git a/qt/bundle/win/src/main.rs b/qt/bundle/win/src/main.rs deleted file mode 100644 index 2091f6a51..000000000 --- a/qt/bundle/win/src/main.rs +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright: Ankitects Pty Ltd and contributors -// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -use std::fs; -use std::io::prelude::*; -use std::path::Path; -use std::process::Command; - -use anyhow::bail; -use anyhow::Context; -use anyhow::Result; -use camino::Utf8Path; -use camino::Utf8PathBuf; -use clap::Parser; -use tugger_windows_codesign::CodeSigningCertificate; -use tugger_windows_codesign::SigntoolSign; -use tugger_windows_codesign::SystemStore; -use tugger_windows_codesign::TimestampServer; -use walkdir::WalkDir; - -#[derive(Parser)] -struct Args { - version: String, - bundle_root: Utf8PathBuf, - qt6_setup_path: Utf8PathBuf, -} - -fn main() -> Result<()> { - let args = Args::parse(); - - let src_win_folder = Utf8Path::new("qt/bundle/win"); - let std_dist_folder = args.bundle_root.join("std"); - // folder->installer - let dists = [(&std_dist_folder, &args.qt6_setup_path)]; - - for (folder, _) in dists { - fs::copy( - src_win_folder.join("anki-console.bat"), - folder.join("anki-console.bat"), - ) - .context("anki-console")?; - } - - println!("--- Build uninstaller"); - build_installer( - &args.bundle_root, - &std_dist_folder, - &args.qt6_setup_path, - &args.version, - true, - ) - .context("uninstaller")?; - - // sign the anki.exe and uninstaller.exe in std - println!("--- Sign binaries"); - codesign([ - &std_dist_folder.join("anki.exe"), - &std_dist_folder.join("uninstall.exe"), - ])?; - - println!("--- Build manifest"); - for (folder, _) in dists { - build_manifest(folder).context("manifest")?; - } - - for (folder, installer) in dists { - println!("--- Build {}", installer); - build_installer(&args.bundle_root, folder, installer, &args.version, false)?; - } - - println!("--- Sign installers"); - codesign(dists.iter().map(|tup| tup.1))?; - - Ok(()) -} - -fn build_installer( - bundle_root: &Utf8Path, - dist_folder: &Utf8Path, - installer: &Utf8Path, - version: &str, - uninstaller: bool, -) -> Result<()> { - let rendered_nsi = include_str!("../anki.template.nsi") - .replace("@@SRC@@", dist_folder.as_str()) - .replace("@@INSTALLER@@", installer.as_str()) - .replace("@@VERSION@@", version); - let rendered_nsi_path = bundle_root.join("anki.nsi"); - fs::write(&rendered_nsi_path, rendered_nsi).context("anki.nsi")?; - fs::write( - bundle_root.join("fileassoc.nsh"), - include_str!("../fileassoc.nsh"), - )?; - fs::copy( - "out/extracted/nsis_plugins/nsProcess.dll", - bundle_root.join("nsProcess.dll"), - )?; - let mut cmd = Command::new("c:/program files (x86)/nsis/makensis.exe"); - cmd.arg("-V3"); - if uninstaller { - cmd.arg("-DWRITE_UNINSTALLER"); - }; - if option_env!("RELEASE").is_none() { - cmd.arg("-DNO_COMPRESS"); - } - cmd.arg(rendered_nsi_path); - let status = cmd.status()?; - if !status.success() { - bail!("makensis failed"); - } - Ok(()) -} - -fn codesign(paths: impl IntoIterator>) -> Result<()> { - if option_env!("ANKI_CODESIGN").is_none() { - return Ok(()); - } - let cert = CodeSigningCertificate::Sha1Thumbprint( - SystemStore::My, - "dccfc6d312fc0432197bb7be951478e5866eebf8".into(), - ); - let mut sign = SigntoolSign::new(cert); - sign.file_digest_algorithm("sha256") - .timestamp_server(TimestampServer::Rfc3161( - "http://time.certum.pl".into(), - "sha256".into(), - )) - .verbose(); - paths.into_iter().for_each(|path| { - sign.sign_file(path); - }); - sign.run() -} - -fn build_manifest(base_path: &Utf8Path) -> Result<()> { - let mut buf = vec![]; - for entry in WalkDir::new(base_path) - .min_depth(1) - .sort_by_file_name() - .into_iter() - { - let entry = entry?; - let path = entry.path(); - let relative_path = path.strip_prefix(base_path)?; - write!( - &mut buf, - "{}\r\n", - relative_path.to_str().context("relative_path utf8")? - )?; - } - fs::write(base_path.join("anki.install-manifest"), buf)?; - Ok(()) -} diff --git a/qt/hatch_build.py b/qt/hatch_build.py new file mode 100644 index 000000000..e475b5d84 --- /dev/null +++ b/qt/hatch_build.py @@ -0,0 +1,51 @@ +# Copyright: Ankitects Pty Ltd and contributors +# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +import os +import sys +from pathlib import Path +from typing import Any, Dict + +from hatchling.builders.hooks.plugin.interface import BuildHookInterface + + +class CustomBuildHook(BuildHookInterface): + """Build hook to copy generated files into both sdist and wheel.""" + + PLUGIN_NAME = "custom" + + def initialize(self, version: str, build_data: Dict[str, Any]) -> None: + """Initialize the build hook.""" + force_include = build_data.setdefault("force_include", {}) + + # Look for generated files in out/qt/_aqt + project_root = Path(self.root).parent + generated_root = project_root / "out" / "qt" / "_aqt" + + if not os.environ.get("ANKI_WHEEL_TAG"): + # On Windows, uv invokes this build hook during the initial uv sync, + # when the tag has not been declared by our build script. + return + + assert generated_root.exists(), "you should build with --wheel" + self._add_aqt_files(force_include, generated_root) + + def _add_aqt_files(self, force_include: Dict[str, str], aqt_root: Path) -> None: + """Add _aqt files to the build.""" + for path in aqt_root.rglob("*"): + if path.is_file() and not self._should_exclude(path): + relative_path = path.relative_to(aqt_root) + # Place files under _aqt/ in the distribution + dist_path = "_aqt" / relative_path + force_include[str(path)] = str(dist_path) + + def _should_exclude(self, path: Path) -> bool: + """Check if a file should be excluded from the wheel.""" + # Match the exclusions from write_wheel.py exclude_aqt function + if path.suffix in [".ui", ".scss", ".map", ".ts"]: + return True + if path.name.startswith("tsconfig"): + return True + if "/aqt/data" in str(path): + return True + return False diff --git a/qt/launcher/Cargo.toml b/qt/launcher/Cargo.toml new file mode 100644 index 000000000..45ca11e9b --- /dev/null +++ b/qt/launcher/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "launcher" +version = "1.0.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +publish = false +rust-version.workspace = true + +[dependencies] +anki_io.workspace = true +anki_process.workspace = true +anyhow.workspace = true +dirs.workspace = true + +[target.'cfg(windows)'.dependencies] +winapi.workspace = true +libc.workspace = true +libc-stdhandle.workspace = true + +[[bin]] +name = "build_win" +path = "src/bin/build_win.rs" + +[target.'cfg(windows)'.build-dependencies] +embed-resource.workspace = true diff --git a/qt/launcher/build.rs b/qt/launcher/build.rs new file mode 100644 index 000000000..3ba75b0e1 --- /dev/null +++ b/qt/launcher/build.rs @@ -0,0 +1,10 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +fn main() { + #[cfg(windows)] + { + embed_resource::compile("win/anki-manifest.rc", embed_resource::NONE) + .manifest_required() + .unwrap(); + } +} diff --git a/qt/bundle/lin/README.md b/qt/launcher/lin/README.md similarity index 100% rename from qt/bundle/lin/README.md rename to qt/launcher/lin/README.md diff --git a/qt/launcher/lin/anki b/qt/launcher/lin/anki new file mode 100644 index 000000000..2a4a46062 --- /dev/null +++ b/qt/launcher/lin/anki @@ -0,0 +1,30 @@ +#!/bin/bash +# Universal Anki launcher script + +# Get the directory where this script is located (resolve symlinks) +SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)" + +# Determine architecture +ARCH=$(uname -m) +case "$ARCH" in + x86_64|amd64) + LAUNCHER="$SCRIPT_DIR/launcher.amd64" + ;; + aarch64|arm64) + LAUNCHER="$SCRIPT_DIR/launcher.arm64" + ;; + *) + echo "Error: Unsupported architecture: $ARCH" + echo "Supported architectures: x86_64, aarch64" + exit 1 + ;; +esac + +# Check if launcher exists +if [ ! -f "$LAUNCHER" ]; then + echo "Error: Launcher not found: $LAUNCHER" + exit 1 +fi + +# Execute the appropriate launcher with all arguments +exec "$LAUNCHER" "$@" \ No newline at end of file diff --git a/qt/bundle/lin/anki.1 b/qt/launcher/lin/anki.1 similarity index 100% rename from qt/bundle/lin/anki.1 rename to qt/launcher/lin/anki.1 diff --git a/qt/bundle/lin/anki.desktop b/qt/launcher/lin/anki.desktop similarity index 100% rename from qt/bundle/lin/anki.desktop rename to qt/launcher/lin/anki.desktop diff --git a/qt/bundle/lin/anki.png b/qt/launcher/lin/anki.png similarity index 100% rename from qt/bundle/lin/anki.png rename to qt/launcher/lin/anki.png diff --git a/qt/bundle/lin/anki.xml b/qt/launcher/lin/anki.xml similarity index 100% rename from qt/bundle/lin/anki.xml rename to qt/launcher/lin/anki.xml diff --git a/qt/bundle/lin/anki.xpm b/qt/launcher/lin/anki.xpm similarity index 100% rename from qt/bundle/lin/anki.xpm rename to qt/launcher/lin/anki.xpm diff --git a/qt/launcher/lin/build.sh b/qt/launcher/lin/build.sh new file mode 100755 index 000000000..e4ddce243 --- /dev/null +++ b/qt/launcher/lin/build.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +set -e + +# Add Linux cross-compilation target +rustup target add aarch64-unknown-linux-gnu + +# Define output paths +OUTPUT_DIR="../../../out/launcher" +LAUNCHER_DIR="$OUTPUT_DIR/anki-launcher" + +# Clean existing output directory +rm -rf "$LAUNCHER_DIR" + +# Build binaries for both Linux architectures +cargo build -p launcher --release --target x86_64-unknown-linux-gnu +CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \ + cargo build -p launcher --release --target aarch64-unknown-linux-gnu +(cd ../../.. && ./ninja extract:uv_lin_arm) + +# Create output directory +mkdir -p "$LAUNCHER_DIR" + +# Copy binaries and support files +TARGET_DIR=${CARGO_TARGET_DIR:-../../../target} + +# Copy launcher binaries with architecture suffixes +cp "$TARGET_DIR/x86_64-unknown-linux-gnu/release/launcher" "$LAUNCHER_DIR/launcher.amd64" +cp "$TARGET_DIR/aarch64-unknown-linux-gnu/release/launcher" "$LAUNCHER_DIR/launcher.arm64" + +# Copy uv binaries with architecture suffixes +cp "../../../out/extracted/uv/uv" "$LAUNCHER_DIR/uv.amd64" +cp "../../../out/extracted/uv_lin_arm/uv" "$LAUNCHER_DIR/uv.arm64" + +# Copy support files from lin directory +for file in README.md anki.1 anki.desktop anki.png anki.xml anki.xpm install.sh uninstall.sh anki; do + cp "$file" "$LAUNCHER_DIR/" +done + +# Copy additional files from parent directory +cp ../pyproject.toml "$LAUNCHER_DIR/" +cp ../../../.python-version "$LAUNCHER_DIR/" + +# Set executable permissions +chmod +x \ + "$LAUNCHER_DIR/anki" \ + "$LAUNCHER_DIR/launcher.amd64" \ + "$LAUNCHER_DIR/launcher.arm64" \ + "$LAUNCHER_DIR/uv.amd64" \ + "$LAUNCHER_DIR/uv.arm64" \ + "$LAUNCHER_DIR/install.sh" \ + "$LAUNCHER_DIR/uninstall.sh" + +# Set proper permissions and create tarball +chmod -R a+r "$LAUNCHER_DIR" + +# Create tarball using the same options as the Rust template +ZSTD="zstd -c --long -T0 -18" +TRANSFORM="s%^.%anki-launcher%S" +TARBALL="$OUTPUT_DIR/anki-launcher.tar.zst" + +tar -I "$ZSTD" --transform "$TRANSFORM" -cf "$TARBALL" -C "$LAUNCHER_DIR" . + +echo "Build complete:" +echo "Universal launcher: $LAUNCHER_DIR" +echo "Tarball: $TARBALL" diff --git a/qt/bundle/lin/install.sh b/qt/launcher/lin/install.sh similarity index 92% rename from qt/bundle/lin/install.sh rename to qt/launcher/lin/install.sh index 519a45fa2..c9f129654 100755 --- a/qt/bundle/lin/install.sh +++ b/qt/launcher/lin/install.sh @@ -13,7 +13,7 @@ fi rm -rf "$PREFIX"/share/anki "$PREFIX"/bin/anki mkdir -p "$PREFIX"/share/anki -cp -av --no-preserve=owner,context -- * "$PREFIX"/share/anki/ +cp -av --no-preserve=owner,context -- * .python-version "$PREFIX"/share/anki/ mkdir -p "$PREFIX"/bin ln -sf "$PREFIX"/share/anki/anki "$PREFIX"/bin/anki # fix a previous packaging issue where we created this as a file diff --git a/qt/bundle/lin/uninstall.sh b/qt/launcher/lin/uninstall.sh similarity index 100% rename from qt/bundle/lin/uninstall.sh rename to qt/launcher/lin/uninstall.sh diff --git a/qt/bundle/mac/src/Info.plist b/qt/launcher/mac/Info.plist similarity index 92% rename from qt/bundle/mac/src/Info.plist rename to qt/launcher/mac/Info.plist index 5933838e4..59b67605f 100644 --- a/qt/bundle/mac/src/Info.plist +++ b/qt/launcher/mac/Info.plist @@ -5,9 +5,9 @@ CFBundleDisplayName Anki CFBundleShortVersionString - 2.1.46 + 1.0 LSMinimumSystemVersion - 10.14.0 + 11 CFBundleDocumentTypes @@ -26,11 +26,11 @@ CFBundleExecutable - anki + launcher CFBundleIconName AppIcon CFBundleIdentifier - net.ankiweb.dtop + net.ankiweb.launcher CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/qt/launcher/mac/build.sh b/qt/launcher/mac/build.sh new file mode 100755 index 000000000..8a60d488d --- /dev/null +++ b/qt/launcher/mac/build.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +set -e + +# Define output path +OUTPUT_DIR="../../../out/launcher" +APP_LAUNCHER="$OUTPUT_DIR/Anki.app" +rm -rf "$APP_LAUNCHER" + +# Build binaries for both architectures +cargo build -p launcher --release --target aarch64-apple-darwin +cargo build -p launcher --release --target x86_64-apple-darwin +(cd ../../.. && ./ninja launcher:uv_universal) + +# Ensure output directory exists +mkdir -p "$OUTPUT_DIR" + +# Remove existing app launcher +rm -rf "$APP_LAUNCHER" + +# Create app launcher structure +mkdir -p "$APP_LAUNCHER/Contents/MacOS" "$APP_LAUNCHER/Contents/Resources" + +# Copy binaries in +TARGET_DIR=${CARGO_TARGET_DIR:-target} +lipo -create \ + "$TARGET_DIR/aarch64-apple-darwin/release/launcher" \ + "$TARGET_DIR/x86_64-apple-darwin/release/launcher" \ + -output "$APP_LAUNCHER/Contents/MacOS/launcher" +cp "$OUTPUT_DIR/uv" "$APP_LAUNCHER/Contents/MacOS/" + +# Copy support files +cp Info.plist "$APP_LAUNCHER/Contents/" +cp icon/Assets.car "$APP_LAUNCHER/Contents/Resources/" +cp ../pyproject.toml "$APP_LAUNCHER/Contents/Resources/" +cp ../../../.python-version "$APP_LAUNCHER/Contents/Resources/" + +# Codesign +for i in "$APP_LAUNCHER/Contents/MacOS/uv" "$APP_LAUNCHER/Contents/MacOS/launcher" "$APP_LAUNCHER"; do + codesign --force -vvvv -o runtime -s "Developer ID Application:" \ + --entitlements entitlements.python.xml \ + "$i" +done + +# Check +codesign -vvv "$APP_LAUNCHER" +spctl -a "$APP_LAUNCHER" + +# Notarize +./notarize.sh "$OUTPUT_DIR" + +# Bundle +./dmg/build.sh "$OUTPUT_DIR" \ No newline at end of file diff --git a/qt/bundle/mac/dmg/anki-logo-bg.png b/qt/launcher/mac/dmg/anki-logo-bg.png similarity index 100% rename from qt/bundle/mac/dmg/anki-logo-bg.png rename to qt/launcher/mac/dmg/anki-logo-bg.png diff --git a/qt/bundle/mac/dmg/build.sh b/qt/launcher/mac/dmg/build.sh similarity index 87% rename from qt/bundle/mac/dmg/build.sh rename to qt/launcher/mac/dmg/build.sh index 6efb510a7..16b48c06a 100755 --- a/qt/bundle/mac/dmg/build.sh +++ b/qt/launcher/mac/dmg/build.sh @@ -4,9 +4,9 @@ set -e # base folder with Anki.app in it -dist=$1 -dmg_path=$2 -script_folder=$(dirname $0) +output="$1" +dist="$1/tmp" +dmg_path="$output/Anki.dmg" if [ -d "/Volumes/Anki" ] then @@ -14,9 +14,14 @@ then exit 1 fi +rm -rf $dist $dmg_path +mkdir -p $dist +rsync -av $output/Anki.app $dist/ +script_folder=$(dirname $0) + echo "bundling..." ln -s /Applications $dist/Applications -mkdir $dist/.background +mkdir -p $dist/.background cp ${script_folder}/anki-logo-bg.png $dist/.background cp ${script_folder}/dmg_ds_store $dist/.DS_Store diff --git a/qt/bundle/mac/dmg/dmg_ds_store b/qt/launcher/mac/dmg/dmg_ds_store similarity index 100% rename from qt/bundle/mac/dmg/dmg_ds_store rename to qt/launcher/mac/dmg/dmg_ds_store diff --git a/qt/bundle/mac/dmg/set-dmg-settings.app/Contents/Info.plist b/qt/launcher/mac/dmg/set-dmg-settings.app/Contents/Info.plist similarity index 100% rename from qt/bundle/mac/dmg/set-dmg-settings.app/Contents/Info.plist rename to qt/launcher/mac/dmg/set-dmg-settings.app/Contents/Info.plist diff --git a/qt/bundle/mac/dmg/set-dmg-settings.app/Contents/MacOS/applet b/qt/launcher/mac/dmg/set-dmg-settings.app/Contents/MacOS/applet similarity index 100% rename from qt/bundle/mac/dmg/set-dmg-settings.app/Contents/MacOS/applet rename to qt/launcher/mac/dmg/set-dmg-settings.app/Contents/MacOS/applet diff --git a/qt/bundle/mac/dmg/set-dmg-settings.app/Contents/PkgInfo b/qt/launcher/mac/dmg/set-dmg-settings.app/Contents/PkgInfo similarity index 100% rename from qt/bundle/mac/dmg/set-dmg-settings.app/Contents/PkgInfo rename to qt/launcher/mac/dmg/set-dmg-settings.app/Contents/PkgInfo diff --git a/qt/bundle/mac/dmg/set-dmg-settings.app/Contents/Resources/Scripts/main.scpt b/qt/launcher/mac/dmg/set-dmg-settings.app/Contents/Resources/Scripts/main.scpt similarity index 100% rename from qt/bundle/mac/dmg/set-dmg-settings.app/Contents/Resources/Scripts/main.scpt rename to qt/launcher/mac/dmg/set-dmg-settings.app/Contents/Resources/Scripts/main.scpt diff --git a/qt/bundle/mac/dmg/set-dmg-settings.app/Contents/Resources/applet.icns b/qt/launcher/mac/dmg/set-dmg-settings.app/Contents/Resources/applet.icns similarity index 100% rename from qt/bundle/mac/dmg/set-dmg-settings.app/Contents/Resources/applet.icns rename to qt/launcher/mac/dmg/set-dmg-settings.app/Contents/Resources/applet.icns diff --git a/qt/bundle/mac/dmg/set-dmg-settings.app/Contents/Resources/applet.rsrc b/qt/launcher/mac/dmg/set-dmg-settings.app/Contents/Resources/applet.rsrc similarity index 100% rename from qt/bundle/mac/dmg/set-dmg-settings.app/Contents/Resources/applet.rsrc rename to qt/launcher/mac/dmg/set-dmg-settings.app/Contents/Resources/applet.rsrc diff --git a/qt/bundle/mac/dmg/set-dmg-settings.app/Contents/Resources/description.rtfd/TXT.rtf b/qt/launcher/mac/dmg/set-dmg-settings.app/Contents/Resources/description.rtfd/TXT.rtf similarity index 100% rename from qt/bundle/mac/dmg/set-dmg-settings.app/Contents/Resources/description.rtfd/TXT.rtf rename to qt/launcher/mac/dmg/set-dmg-settings.app/Contents/Resources/description.rtfd/TXT.rtf diff --git a/qt/bundle/mac/dmg/set-dmg-settings.app/Contents/_CodeSignature/CodeResources b/qt/launcher/mac/dmg/set-dmg-settings.app/Contents/_CodeSignature/CodeResources similarity index 100% rename from qt/bundle/mac/dmg/set-dmg-settings.app/Contents/_CodeSignature/CodeResources rename to qt/launcher/mac/dmg/set-dmg-settings.app/Contents/_CodeSignature/CodeResources diff --git a/qt/bundle/mac/dmg/set-dmg-settings.scpt b/qt/launcher/mac/dmg/set-dmg-settings.scpt similarity index 100% rename from qt/bundle/mac/dmg/set-dmg-settings.scpt rename to qt/launcher/mac/dmg/set-dmg-settings.scpt diff --git a/qt/bundle/mac/entitlements.python.xml b/qt/launcher/mac/entitlements.python.xml similarity index 100% rename from qt/bundle/mac/entitlements.python.xml rename to qt/launcher/mac/entitlements.python.xml diff --git a/qt/bundle/mac/icon/Assets.car b/qt/launcher/mac/icon/Assets.car similarity index 100% rename from qt/bundle/mac/icon/Assets.car rename to qt/launcher/mac/icon/Assets.car diff --git a/qt/bundle/mac/icon/Assets.xcassets/AppIcon.appiconset/Contents.json b/qt/launcher/mac/icon/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from qt/bundle/mac/icon/Assets.xcassets/AppIcon.appiconset/Contents.json rename to qt/launcher/mac/icon/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/qt/bundle/mac/icon/Assets.xcassets/AppIcon.appiconset/round-1024-512.png b/qt/launcher/mac/icon/Assets.xcassets/AppIcon.appiconset/round-1024-512.png similarity index 100% rename from qt/bundle/mac/icon/Assets.xcassets/AppIcon.appiconset/round-1024-512.png rename to qt/launcher/mac/icon/Assets.xcassets/AppIcon.appiconset/round-1024-512.png diff --git a/qt/bundle/mac/icon/Assets.xcassets/Contents.json b/qt/launcher/mac/icon/Assets.xcassets/Contents.json similarity index 100% rename from qt/bundle/mac/icon/Assets.xcassets/Contents.json rename to qt/launcher/mac/icon/Assets.xcassets/Contents.json diff --git a/qt/bundle/mac/icon/build.sh b/qt/launcher/mac/icon/build.sh similarity index 100% rename from qt/bundle/mac/icon/build.sh rename to qt/launcher/mac/icon/build.sh diff --git a/qt/launcher/mac/notarize.sh b/qt/launcher/mac/notarize.sh new file mode 100755 index 000000000..d906bd7f8 --- /dev/null +++ b/qt/launcher/mac/notarize.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e + +# Define output path +OUTPUT_DIR="$1" +APP_LAUNCHER="$OUTPUT_DIR/Anki.app" +ZIP_FILE="$OUTPUT_DIR/Anki.zip" + +# Create zip for notarization +(cd "$OUTPUT_DIR" && rm -rf Anki.zip && zip -r Anki.zip Anki.app) + +# Upload for notarization +xcrun notarytool submit "$ZIP_FILE" -p default --wait + +# Staple the app +xcrun stapler staple "$APP_LAUNCHER" \ No newline at end of file diff --git a/qt/launcher/pyproject.toml b/qt/launcher/pyproject.toml new file mode 100644 index 000000000..6ba027844 --- /dev/null +++ b/qt/launcher/pyproject.toml @@ -0,0 +1,22 @@ +[project] +name = "anki-launcher" +version = "0.1.0" +description = "UV-based launcher for Anki." +requires-python = ">=3.9" +dependencies = [ + "anki-release", + # so we can use testpypi + "anki", + "aqt", +] + +[tool.uv.sources] +anki-release = { index = "testpypi" } +anki = { index = "testpypi" } +aqt = { index = "testpypi" } + +[[tool.uv.index]] +name = "testpypi" +url = "https://test.pypi.org/simple/" +publish-url = "https://test.pypi.org/legacy/" +explicit = true diff --git a/qt/launcher/src/bin/build_win.rs b/qt/launcher/src/bin/build_win.rs new file mode 100644 index 000000000..959034438 --- /dev/null +++ b/qt/launcher/src/bin/build_win.rs @@ -0,0 +1,295 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +use std::env; +use std::path::Path; +use std::path::PathBuf; +use std::process::Command; + +use anki_io::copy_file; +use anki_io::create_dir_all; +use anki_io::remove_dir_all; +use anki_io::write_file; +use anki_process::CommandExt; +use anyhow::Result; + +const OUTPUT_DIR: &str = "../../../out/launcher"; +const LAUNCHER_EXE_DIR: &str = "../../../out/launcher_exe"; +const NSIS_DIR: &str = "../../../out/nsis"; +const CARGO_TARGET_DIR: &str = "../../../out/rust"; +const NSIS_PATH: &str = "C:\\Program Files (x86)\\NSIS\\makensis.exe"; + +fn main() -> Result<()> { + println!("Building Windows launcher..."); + + let output_dir = PathBuf::from(OUTPUT_DIR); + let launcher_exe_dir = PathBuf::from(LAUNCHER_EXE_DIR); + let nsis_dir = PathBuf::from(NSIS_DIR); + + setup_directories(&output_dir, &launcher_exe_dir, &nsis_dir)?; + build_launcher_binary()?; + extract_nsis_plugins()?; + copy_files(&output_dir)?; + sign_binaries(&output_dir)?; + copy_nsis_files(&nsis_dir)?; + build_uninstaller(&output_dir, &nsis_dir)?; + sign_file(&output_dir.join("uninstall.exe"))?; + generate_install_manifest(&output_dir)?; + build_installer(&output_dir, &nsis_dir)?; + sign_file(&PathBuf::from("../../../out/launcher_exe/anki-install.exe"))?; + + println!("Build completed successfully!"); + println!("Output directory: {}", output_dir.display()); + println!("Installer: ../../../out/launcher_exe/anki-install.exe"); + + Ok(()) +} + +fn setup_directories(output_dir: &Path, launcher_exe_dir: &Path, nsis_dir: &Path) -> Result<()> { + println!("Setting up directories..."); + + // Remove existing output directories + if output_dir.exists() { + remove_dir_all(output_dir)?; + } + if launcher_exe_dir.exists() { + remove_dir_all(launcher_exe_dir)?; + } + if nsis_dir.exists() { + remove_dir_all(nsis_dir)?; + } + + // Create output directories + create_dir_all(output_dir)?; + create_dir_all(launcher_exe_dir)?; + create_dir_all(nsis_dir)?; + + Ok(()) +} + +fn build_launcher_binary() -> Result<()> { + println!("Building launcher binary..."); + + env::set_var("CARGO_TARGET_DIR", CARGO_TARGET_DIR); + + Command::new("cargo") + .args([ + "build", + "-p", + "launcher", + "--release", + "--target", + "x86_64-pc-windows-msvc", + ]) + .ensure_success()?; + + Ok(()) +} + +fn extract_nsis_plugins() -> Result<()> { + println!("Extracting NSIS plugins..."); + + // Change to the anki root directory and run tools/ninja.bat + Command::new("cmd") + .args([ + "/c", + "cd", + "/d", + "..\\..\\..\\", + "&&", + "tools\\ninja.bat", + "extract:nsis_plugins", + ]) + .ensure_success()?; + + Ok(()) +} + +fn copy_files(output_dir: &Path) -> Result<()> { + println!("Copying binaries..."); + + // Copy launcher binary as anki.exe + let launcher_src = + PathBuf::from(CARGO_TARGET_DIR).join("x86_64-pc-windows-msvc/release/launcher.exe"); + let launcher_dst = output_dir.join("anki.exe"); + copy_file(&launcher_src, &launcher_dst)?; + + // Copy uv.exe + let uv_src = PathBuf::from("../../../out/extracted/uv/uv.exe"); + let uv_dst = output_dir.join("uv.exe"); + copy_file(&uv_src, &uv_dst)?; + + println!("Copying support files..."); + + // Copy pyproject.toml + copy_file("../pyproject.toml", output_dir.join("pyproject.toml"))?; + + // Copy .python-version + copy_file( + "../../../.python-version", + output_dir.join(".python-version"), + )?; + + // Copy anki-console.bat + copy_file("anki-console.bat", output_dir.join("anki-console.bat"))?; + + Ok(()) +} + +fn sign_binaries(output_dir: &Path) -> Result<()> { + sign_file(&output_dir.join("anki.exe"))?; + sign_file(&output_dir.join("uv.exe"))?; + Ok(()) +} + +fn sign_file(file_path: &Path) -> Result<()> { + let codesign = env::var("CODESIGN").unwrap_or_default(); + if codesign != "1" { + println!( + "Skipping code signing for {} (CODESIGN not set to 1)", + file_path.display() + ); + return Ok(()); + } + + let signtool_path = find_signtool()?; + println!("Signing {}...", file_path.display()); + + Command::new(&signtool_path) + .args([ + "sign", + "/sha1", + "dccfc6d312fc0432197bb7be951478e5866eebf8", + "/fd", + "sha256", + "/tr", + "http://time.certum.pl", + "/td", + "sha256", + "/v", + ]) + .arg(file_path) + .ensure_success()?; + + Ok(()) +} + +fn find_signtool() -> Result { + println!("Locating signtool.exe..."); + + let output = Command::new("where") + .args([ + "/r", + "C:\\Program Files (x86)\\Windows Kits", + "signtool.exe", + ]) + .utf8_output()?; + + // Find signtool.exe with "arm64" in the path (as per original batch logic) + for line in output.stdout.lines() { + if line.contains("\\arm64\\") { + let signtool_path = PathBuf::from(line.trim()); + println!("Using signtool: {}", signtool_path.display()); + return Ok(signtool_path); + } + } + + anyhow::bail!("Could not find signtool.exe with arm64 architecture"); +} + +fn generate_install_manifest(output_dir: &Path) -> Result<()> { + println!("Generating install manifest..."); + + let mut manifest_content = String::new(); + let entries = anki_io::read_dir_files(output_dir)?; + + for entry in entries { + let entry = entry?; + let path = entry.path(); + if let Some(file_name) = path.file_name() { + let file_name_str = file_name.to_string_lossy(); + // Skip manifest file and uninstaller (can't delete itself) + if file_name_str != "anki.install-manifest" && file_name_str != "uninstall.exe" { + if let Ok(relative_path) = path.strip_prefix(output_dir) { + // Convert to Windows-style backslashes for NSIS + let windows_path = relative_path.display().to_string().replace('/', "\\"); + // Use Windows line endings (\r\n) as expected by NSIS + manifest_content.push_str(&format!("{}\r\n", windows_path)); + } + } + } + } + + write_file(output_dir.join("anki.install-manifest"), manifest_content)?; + + Ok(()) +} + +fn copy_nsis_files(nsis_dir: &Path) -> Result<()> { + println!("Copying NSIS support files..."); + + // Copy anki.template.nsi as anki.nsi + copy_file("anki.template.nsi", nsis_dir.join("anki.nsi"))?; + + // Copy fileassoc.nsh + copy_file("fileassoc.nsh", nsis_dir.join("fileassoc.nsh"))?; + + // Copy nsProcess.dll + copy_file( + "../../../out/extracted/nsis_plugins/nsProcess.dll", + nsis_dir.join("nsProcess.dll"), + )?; + + Ok(()) +} + +fn build_uninstaller(output_dir: &Path, nsis_dir: &Path) -> Result<()> { + println!("Building uninstaller..."); + + let mut flags = vec!["-V3", "-DWRITE_UNINSTALLER"]; + if env::var("NO_COMPRESS").unwrap_or_default() == "1" { + println!("NO_COMPRESS=1 detected, disabling compression"); + flags.push("-DNO_COMPRESS"); + } + + run_nsis( + &PathBuf::from("anki.nsi"), + &flags, + nsis_dir, // Run from nsis directory + )?; + + // Copy uninstaller from nsis directory to output directory + copy_file( + nsis_dir.join("uninstall.exe"), + output_dir.join("uninstall.exe"), + )?; + + Ok(()) +} + +fn build_installer(_output_dir: &Path, nsis_dir: &Path) -> Result<()> { + println!("Building installer..."); + + let mut flags = vec!["-V3"]; + if env::var("NO_COMPRESS").unwrap_or_default() == "1" { + println!("NO_COMPRESS=1 detected, disabling compression"); + flags.push("-DNO_COMPRESS"); + } + + run_nsis( + &PathBuf::from("anki.nsi"), + &flags, + nsis_dir, // Run from nsis directory + )?; + + Ok(()) +} + +fn run_nsis(script_path: &Path, flags: &[&str], working_dir: &Path) -> Result<()> { + let mut cmd = Command::new(NSIS_PATH); + cmd.args(flags).arg(script_path).current_dir(working_dir); + + cmd.ensure_success()?; + + Ok(()) +} diff --git a/qt/launcher/src/main.rs b/qt/launcher/src/main.rs new file mode 100644 index 000000000..77268ce3a --- /dev/null +++ b/qt/launcher/src/main.rs @@ -0,0 +1,117 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +#![windows_subsystem = "windows"] + +use std::io::stdin; +use std::process::Command; + +use anki_io::copy_file; +use anki_io::create_dir_all; +use anki_io::metadata; +use anki_io::remove_file; +use anki_io::write_file; +use anki_process::CommandExt; +use anyhow::Context; +use anyhow::Result; + +use crate::platform::exec_anki; +use crate::platform::get_anki_binary_path; +use crate::platform::get_exe_and_resources_dirs; +use crate::platform::get_uv_binary_name; +use crate::platform::handle_first_launch; +use crate::platform::handle_terminal_launch; +use crate::platform::initial_terminal_setup; +use crate::platform::launch_anki_detached; + +mod platform; + +#[derive(Debug, Clone, Default)] +pub struct Config { + pub show_console: bool, +} + +fn main() { + if let Err(e) = run() { + eprintln!("Error: {:#}", e); + eprintln!("Press enter to close..."); + let mut input = String::new(); + let _ = stdin().read_line(&mut input); + + std::process::exit(1); + } +} + +fn run() -> Result<()> { + let mut config = Config::default(); + initial_terminal_setup(&mut config); + + let uv_install_root = dirs::data_local_dir() + .context("Unable to determine data_dir")? + .join("AnkiProgramFiles"); + + let sync_complete_marker = uv_install_root.join(".sync_complete"); + let (exe_dir, resources_dir) = get_exe_and_resources_dirs()?; + let dist_pyproject_path = resources_dir.join("pyproject.toml"); + let user_pyproject_path = uv_install_root.join("pyproject.toml"); + let dist_python_version_path = resources_dir.join(".python-version"); + let user_python_version_path = uv_install_root.join(".python-version"); + let uv_lock_path = uv_install_root.join("uv.lock"); + let uv_path: std::path::PathBuf = exe_dir.join(get_uv_binary_name()); + + let pyproject_has_changed = + !user_pyproject_path.exists() || !sync_complete_marker.exists() || { + let pyproject_toml_time = metadata(&user_pyproject_path)? + .modified() + .context("Failed to get pyproject.toml modified time")?; + let sync_complete_time = metadata(&sync_complete_marker)? + .modified() + .context("Failed to get sync marker modified time")?; + Ok::(pyproject_toml_time > sync_complete_time) + } + .unwrap_or(true); + + if !pyproject_has_changed { + // If venv is already up to date, exec as normal + let anki_bin = get_anki_binary_path(&uv_install_root); + exec_anki(&anki_bin, &config)?; + return Ok(()); + } + + // we'll need to launch uv; reinvoke ourselves in a terminal so the user can see + handle_terminal_launch()?; + + // Create install directory and copy project files in + create_dir_all(&uv_install_root)?; + if !user_pyproject_path.exists() { + copy_file(&dist_pyproject_path, &user_pyproject_path)?; + copy_file(&dist_python_version_path, &user_python_version_path)?; + } + + // Remove sync marker before attempting sync + let _ = remove_file(&sync_complete_marker); + + // Sync the venv + if let Err(e) = Command::new(&uv_path) + .current_dir(&uv_install_root) + .args(["sync", "--refresh"]) + .ensure_success() + { + // If sync fails due to things like a missing wheel on pypi, + // we need to remove the lockfile or uv will cache the bad result. + let _ = remove_file(&uv_lock_path); + return Err(e.into()); + } + + // Write marker file to indicate successful sync + write_file(&sync_complete_marker, "")?; + + // First launch + let anki_bin = get_anki_binary_path(&uv_install_root); + handle_first_launch(&anki_bin)?; + + // Then launch the binary as detached subprocess so the terminal can close + launch_anki_detached(&anki_bin, &config)?; + + Ok(()) +} diff --git a/qt/launcher/src/platform/mac.rs b/qt/launcher/src/platform/mac.rs new file mode 100644 index 000000000..b5157dd4b --- /dev/null +++ b/qt/launcher/src/platform/mac.rs @@ -0,0 +1,80 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +use std::os::unix::process::CommandExt; +use std::process::Command; + +use anki_process::CommandExt as AnkiCommandExt; +use anyhow::Context; +use anyhow::Result; + +// Re-export Unix functions that macOS uses +pub use super::unix::{ + exec_anki, + get_anki_binary_path, + initial_terminal_setup, +}; + +pub fn launch_anki_detached(anki_bin: &std::path::Path, _config: &crate::Config) -> Result<()> { + use std::process::Stdio; + + let child = Command::new(anki_bin) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .process_group(0) + .ensure_spawn()?; + std::mem::forget(child); + Ok(()) +} + +pub fn handle_terminal_launch() -> Result<()> { + let stdout_is_terminal = std::io::IsTerminal::is_terminal(&std::io::stdout()); + if stdout_is_terminal { + print!("\x1B[2J\x1B[H"); // Clear screen and move cursor to top + println!("\x1B[1mPreparing to start Anki...\x1B[0m\n"); + } else { + // If launched from GUI, relaunch in Terminal.app + relaunch_in_terminal()?; + } + Ok(()) +} + +fn relaunch_in_terminal() -> Result<()> { + let current_exe = std::env::current_exe().context("Failed to get current executable path")?; + Command::new("open") + .args(["-a", "Terminal"]) + .arg(current_exe) + .ensure_spawn()?; + std::process::exit(0); +} + +pub fn handle_first_launch(anki_bin: &std::path::Path) -> Result<()> { + // Pre-validate by running --version to trigger any Gatekeeper checks + println!("\n\x1B[1mThis may take a few minutes. Please wait...\x1B[0m"); + let _ = Command::new(anki_bin) + .env("ANKI_FIRST_RUN", "1") + .arg("--version") + .ensure_success(); + Ok(()) +} + +pub fn get_exe_and_resources_dirs() -> Result<(std::path::PathBuf, std::path::PathBuf)> { + let exe_dir = std::env::current_exe() + .context("Failed to get current executable path")? + .parent() + .context("Failed to get executable directory")? + .to_owned(); + + let resources_dir = exe_dir + .parent() + .context("Failed to get parent directory")? + .join("Resources"); + + Ok((exe_dir, resources_dir)) +} + +pub fn get_uv_binary_name() -> &'static str { + // macOS uses standard uv binary name + "uv" +} diff --git a/qt/launcher/src/platform/mod.rs b/qt/launcher/src/platform/mod.rs new file mode 100644 index 000000000..bb7208abe --- /dev/null +++ b/qt/launcher/src/platform/mod.rs @@ -0,0 +1,18 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +#[cfg(unix)] +mod unix; + +#[cfg(target_os = "macos")] +mod mac; + +#[cfg(target_os = "windows")] +mod windows; + +#[cfg(target_os = "macos")] +pub use mac::*; +#[cfg(all(unix, not(target_os = "macos")))] +pub use unix::*; +#[cfg(target_os = "windows")] +pub use windows::*; diff --git a/qt/launcher/src/platform/unix.rs b/qt/launcher/src/platform/unix.rs new file mode 100644 index 000000000..4155f39d1 --- /dev/null +++ b/qt/launcher/src/platform/unix.rs @@ -0,0 +1,74 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +#![allow(dead_code)] + +use std::path::PathBuf; +use std::process::Command; + +use anki_process::CommandExt as AnkiCommandExt; +use anyhow::Context; +use anyhow::Result; + +use crate::Config; + +pub fn initial_terminal_setup(_config: &mut Config) { + // No special terminal setup needed on Unix +} + +pub fn handle_terminal_launch() -> Result<()> { + print!("\x1B[2J\x1B[H"); // Clear screen and move cursor to top + println!("\x1B[1mPreparing to start Anki...\x1B[0m\n"); + // Skip terminal relaunch on non-macOS Unix systems as we don't know which + // terminal is installed + Ok(()) +} + +pub fn get_anki_binary_path(uv_install_root: &std::path::Path) -> PathBuf { + uv_install_root.join(".venv/bin/anki") +} + +pub fn launch_anki_detached(anki_bin: &std::path::Path, config: &Config) -> Result<()> { + // On non-macOS Unix systems, we don't need to detach since we never spawned a + // terminal + exec_anki(anki_bin, config) +} + +pub fn handle_first_launch(_anki_bin: &std::path::Path) -> Result<()> { + // No special first launch handling needed for generic Unix systems + Ok(()) +} + +pub fn exec_anki(anki_bin: &std::path::Path, _config: &Config) -> Result<()> { + let args: Vec = std::env::args().skip(1).collect(); + Command::new(anki_bin) + .args(args) + .ensure_exec() + .map_err(anyhow::Error::new) +} + +pub fn get_exe_and_resources_dirs() -> Result<(PathBuf, PathBuf)> { + let exe_dir = std::env::current_exe() + .context("Failed to get current executable path")? + .parent() + .context("Failed to get executable directory")? + .to_owned(); + + // On generic Unix systems, assume resources are in the same directory as + // executable + let resources_dir = exe_dir.clone(); + + Ok((exe_dir, resources_dir)) +} + +pub fn get_uv_binary_name() -> &'static str { + // Use architecture-specific uv binary for non-Mac Unix systems + if cfg!(target_arch = "x86_64") { + "uv.amd64" + } else if cfg!(target_arch = "aarch64") { + "uv.arm64" + } else { + // Fallback to generic uv for other architectures + "uv" + } +} diff --git a/qt/launcher/src/platform/windows.rs b/qt/launcher/src/platform/windows.rs new file mode 100644 index 000000000..c536c8c76 --- /dev/null +++ b/qt/launcher/src/platform/windows.rs @@ -0,0 +1,118 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +use std::path::PathBuf; +use std::process::Command; + +use anki_process::CommandExt; +use anyhow::Context; +use anyhow::Result; + +use crate::Config; + +pub fn handle_terminal_launch() -> Result<()> { + // uv will do this itself + Ok(()) +} + +/// If parent process has a console (eg cmd.exe), redirect our output there. +/// Sets config.show_console to true if successfully attached to console. +pub fn initial_terminal_setup(config: &mut Config) { + use std::ffi::CString; + + use libc_stdhandle::*; + use winapi::um::wincon; + + let console_attached = unsafe { wincon::AttachConsole(wincon::ATTACH_PARENT_PROCESS) }; + if console_attached == 0 { + return; + } + + let conin = CString::new("CONIN$").unwrap(); + let conout = CString::new("CONOUT$").unwrap(); + let r = CString::new("r").unwrap(); + let w = CString::new("w").unwrap(); + + // Python uses the CRT for I/O, and it requires the descriptors are reopened. + unsafe { + libc::freopen(conin.as_ptr(), r.as_ptr(), stdin()); + libc::freopen(conout.as_ptr(), w.as_ptr(), stdout()); + libc::freopen(conout.as_ptr(), w.as_ptr(), stderr()); + } + + config.show_console = true; +} + +pub fn get_anki_binary_path(uv_install_root: &std::path::Path) -> std::path::PathBuf { + uv_install_root.join(".venv/Scripts/anki.exe") +} + +fn build_python_command( + anki_bin: &std::path::Path, + args: &[String], + config: &Config, +) -> Result { + let venv_dir = anki_bin + .parent() + .context("Failed to get venv Scripts directory")? + .parent() + .context("Failed to get venv directory")?; + + // Use python.exe if show_console is true, otherwise pythonw.exe + let python_exe = if config.show_console { + venv_dir.join("Scripts/python.exe") + } else { + venv_dir.join("Scripts/pythonw.exe") + }; + + let mut cmd = Command::new(python_exe); + cmd.args(["-c", "import aqt; aqt.run()"]); + cmd.args(args); + + Ok(cmd) +} + +pub fn launch_anki_detached(anki_bin: &std::path::Path, config: &Config) -> Result<()> { + use std::os::windows::process::CommandExt; + use std::process::Stdio; + + const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; + const DETACHED_PROCESS: u32 = 0x00000008; + + let mut cmd = build_python_command(anki_bin, &[], config)?; + cmd.stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .creation_flags(CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS) + .ensure_spawn()?; + Ok(()) +} + +pub fn handle_first_launch(_anki_bin: &std::path::Path) -> Result<()> { + Ok(()) +} + +pub fn exec_anki(anki_bin: &std::path::Path, config: &Config) -> Result<()> { + let args: Vec = std::env::args().skip(1).collect(); + let mut cmd = build_python_command(anki_bin, &args, config)?; + cmd.ensure_success()?; + Ok(()) +} + +pub fn get_exe_and_resources_dirs() -> Result<(PathBuf, PathBuf)> { + let exe_dir = std::env::current_exe() + .context("Failed to get current executable path")? + .parent() + .context("Failed to get executable directory")? + .to_owned(); + + // On Windows, resources dir is the same as exe_dir + let resources_dir = exe_dir.clone(); + + Ok((exe_dir, resources_dir)) +} + +pub fn get_uv_binary_name() -> &'static str { + // Windows uses standard uv binary name + "uv.exe" +} diff --git a/qt/bundle/win/anki-console.bat b/qt/launcher/win/anki-console.bat similarity index 100% rename from qt/bundle/win/anki-console.bat rename to qt/launcher/win/anki-console.bat diff --git a/qt/bundle/win/anki-icon.ico b/qt/launcher/win/anki-icon.ico similarity index 100% rename from qt/bundle/win/anki-icon.ico rename to qt/launcher/win/anki-icon.ico diff --git a/qt/bundle/win/anki-manifest.rc b/qt/launcher/win/anki-manifest.rc similarity index 100% rename from qt/bundle/win/anki-manifest.rc rename to qt/launcher/win/anki-manifest.rc diff --git a/qt/launcher/win/anki.exe.manifest b/qt/launcher/win/anki.exe.manifest new file mode 100644 index 000000000..93e98c6ec --- /dev/null +++ b/qt/launcher/win/anki.exe.manifest @@ -0,0 +1,25 @@ + + + + + + + + + + + + + true + PerMonitorV2 + true + UTF-8 + + + + + + + + + diff --git a/qt/bundle/win/anki.template.nsi b/qt/launcher/win/anki.template.nsi similarity index 90% rename from qt/bundle/win/anki.template.nsi rename to qt/launcher/win/anki.template.nsi index 513c81bfb..7b2bfd8fc 100644 --- a/qt/bundle/win/anki.template.nsi +++ b/qt/launcher/win/anki.template.nsi @@ -23,8 +23,8 @@ Name "Anki" Unicode true -; The file to write (make relative to repo root instead of out/bundle) -OutFile "..\..\@@INSTALLER@@" +; The file to write (relative to nsis directory) +OutFile "..\launcher_exe\anki-install.exe" ; Non elevated RequestExecutionLevel user @@ -62,7 +62,7 @@ Function .onInit FunctionEnd !ifdef WRITE_UNINSTALLER -!uninstfinalize 'copy "%1" "std\uninstall.exe"' +!uninstfinalize 'copy "%1" "uninstall.exe"' !endif ;-------------------------------- @@ -191,7 +191,7 @@ Section "" ; Add files to installer !ifndef WRITE_UNINSTALLER - File /r ..\..\@@SRC@@\*.* + File /r ..\launcher\*.* !endif !insertmacro APP_ASSOCIATE_HKCU "apkg" "anki.apkg" \ @@ -213,8 +213,8 @@ Section "" WriteRegStr HKCU Software\Anki "Install_Dir64" "$INSTDIR" ; Write the uninstall keys for Windows - WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\Anki" "DisplayName" "Anki" - WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\Anki" "DisplayVersion" "@@VERSION@@" + WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\Anki" "DisplayName" "Anki Launcher" + WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\Anki" "DisplayVersion" "1.0.0" WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\Anki" "UninstallString" '"$INSTDIR\uninstall.exe"' WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\Anki" "QuietUninstallString" '"$INSTDIR\uninstall.exe" /S' WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\Anki" "NoModify" 1 @@ -224,10 +224,29 @@ Section "" WriteUninstaller "uninstall.exe" !endif + ; Ensure uv gets re-run + Push "$INSTDIR\pyproject.toml" + Call TouchFile + + ; Launch Anki after installation + Exec "$INSTDIR\anki.exe" + Quit + SectionEnd ; end the section ;-------------------------------- +; Touch file function to update mtime using copy trick +Function TouchFile + Exch $R0 ; file path + + nsExec::Exec 'cmd /c "copy /B "$R0" +,,"' + + Pop $R0 +FunctionEnd + +;-------------------------------- + ; Uninstaller function un.onInit @@ -252,9 +271,15 @@ Section "Uninstall" !insertmacro APP_UNASSOCIATE_HKCU "ankiaddon" "anki.ankiaddon" !insertmacro UPDATEFILEASSOC + ; Schedule uninstaller for deletion on reboot + Delete /REBOOTOK "$INSTDIR\uninstall.exe" + ; try to remove top level folder if empty RMDir "$INSTDIR" + ; Remove AnkiProgramData folder created during runtime + RMDir /r "$LOCALAPPDATA\AnkiProgramFiles" + ; Remove registry keys DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\Anki" DeleteRegKey HKCU Software\Anki diff --git a/qt/launcher/win/build.bat b/qt/launcher/win/build.bat new file mode 100644 index 000000000..b21831462 --- /dev/null +++ b/qt/launcher/win/build.bat @@ -0,0 +1,5 @@ +@echo off + +set CODESIGN=1 +REM set NO_COMPRESS=1 +cargo run --bin build_win diff --git a/qt/bundle/win/fileassoc.nsh b/qt/launcher/win/fileassoc.nsh similarity index 100% rename from qt/bundle/win/fileassoc.nsh rename to qt/launcher/win/fileassoc.nsh diff --git a/qt/mac/ankihelper.xcodeproj/project.pbxproj b/qt/mac/ankihelper.xcodeproj/project.pbxproj index 8232fba8e..016b03192 100644 --- a/qt/mac/ankihelper.xcodeproj/project.pbxproj +++ b/qt/mac/ankihelper.xcodeproj/project.pbxproj @@ -172,7 +172,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 11.6; + MACOSX_DEPLOYMENT_TARGET = 11; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -225,7 +225,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 11.6; + MACOSX_DEPLOYMENT_TARGET = 11; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; diff --git a/qt/mac/helper_build.py b/qt/mac/helper_build.py index 4edbd05c1..0638c9249 100644 --- a/qt/mac/helper_build.py +++ b/qt/mac/helper_build.py @@ -11,19 +11,32 @@ out_dylib, *src_files = sys.argv[1:] out_dir = Path(out_dylib).parent.resolve() src_dir = Path(src_files[0]).parent.resolve() -if platform.machine() == "arm64" and not os.environ.get("MAC_X86"): - target = "arm64-apple-macos11" -else: - target = "x86_64-apple-macos10.14" +# Build for both architectures +architectures = ["arm64", "x86_64"] +temp_files = [] -args = [ - "swiftc", - "-target", - target, - "-emit-library", - "-module-name", - "ankihelper", - "-O", -] -args.extend(src_dir / Path(file).name for file in src_files) -subprocess.run(args, check=True, cwd=out_dir) +for arch in architectures: + target = f"{arch}-apple-macos11" + temp_out = out_dir / f"temp_{arch}.dylib" + temp_files.append(temp_out) + + args = [ + "swiftc", + "-target", + target, + "-emit-library", + "-module-name", + "ankihelper", + "-O", + ] + args.extend(src_dir / Path(file).name for file in src_files) + args.extend(["-o", str(temp_out)]) + subprocess.run(args, check=True, cwd=out_dir) + +# Create universal binary +lipo_args = ["lipo", "-create", "-output", out_dylib] + [str(f) for f in temp_files] +subprocess.run(lipo_args, check=True) + +# Clean up temporary files +for temp_file in temp_files: + temp_file.unlink() diff --git a/qt/pyproject.toml b/qt/pyproject.toml new file mode 100644 index 000000000..ab16afc3d --- /dev/null +++ b/qt/pyproject.toml @@ -0,0 +1,80 @@ +[project] +name = "aqt" +# dynamic = ["version"] +version = "0.1.2" +requires-python = ">=3.9" +license = "AGPL-3.0-or-later" +dependencies = [ + "beautifulsoup4", + "flask", + "flask_cors", + "jsonschema", + "requests", + "send2trash", + "waitress>=2.0.0", + "psutil; sys.platform == 'win32'", + "pywin32; sys.platform == 'win32'", + "pip-system-certs!=5.1", + "mock", + "types-decorator", + "types-flask", + "types-flask-cors", + "types-markdown", + "types-waitress", + "types-pywin32", + "pyqt6>=6.2", + "pyqt6-webengine>=6.2", +] + +[project.optional-dependencies] +audio = [ + "anki-audio==0.1.0; sys.platform == 'win32' or sys.platform == 'darwin'", +] +qt66 = [ + "pyqt6==6.6.1", + "pyqt6-qt6==6.6.2", + "pyqt6-webengine==6.6.0", + "pyqt6-webengine-qt6==6.6.2", + "pyqt6_sip==13.6.0", +] +qt67 = [ + "pyqt6==6.7.1", + "pyqt6-qt6==6.7.3", + "pyqt6-webengine==6.7.0", + "pyqt6-webengine-qt6==6.7.3", + "pyqt6_sip==13.10.2", +] +qt = [ + "pyqt6==6.8.0", + "pyqt6-qt6==6.8.1", + "pyqt6-webengine==6.8.0", + "pyqt6-webengine-qt6==6.8.1", + "pyqt6_sip==13.10.2", +] + +[tool.uv] +conflicts = [ + [ + { extra = "qt" }, + { extra = "qt66" }, + { extra = "qt67" }, + ], +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project.scripts] +anki = "aqt:run" + +[tool.hatch.build.targets.wheel] +packages = ["aqt"] +exclude = ["**/*.scss", "**/*.ui"] + +[tool.hatch.version] +source = "code" +path = "../python/version.py" + +[tool.hatch.build.hooks.custom] +path = "hatch_build.py" diff --git a/qt/release/publish.sh b/qt/release/publish.sh new file mode 100755 index 000000000..b906637f9 --- /dev/null +++ b/qt/release/publish.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +export UV_PUBLISH_TOKEN=$(pass show w/pypi-api-test) + +# Get the project root (two levels up from qt/release) +PROJ_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" + +# Use extracted uv binary +UV="$PROJ_ROOT/out/extracted/uv/uv" + +rm -rf dist +"$UV" build --wheel +"$UV" publish --index testpypi diff --git a/qt/release/pyproject.toml b/qt/release/pyproject.toml new file mode 100644 index 000000000..8f9a8fe63 --- /dev/null +++ b/qt/release/pyproject.toml @@ -0,0 +1,80 @@ +[project] +name = "anki-release" +version = "0.1.3" +description = "A package to lock Anki's dependencies" +requires-python = ">=3.9" +dependencies = [ + "anki==0.1.2", + "aqt==0.1.2", + "anki-audio==0.1.0 ; sys_platform == 'darwin' or sys_platform == 'win32'", + "attrs==25.3.0", + "beautifulsoup4==4.12.3", + "blinker==1.9.0", + "certifi==2025.4.26", + "charset-normalizer==3.4.2", + "click==8.1.8 ; python_full_version < '3.10'", + "click==8.2.1 ; python_full_version >= '3.10'", + "colorama==0.4.6 ; sys_platform == 'win32'", + "decorator==5.2.1", + "distro==1.9.0 ; sys_platform != 'darwin' and sys_platform != 'win32'", + "flask==3.1.1", + "flask-cors==6.0.0", + "idna==3.10", + "importlib-metadata==8.7.0 ; python_full_version < '3.10'", + "itsdangerous==2.2.0", + "jinja2==3.1.6", + "jsonschema==4.24.0", + "jsonschema-specifications==2025.4.1", + "markdown==3.8", + "markupsafe==3.0.2", + "mock==5.2.0", + "orjson==3.10.18", + "pip-system-certs==4.0", + "protobuf==6.31.1", + "psutil==7.0.0 ; sys_platform == 'win32'", + "pyqt6==6.8.0", + "pyqt6-qt6==6.8.1", + "pyqt6-sip==13.10.2", + "pyqt6-webengine==6.8.0", + "pyqt6-webengine-qt6==6.8.1", + "pysocks==1.7.1", + "pywin32==310 ; sys_platform == 'win32'", + "referencing==0.36.2", + "requests==2.32.3", + "rpds-py==0.25.1", + "send2trash==1.8.3", + "soupsieve==2.7", + "types-click==7.1.8", + "types-decorator==5.2.0.20250324", + "types-flask==1.1.6", + "types-flask-cors==6.0.0.20250520", + "types-jinja2==2.11.9", + "types-markdown==3.8.0.20250415", + "types-markupsafe==1.1.10", + "types-orjson==3.6.2", + "types-protobuf==6.30.2.20250516", + "types-pywin32==310.0.0.20250516", + "types-requests==2.32.0.20250602", + "types-waitress==3.0.1.20241117", + "types-werkzeug==1.0.9", + "typing-extensions==4.14.0", + "urllib3==2.4.0", + "waitress==3.0.2", + "werkzeug==3.1.3", + "wrapt==1.17.2", + "zipp==3.23.0 ; python_full_version < '3.10'", +] + +[[tool.uv.index]] +name = "testpypi" +url = "https://test.pypi.org/simple/" +publish-url = "https://test.pypi.org/legacy/" +explicit = true + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +# hatch throws an error if nothing is included +[tool.hatch.build.targets.wheel] +include = ["no-such-file"] diff --git a/qt/release/update.sh b/qt/release/update.sh new file mode 100755 index 000000000..126214248 --- /dev/null +++ b/qt/release/update.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +set -e + +test -f update.sh || { + echo "run from release folder" + exit 1 +} + +# Get the project root (two levels up from qt/release) +PROJ_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" + +# Use extracted uv binary +UV="$PROJ_ROOT/out/extracted/uv/uv" + +# Prompt for wheel version +read -p "Wheel version: " VERSION + +# Export dependencies using uv +echo "Exporting dependencies..." +rm -f pyproject.toml +DEPS=$("$UV" export --no-hashes --no-annotate --no-header --extra audio --extra qt --all-packages --no-dev --no-emit-workspace) + +# Generate the pyproject.toml file +cat > pyproject.toml << EOF +[project] +name = "anki-release" +version = "$VERSION" +description = "A package to lock Anki's dependencies" +requires-python = ">=3.9" +dependencies = [ + "anki==$VERSION", + "aqt==$VERSION", +EOF + +# Add the exported dependencies to the file +echo "$DEPS" | while IFS= read -r line; do + if [[ -n "$line" ]]; then + echo " \"$line\"," >> pyproject.toml + fi +done + +# Complete the pyproject.toml file +cat >> pyproject.toml << 'EOF' +] + +[[tool.uv.index]] +name = "testpypi" +url = "https://test.pypi.org/simple/" +publish-url = "https://test.pypi.org/legacy/" +explicit = true + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +# hatch throws an error if nothing is included +[tool.hatch.build.targets.wheel] +include = ["no-such-file"] +EOF + +echo "Generated pyproject.toml with version $VERSION" diff --git a/rslib/Cargo.toml b/rslib/Cargo.toml index 1c4abf46f..d3c7e215f 100644 --- a/rslib/Cargo.toml +++ b/rslib/Cargo.toml @@ -106,6 +106,5 @@ unicode-normalization.workspace = true zip.workspace = true zstd.workspace = true -[target.'cfg(windows)'.dependencies.windows] -version = "0.56.0" -features = ["Media_SpeechSynthesis", "Foundation_Collections", "Storage_Streams"] +[target.'cfg(windows)'.dependencies] +windows.workspace = true diff --git a/rslib/linkchecker/tests/links.rs b/rslib/linkchecker/tests/links.rs index 48656ace4..2f39fbe31 100644 --- a/rslib/linkchecker/tests/links.rs +++ b/rslib/linkchecker/tests/links.rs @@ -52,7 +52,7 @@ impl CheckableUrl { fn anchor(&self) -> Cow { match *self { Self::HelpPage(page) => help_page_link_suffix(page).into(), - Self::String(s) => s.split('#').last().unwrap_or_default().into(), + Self::String(s) => s.split('#').next_back().unwrap_or_default().into(), } } } diff --git a/rslib/process/src/lib.rs b/rslib/process/src/lib.rs index d026dec34..dcf0703f6 100644 --- a/rslib/process/src/lib.rs +++ b/rslib/process/src/lib.rs @@ -57,6 +57,9 @@ pub trait CommandExt { fn ensure_success(&mut self) -> Result<&mut Self>; fn utf8_output(&mut self) -> Result; + fn ensure_spawn(&mut self) -> Result; + #[cfg(unix)] + fn ensure_exec(&mut self) -> Result<()>; } impl CommandExt for Command { @@ -94,6 +97,23 @@ impl CommandExt for Command { })?, }) } + + fn ensure_spawn(&mut self) -> Result { + self.spawn().with_context(|_| DidNotExecuteSnafu { + cmdline: get_cmdline(self), + }) + } + + #[cfg(unix)] + fn ensure_exec(&mut self) -> Result<()> { + use std::os::unix::process::CommandExt as UnixCommandExt; + let cmdline = get_cmdline(self); + let error = self.exec(); + Err(Error::DidNotExecute { + cmdline, + source: error, + }) + } } fn get_cmdline(arg: &mut Command) -> String { diff --git a/rslib/src/card_rendering/tts/windows.rs b/rslib/src/card_rendering/tts/windows.rs index a53994c2e..5afb5cc2d 100644 --- a/rslib/src/card_rendering/tts/windows.rs +++ b/rslib/src/card_rendering/tts/windows.rs @@ -5,12 +5,13 @@ use std::fs::File; use std::io::Write; use anki_proto::card_rendering::all_tts_voices_response::TtsVoice; -use futures::executor::block_on; +use windows::core::Interface; use windows::core::HSTRING; use windows::Media::SpeechSynthesis::SpeechSynthesisStream; use windows::Media::SpeechSynthesis::SpeechSynthesizer; use windows::Media::SpeechSynthesis::VoiceInformation; use windows::Storage::Streams::DataReader; +use windows::Storage::Streams::IRandomAccessStream; use crate::error::windows::WindowsErrorDetails; use crate::error::windows::WindowsSnafu; @@ -45,7 +46,7 @@ fn find_voice(voice_id: &str) -> Result { fn to_hstring(text: &str) -> HSTRING { let utf16: Vec = text.encode_utf16().collect(); - HSTRING::from_wide(&utf16).expect("Strings are valid Unicode") + HSTRING::from_wide(&utf16) } fn synthesize_stream( @@ -64,16 +65,20 @@ fn synthesize_stream( details: WindowsErrorDetails::SettingRate(speed), })?; let async_op = synthesizer.SynthesizeTextToStreamAsync(&to_hstring(text))?; - let stream = block_on(async_op).context(WindowsSnafu { + let stream = async_op.get().context(WindowsSnafu { details: WindowsErrorDetails::Synthesizing, })?; Ok(stream) } fn write_stream_to_path(stream: SpeechSynthesisStream, path: &str) -> Result<()> { - let input_stream = stream.GetInputStreamAt(0)?; + let random_access_stream: IRandomAccessStream = stream.cast()?; + let input_stream = random_access_stream.GetInputStreamAt(0)?; let date_reader = DataReader::CreateDataReader(&input_stream)?; - let stream_size = stream.Size()?.try_into().or_invalid("stream too large")?; + let stream_size = random_access_stream + .Size()? + .try_into() + .or_invalid("stream too large")?; date_reader.LoadAsync(stream_size)?; let mut file = File::create(path)?; write_reader_to_file(date_reader, &mut file, stream_size as usize) diff --git a/rslib/src/import_export/package/colpkg/export.rs b/rslib/src/import_export/package/colpkg/export.rs index 9d85c19aa..20f25a1c1 100644 --- a/rslib/src/import_export/package/colpkg/export.rs +++ b/rslib/src/import_export/package/colpkg/export.rs @@ -146,8 +146,12 @@ pub(crate) fn export_collection( Ok(()) } -fn file_options_stored() -> FileOptions { - FileOptions::default().compression_method(CompressionMethod::Stored) +fn file_options_stored() -> FileOptions<'static, ()> { + FileOptions::<'static, ()>::default().compression_method(CompressionMethod::Stored) +} + +fn file_options_default() -> FileOptions<'static, ()> { + FileOptions::<'static, ()>::default() } fn write_collection( @@ -160,7 +164,7 @@ fn write_collection( zip.start_file(meta.collection_filename(), file_options_stored())?; zstd_copy(col, zip, size)?; } else { - zip.start_file(meta.collection_filename(), FileOptions::default())?; + zip.start_file(meta.collection_filename(), file_options_default())?; io::copy(col, zip)?; } Ok(()) diff --git a/rslib/src/import_export/package/colpkg/import.rs b/rslib/src/import_export/package/colpkg/import.rs index b8316c032..5ae8b4c8e 100644 --- a/rslib/src/import_export/package/colpkg/import.rs +++ b/rslib/src/import_export/package/colpkg/import.rs @@ -124,7 +124,7 @@ fn maybe_restore_media_file( Ok(()) } -fn restore_media_file(meta: &Meta, zip_file: &mut ZipFile, path: &Path) -> Result<()> { +fn restore_media_file(meta: &Meta, zip_file: &mut ZipFile, path: &Path) -> Result<()> { let mut tempfile = new_tempfile_in_parent_of(path)?; meta.copy(zip_file, &mut tempfile) .with_context(|_| FileIoSnafu { diff --git a/rslib/src/import_export/package/media.rs b/rslib/src/import_export/package/media.rs index 6b0b4c370..ff5bdf4d7 100644 --- a/rslib/src/import_export/package/media.rs +++ b/rslib/src/import_export/package/media.rs @@ -96,7 +96,10 @@ impl SafeMediaEntry { media_folder.join(&self.name) } - pub(super) fn fetch_file<'a>(&self, archive: &'a mut ZipArchive) -> Result> { + pub(super) fn fetch_file<'a>( + &self, + archive: &'a mut ZipArchive, + ) -> Result> { match archive.by_name(&self.index.to_string()) { Ok(file) => Ok(file), Err(err) => invalid_input!(err, "{} missing from archive", self.index), diff --git a/rslib/src/notetype/cardgen.rs b/rslib/src/notetype/cardgen.rs index 8ee2717ba..8e03d8ee4 100644 --- a/rslib/src/notetype/cardgen.rs +++ b/rslib/src/notetype/cardgen.rs @@ -352,7 +352,7 @@ impl Collection { fn random_position(highest_position: u32) -> u32 { let mut rng = StdRng::seed_from_u64(highest_position as u64); - rng.gen_range(1..highest_position.max(1000)) + rng.random_range(1..highest_position.max(1000)) } #[cfg(test)] diff --git a/rslib/src/scheduler/answering/learning.rs b/rslib/src/scheduler/answering/learning.rs index 80204e13f..3283dc3ee 100644 --- a/rslib/src/scheduler/answering/learning.rs +++ b/rslib/src/scheduler/answering/learning.rs @@ -87,7 +87,7 @@ impl CardStateUpdater { if secs >= upper_exclusive { secs } else { - rng.gen_range(secs..upper_exclusive) + rng.random_range(secs..upper_exclusive) } } else { secs diff --git a/rslib/src/scheduler/answering/mod.rs b/rslib/src/scheduler/answering/mod.rs index 02e5ee8d6..d498f7eaf 100644 --- a/rslib/src/scheduler/answering/mod.rs +++ b/rslib/src/scheduler/answering/mod.rs @@ -630,7 +630,7 @@ fn get_fuzz_seed_for_id_and_reps(card_id: CardId, card_reps: u32) -> Option /// Return a fuzz factor from the range `0.0..1.0`, using the provided seed. /// None if seed is None. fn get_fuzz_factor(seed: Option) -> Option { - seed.map(|s| StdRng::seed_from_u64(s).gen_range(0.0..1.0)) + seed.map(|s| StdRng::seed_from_u64(s).random_range(0.0..1.0)) } #[cfg(test)] diff --git a/rslib/src/scheduler/fsrs/simulator.rs b/rslib/src/scheduler/fsrs/simulator.rs index 133b3ff2c..34cc925d6 100644 --- a/rslib/src/scheduler/fsrs/simulator.rs +++ b/rslib/src/scheduler/fsrs/simulator.rs @@ -68,7 +68,7 @@ pub(crate) fn apply_load_balance_and_easy_days( sibling_modifier: 1.0, easy_days_modifier: easy_days_modifier[interval_index], }); - let fuzz_seed = rng.gen(); + let fuzz_seed = rng.random(); select_weighted_interval(intervals, Some(fuzz_seed)).unwrap() as f32 } @@ -106,7 +106,7 @@ fn create_review_priority_fn( // Random ordering Random => { - wrap!(move |_c, _w| rand::thread_rng().gen_range(0..deck_size) as i32) + wrap!(move |_c, _w| rand::rng().random_range(0..deck_size) as i32) } // Not implemented yet diff --git a/rslib/src/scheduler/new.rs b/rslib/src/scheduler/new.rs index 73b7abf5f..541d8d55e 100644 --- a/rslib/src/scheduler/new.rs +++ b/rslib/src/scheduler/new.rs @@ -127,7 +127,7 @@ fn nids_in_desired_order(cards: &[Card], order: NewCardDueOrder) -> Vec nids.sort_unstable(); } NewCardDueOrder::Random => { - nids.shuffle(&mut rand::thread_rng()); + nids.shuffle(&mut rand::rng()); } NewCardDueOrder::Preserve => unreachable!(), } diff --git a/rslib/src/scheduler/reviews.rs b/rslib/src/scheduler/reviews.rs index 0a7f32032..7e61447da 100644 --- a/rslib/src/scheduler/reviews.rs +++ b/rslib/src/scheduler/reviews.rs @@ -4,8 +4,8 @@ use std::collections::HashMap; use std::sync::LazyLock; -use rand::distributions::Distribution; -use rand::distributions::Uniform; +use rand::distr::Distribution; +use rand::distr::Uniform; use regex::Regex; use super::answering::CardAnswer; @@ -114,8 +114,8 @@ impl Collection { let spec = parse_due_date_str(days)?; let usn = self.usn()?; let today = self.timing_today()?.days_elapsed; - let mut rng = rand::thread_rng(); - let distribution = Uniform::from(spec.min..=spec.max); + let mut rng = rand::rng(); + let distribution = Uniform::new_inclusive(spec.min, spec.max).unwrap(); let mut decks_initial_ease: HashMap = HashMap::new(); self.transact(Op::SetDueDate, |col| { for mut card in col.all_cards_for_ids(cids, false)? { diff --git a/rslib/src/scheduler/states/load_balancer.rs b/rslib/src/scheduler/states/load_balancer.rs index 915b0b8b3..20b6936df 100644 --- a/rslib/src/scheduler/states/load_balancer.rs +++ b/rslib/src/scheduler/states/load_balancer.rs @@ -5,8 +5,8 @@ use std::collections::HashMap; use std::collections::HashSet; use chrono::Datelike; -use rand::distributions::Distribution; -use rand::distributions::WeightedIndex; +use rand::distr::weighted::WeightedIndex; +use rand::distr::Distribution; use rand::rngs::StdRng; use rand::SeedableRng; diff --git a/rslib/src/storage/sqlite.rs b/rslib/src/storage/sqlite.rs index f882cee32..e31fdd46a 100644 --- a/rslib/src/storage/sqlite.rs +++ b/rslib/src/storage/sqlite.rs @@ -15,6 +15,7 @@ use fsrs::FSRS5_DEFAULT_DECAY; use regex::Regex; use rusqlite::functions::FunctionFlags; use rusqlite::params; +use rusqlite::trace::TraceEvent; use rusqlite::Connection; use serde_json::Value; use unicase::UniCase; @@ -47,10 +48,13 @@ pub struct SqliteStorage { } fn open_or_create_collection_db(path: &Path) -> Result { - let mut db = Connection::open(path)?; + let db = Connection::open(path)?; if std::env::var("TRACESQL").is_ok() { - db.trace(Some(trace)); + db.trace_v2( + rusqlite::trace::TraceEventCodes::SQLITE_TRACE_STMT, + Some(trace), + ); } db.busy_timeout(std::time::Duration::from_secs(0))?; @@ -415,8 +419,10 @@ fn schema_version(db: &Connection) -> Result<(bool, u8)> { )) } -fn trace(s: &str) { - println!("sql: {}", s.trim().replace('\n', " ")); +fn trace(event: TraceEvent) { + if let TraceEvent::Stmt(_, sql) = event { + println!("sql: {}", sql.trim().replace('\n', " ")); + } } impl SqliteStorage { diff --git a/rslib/src/sync/http_server/mod.rs b/rslib/src/sync/http_server/mod.rs index f96209df7..ddac26670 100644 --- a/rslib/src/sync/http_server/mod.rs +++ b/rslib/src/sync/http_server/mod.rs @@ -22,7 +22,7 @@ use anki_io::create_dir_all; use axum::extract::DefaultBodyLimit; use axum::routing::get; use axum::Router; -use axum_client_ip::SecureClientIpSource; +use axum_client_ip::ClientIpSource; use pbkdf2::password_hash::PasswordHash; use pbkdf2::password_hash::PasswordHasher; use pbkdf2::password_hash::PasswordVerifier; @@ -69,7 +69,7 @@ pub struct SyncServerConfig { #[serde(default = "default_base", rename = "base")] pub base_folder: PathBuf, #[serde(default = "default_ip_header")] - pub ip_header: SecureClientIpSource, + pub ip_header: ClientIpSource, } fn default_host() -> IpAddr { @@ -86,8 +86,8 @@ fn default_base() -> PathBuf { .join(".syncserver") } -pub fn default_ip_header() -> SecureClientIpSource { - SecureClientIpSource::ConnectInfo +pub fn default_ip_header() -> ClientIpSource { + ClientIpSource::ConnectInfo } impl SimpleServerInner { diff --git a/rslib/src/sync/http_server/routes.rs b/rslib/src/sync/http_server/routes.rs index dd4c0d3bd..072ad6140 100644 --- a/rslib/src/sync/http_server/routes.rs +++ b/rslib/src/sync/http_server/routes.rs @@ -53,7 +53,7 @@ async fn sync_handler( } pub fn collection_sync_router() -> Router

{ - Router::new().route("/:method", post(sync_handler::

)) + Router::new().route("/{method}", post(sync_handler::

)) } /// The Rust code used to send a GET with query params, which was inconsistent @@ -112,5 +112,5 @@ pub fn media_sync_router() -> Router

{ "/begin", get(media_begin_get::

).post(media_begin_post::

), ) - .route("/:method", post(media_sync_handler::

)) + .route("/{method}", post(media_sync_handler::

)) } diff --git a/rslib/src/sync/media/database/client/mod.rs b/rslib/src/sync/media/database/client/mod.rs index e58cb4fd9..5fe493679 100644 --- a/rslib/src/sync/media/database/client/mod.rs +++ b/rslib/src/sync/media/database/client/mod.rs @@ -283,15 +283,20 @@ fn row_to_name_and_checksum(row: &Row) -> error::Result<(String, Sha1Hash)> { Ok((file_name, sha1)) } -fn trace(s: &str) { - println!("sql: {}", s) +fn trace(event: rusqlite::trace::TraceEvent) { + if let rusqlite::trace::TraceEvent::Stmt(_, sql) = event { + println!("sql: {}", sql); + } } pub(crate) fn open_or_create>(path: P) -> error::Result { let mut db = Connection::open(path)?; if std::env::var("TRACESQL").is_ok() { - db.trace(Some(trace)); + db.trace_v2( + rusqlite::trace::TraceEventCodes::SQLITE_TRACE_STMT, + Some(trace), + ); } db.pragma_update(None, "page_size", 4096)?; diff --git a/rslib/src/sync/media/zip.rs b/rslib/src/sync/media/zip.rs index 0ad558d2d..d9b04f5e3 100644 --- a/rslib/src/sync/media/zip.rs +++ b/rslib/src/sync/media/zip.rs @@ -27,7 +27,8 @@ pub struct ZipFileMetadata { /// The metadata is in a different format to the upload case, since deletions /// don't need to be represented. pub fn zip_files_for_download(files: Vec<(String, Vec)>) -> Result> { - let options = FileOptions::default().compression_method(zip::CompressionMethod::Stored); + let options: FileOptions<'_, ()> = + FileOptions::default().compression_method(zip::CompressionMethod::Stored); let mut zip = ZipWriter::new(io::Cursor::new(vec![])); let mut entries = HashMap::new(); @@ -47,7 +48,8 @@ pub fn zip_files_for_download(files: Vec<(String, Vec)>) -> Result> } pub fn zip_files_for_upload(entries_: Vec<(String, Option>)>) -> Result> { - let options = FileOptions::default().compression_method(zip::CompressionMethod::Stored); + let options: FileOptions<'_, ()> = + FileOptions::default().compression_method(zip::CompressionMethod::Stored); let mut zip = ZipWriter::new(io::Cursor::new(vec![])); let mut entries = vec![]; diff --git a/rslib/src/sync/request/mod.rs b/rslib/src/sync/request/mod.rs index 4678cef9b..329aa44dd 100644 --- a/rslib/src/sync/request/mod.rs +++ b/rslib/src/sync/request/mod.rs @@ -10,14 +10,13 @@ use std::marker::PhantomData; use std::net::IpAddr; use std::sync::LazyLock; -use async_trait::async_trait; use axum::body::Body; use axum::extract::FromRequest; use axum::extract::Multipart; use axum::http::Request; use axum::http::StatusCode; use axum::RequestPartsExt; -use axum_client_ip::SecureClientIp; +use axum_client_ip::ClientIp; use axum_extra::TypedHeader; use header_and_stream::SyncHeader; use serde::de::DeserializeOwned; @@ -101,19 +100,18 @@ where } } -#[async_trait] -impl FromRequest for SyncRequest +impl FromRequest for SyncRequest where S: Send + Sync, T: DeserializeOwned, { type Rejection = HttpError; - async fn from_request(req: Request, state: &S) -> HttpResult { + async fn from_request(req: Request, state: &S) -> Result { let (mut parts, body) = req.into_parts(); let ip = parts - .extract::() + .extract::() .await .map_err(|_| { HttpError::new_without_source(StatusCode::INTERNAL_SERVER_ERROR, "missing ip") diff --git a/run b/run index fd65a70c7..3051345b1 100755 --- a/run +++ b/run @@ -8,6 +8,7 @@ export PYTHONPYCACHEPREFIX=out/pycache export ANKIDEV=${ANKIDEV-1} export QTWEBENGINE_REMOTE_DEBUGGING=${QTWEBENGINE_REMOTE_DEBUGGING-8080} export QTWEBENGINE_CHROMIUM_FLAGS=${QTWEBENGINE_CHROMIUM_FLAGS---remote-allow-origins=http://localhost:$QTWEBENGINE_REMOTE_DEBUGGING} +export PYENV=${PYENV-out/pyenv} # The pages can be accessed by, e.g. surfing to # http://localhost:40000/_anki/pages/deckconfig.html @@ -16,4 +17,4 @@ export ANKI_API_PORT=${ANKI_API_PORT-40000} export ANKI_API_HOST=${ANKI_API_HOST-127.0.0.1} ./ninja pylib qt -./out/pyenv/bin/python tools/run.py $* +${PYENV}/bin/python tools/run.py $* diff --git a/run.bat b/run.bat index 03acf0032..c689dda16 100755 --- a/run.bat +++ b/run.bat @@ -9,6 +9,6 @@ set QTWEBENGINE_CHROMIUM_FLAGS=--remote-allow-origins=http://localhost:8080 set ANKI_API_PORT=40000 set ANKI_API_HOST=127.0.0.1 -call tools\ninja pylib qt extract:win_amd64_audio || exit /b 1 +call tools\ninja pylib qt || exit /b 1 .\out\pyenv\scripts\python tools\run.py %* || exit /b 1 popd diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 608f0e356..fa07f7fa5 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] # older versions may fail to compile; newer versions may fail the clippy tests -channel = "1.85.0" +channel = "1.87.0" diff --git a/tools/build b/tools/build index 4fd78e195..3df9456ed 100755 --- a/tools/build +++ b/tools/build @@ -1,5 +1,6 @@ #!/bin/bash set -e -RELEASE=1 ./ninja wheels +rm -rf out/wheels/* +RELEASE=2 ./ninja wheels echo "wheels are in out/wheels" diff --git a/tools/build-arm-lin b/tools/build-arm-lin new file mode 100755 index 000000000..67fb4e4bf --- /dev/null +++ b/tools/build-arm-lin @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e + +# sudo apt install libc6-dev-arm64-cross gcc-aarch64-linux-gnu +rustup target add aarch64-unknown-linux-gnu + +export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc +export LIN_ARM64=1 + +RELEASE=2 ./ninja wheels:anki +echo "wheels are in out/wheels" diff --git a/tools/build-x64-mac b/tools/build-x64-mac new file mode 100755 index 000000000..050b9c636 --- /dev/null +++ b/tools/build-x64-mac @@ -0,0 +1,10 @@ +#!/bin/bash + +set -e + +rustup target add x86_64-apple-darwin + +export MAC_X86=1 + +RELEASE=2 ./ninja wheels:anki +echo "wheels are in out/wheels" diff --git a/tools/build.bat b/tools/build.bat index 364f0f0f8..3ace08110 100755 --- a/tools/build.bat +++ b/tools/build.bat @@ -1,5 +1,7 @@ @echo off pushd "%~dp0"\.. -set RELEASE=1 +if exist out\wheels rmdir /s /q out\wheels +set RELEASE=2 tools\ninja wheels || exit /b 1 +echo wheels are in out/wheels popd diff --git a/tools/mac-x86 b/tools/mac-x86 deleted file mode 100755 index 312e19320..000000000 --- a/tools/mac-x86 +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# -# Run a command with an alternative buildroot and Intel architecture target, for building Intel on an ARM Mac. -# Eg ./tools/mac-x86 ./tools/run-qt5.14 -# -# Uses hard-coded paths to Python and build folders. -# - -export PYTHON_BINARY=/usr/local/bin/python3.9-intel64 -export BUILD_ROOT=~/Local/build/anki-x86 -export NORMAL_BUILD_ROOT=~/Local/build/anki -export MAC_X86=1 - - -# run provided command -$* - -BUILD_ROOT=$NORMAL_BUILD_ROOT ./ninja just-to-restore-build-root-and-failure-is-expected diff --git a/tools/minilints/src/main.rs b/tools/minilints/src/main.rs index dfe9bef89..3a3c06f2c 100644 --- a/tools/minilints/src/main.rs +++ b/tools/minilints/src/main.rs @@ -29,8 +29,6 @@ const NONSTANDARD_HEADER: &[&str] = &[ "./python/write_wheel.py", "./qt/aqt/mpv.py", "./qt/aqt/winpaths.py", - "./qt/bundle/build.rs", - "./qt/bundle/src/main.rs", ]; const IGNORED_FOLDERS: &[&str] = &[ @@ -38,7 +36,6 @@ const IGNORED_FOLDERS: &[&str] = &[ "./node_modules", "./qt/aqt/forms", "./tools/workspace-hack", - "./qt/bundle/PyOxidizer", "./target", ".mypy_cache", "./extra", @@ -209,7 +206,7 @@ fn sveltekit_temp_file(path: &str) -> bool { } fn check_cargo_deny() -> Result<()> { - Command::run("cargo install cargo-deny@0.18.2")?; + Command::run("cargo install cargo-deny@0.18.3")?; Command::run("cargo deny check")?; Ok(()) } diff --git a/tools/publish b/tools/publish new file mode 100755 index 000000000..570f75c85 --- /dev/null +++ b/tools/publish @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e + +export UV_PUBLISH_TOKEN=$(pass show w/pypi-api-test) +out/extracted/uv/uv publish --index testpypi out/wheels/* + diff --git a/tools/run-qt5.14 b/tools/run-qt5.14 deleted file mode 100755 index bc7b539f4..000000000 --- a/tools/run-qt5.14 +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -set -e - -export PYTHONWARNINGS=default -export PYTHONPYCACHEPREFIX=out/pycache -export ANKIDEV=${ANKIDEV-1} -export QTWEBENGINE_REMOTE_DEBUGGING=${QTWEBENGINE_REMOTE_DEBUGGING-8080} -export QTWEBENGINE_CHROMIUM_FLAGS=${QTWEBENGINE_CHROMIUM_FLAGS---remote-allow-origins=http://localhost:$QTWEBENGINE_REMOTE_DEBUGGING} - -./ninja pylib qt pyenv-qt5.14 -./out/pyenv-qt5.14/bin/python tools/run.py $* diff --git a/tools/run-qt5.15 b/tools/run-qt5.15 deleted file mode 100755 index 67a9b6f32..000000000 --- a/tools/run-qt5.15 +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -set -e - -export PYTHONWARNINGS=default -export PYTHONPYCACHEPREFIX=out/pycache -export ANKIDEV=${ANKIDEV-1} -export QTWEBENGINE_REMOTE_DEBUGGING=${QTWEBENGINE_REMOTE_DEBUGGING-8080} -export QTWEBENGINE_CHROMIUM_FLAGS=${QTWEBENGINE_CHROMIUM_FLAGS---remote-allow-origins=http://localhost:$QTWEBENGINE_REMOTE_DEBUGGING} -./ninja pylib qt pyenv-qt5.15 -./out/pyenv-qt5.15/bin/python tools/run.py $* diff --git a/tools/run-qt5.15.bat b/tools/run-qt5.15.bat deleted file mode 100755 index d17d5b534..000000000 --- a/tools/run-qt5.15.bat +++ /dev/null @@ -1,10 +0,0 @@ -@echo off -pushd "%~dp0\.." - -set PYTHONWARNINGS=default -set PYTHONPYCACHEPREFIX=out\pycache -set ANKIDEV=1 - -call tools\ninja pylib qt pyenv-qt5.15 || exit /b 1 -.\out\pyenv-qt5.15\scripts\python tools\run.py %* || exit /b 1 -popd diff --git a/tools/run-qt6.6 b/tools/run-qt6.6 new file mode 100755 index 000000000..dc0461229 --- /dev/null +++ b/tools/run-qt6.6 @@ -0,0 +1,9 @@ +#!/bin/bash + +set -e + +./ninja extract:uv + +export PYENV=./out/pyenv66 +UV_PROJECT_ENVIRONMENT=$PYENV ./out/extracted/uv/uv sync --all-packages --extra qt66 +./run $* diff --git a/tools/run-qt6.8 b/tools/run-qt6.8 index 9083f343e..1628a1e33 100755 --- a/tools/run-qt6.8 +++ b/tools/run-qt6.8 @@ -2,10 +2,8 @@ set -e -export PYTHONWARNINGS=default -export PYTHONPYCACHEPREFIX=out/pycache -export ANKIDEV=${ANKIDEV-1} -export QTWEBENGINE_REMOTE_DEBUGGING=${QTWEBENGINE_REMOTE_DEBUGGING-8080} -export QTWEBENGINE_CHROMIUM_FLAGS=${QTWEBENGINE_CHROMIUM_FLAGS---remote-allow-origins=http://localhost:$QTWEBENGINE_REMOTE_DEBUGGING} -./ninja pylib qt pyenv-qt6.8 -./out/pyenv-qt6.8/bin/python tools/run.py $* +./ninja extract:uv + +export PYENV=./out/pyenv68 +UV_PROJECT_ENVIRONMENT=$PYENV ./out/extracted/uv/uv sync --all-packages --extra qt68 +./run $* diff --git a/uv.lock b/uv.lock new file mode 100644 index 000000000..d735e0772 --- /dev/null +++ b/uv.lock @@ -0,0 +1,2198 @@ +version = 1 +revision = 2 +requires-python = ">=3.9" +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +conflicts = [[ + { package = "aqt", extra = "qt" }, + { package = "aqt", extra = "qt66" }, + { package = "aqt", extra = "qt67" }, +]] + +[manifest] +members = [ + "anki", + "anki-dev", + "aqt", +] + +[[package]] +name = "alabaster" +version = "0.7.16" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/3e/13dd8e5ed9094e734ac430b5d0eb4f2bb001708a8b7856cbf8e084e001ba/alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65", size = 23776, upload-time = "2024-01-10T00:56:10.189Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/34/d4e1c02d3bee589efb5dfa17f88ea08bdb3e3eac12bc475462aec52ed223/alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92", size = 13511, upload-time = "2024-01-10T00:56:08.388Z" }, +] + +[[package]] +name = "alabaster" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, +] + +[[package]] +name = "anki" +version = "0.1.2" +source = { editable = "pylib" } +dependencies = [ + { name = "beautifulsoup4" }, + { name = "decorator" }, + { name = "distro", marker = "(sys_platform != 'darwin' and sys_platform != 'win32') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "markdown" }, + { name = "orjson" }, + { name = "protobuf" }, + { name = "psutil", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "requests", extra = ["socks"] }, + { name = "types-orjson" }, + { name = "types-protobuf" }, + { name = "types-requests" }, + { name = "typing-extensions" }, +] + +[package.metadata] +requires-dist = [ + { name = "beautifulsoup4" }, + { name = "decorator" }, + { name = "distro", marker = "sys_platform != 'darwin' and sys_platform != 'win32'" }, + { name = "markdown" }, + { name = "orjson" }, + { name = "protobuf", specifier = ">=4.21" }, + { name = "psutil", marker = "sys_platform == 'win32'" }, + { name = "requests", extras = ["socks"] }, + { name = "types-orjson" }, + { name = "types-protobuf" }, + { name = "types-requests" }, + { name = "typing-extensions" }, +] + +[[package]] +name = "anki-audio" +version = "0.1.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/c7/b4c86d89c51d5bdcfc21bffc58be96b84075cff24b6d6fa0276a699084ff/anki_audio-0.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:249e3f7837366f8da3414139282f85df6fe65def2e1f76c2360ea88e03868f6b", size = 29453537, upload-time = "2025-06-13T10:48:32.337Z" }, + { url = "https://files.pythonhosted.org/packages/c8/38/af4dd671296cf68fb7b793d7f16845b074f5662f8e8653146ae950a149a0/anki_audio-0.1.0-cp39-abi3-macosx_11_0_x86_64.whl", hash = "sha256:a0b383880eaa8e27a028aa6ae50c4b95f6079044af5ec8a89ee870def21df9a5", size = 22586305, upload-time = "2025-06-13T10:48:45.557Z" }, + { url = "https://files.pythonhosted.org/packages/74/2b/5dd9b82faa27e04c9052232171de78ea4434dc384df859aa84e6dae8d468/anki_audio-0.1.0-py3-none-win_amd64.whl", hash = "sha256:b48b2537879769e03e9f4b87d7c37ef9d9fa2e5470e2116471d709666b615773", size = 31996219, upload-time = "2025-06-13T10:48:57.562Z" }, +] + +[[package]] +name = "anki-dev" +version = "0.0.0" +source = { virtual = "." } + +[package.optional-dependencies] +sphinx = [ + { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinx-autoapi" }, + { name = "sphinx-rtd-theme" }, +] + +[package.dev-dependencies] +dev = [ + { name = "black" }, + { name = "colorama" }, + { name = "hatchling" }, + { name = "isort" }, + { name = "mypy" }, + { name = "mypy-protobuf" }, + { name = "pychromedevtools" }, + { name = "pylint" }, + { name = "pytest" }, + { name = "wheel" }, +] + +[package.metadata] +requires-dist = [ + { name = "sphinx", marker = "extra == 'sphinx'" }, + { name = "sphinx-autoapi", marker = "extra == 'sphinx'" }, + { name = "sphinx-rtd-theme", marker = "extra == 'sphinx'" }, +] +provides-extras = ["sphinx"] + +[package.metadata.requires-dev] +dev = [ + { name = "black" }, + { name = "colorama" }, + { name = "hatchling" }, + { name = "isort" }, + { name = "mypy" }, + { name = "mypy-protobuf" }, + { name = "pychromedevtools" }, + { name = "pylint" }, + { name = "pytest" }, + { name = "wheel" }, +] + +[[package]] +name = "aqt" +version = "0.1.2" +source = { editable = "qt" } +dependencies = [ + { name = "beautifulsoup4" }, + { name = "flask" }, + { name = "flask-cors" }, + { name = "jsonschema" }, + { name = "mock" }, + { name = "pip-system-certs", version = "4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "pip-system-certs", version = "5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "psutil", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "pyqt6", version = "6.6.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, + { name = "pyqt6", version = "6.7.1", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra != 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, + { name = "pyqt6", version = "6.8.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt' or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt66' and extra != 'extra-3-aqt-qt67')" }, + { name = "pyqt6-webengine", version = "6.6.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, + { name = "pyqt6-webengine", version = "6.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra != 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, + { name = "pyqt6-webengine", version = "6.8.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt' or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt66' and extra != 'extra-3-aqt-qt67')" }, + { name = "pywin32", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "requests" }, + { name = "send2trash" }, + { name = "types-decorator" }, + { name = "types-flask" }, + { name = "types-flask-cors" }, + { name = "types-markdown" }, + { name = "types-pywin32" }, + { name = "types-waitress" }, + { name = "waitress" }, +] + +[package.optional-dependencies] +audio = [ + { name = "anki-audio", marker = "sys_platform == 'darwin' or sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, +] +qt = [ + { name = "pyqt6", version = "6.8.0", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-qt6", version = "6.8.1", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-sip", version = "13.10.2", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-webengine", version = "6.8.0", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-webengine-qt6", version = "6.8.1", source = { registry = "https://pypi.org/simple" } }, +] +qt66 = [ + { name = "pyqt6", version = "6.6.1", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-qt6", version = "6.6.2", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-sip", version = "13.6.0", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-webengine", version = "6.6.0", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-webengine-qt6", version = "6.6.2", source = { registry = "https://pypi.org/simple" } }, +] +qt67 = [ + { name = "pyqt6", version = "6.7.1", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-qt6", version = "6.7.3", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-sip", version = "13.10.2", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-webengine", version = "6.7.0", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-webengine-qt6", version = "6.7.3", source = { registry = "https://pypi.org/simple" } }, +] + +[package.metadata] +requires-dist = [ + { name = "anki-audio", marker = "(sys_platform == 'darwin' and extra == 'audio') or (sys_platform == 'win32' and extra == 'audio')", specifier = "==0.1.0" }, + { name = "beautifulsoup4" }, + { name = "flask" }, + { name = "flask-cors" }, + { name = "jsonschema" }, + { name = "mock" }, + { name = "pip-system-certs", specifier = "!=5.1" }, + { name = "psutil", marker = "sys_platform == 'win32'" }, + { name = "pyqt6", specifier = ">=6.2" }, + { name = "pyqt6", marker = "extra == 'qt'", specifier = "==6.8.0" }, + { name = "pyqt6", marker = "extra == 'qt66'", specifier = "==6.6.1" }, + { name = "pyqt6", marker = "extra == 'qt67'", specifier = "==6.7.1" }, + { name = "pyqt6-qt6", marker = "extra == 'qt'", specifier = "==6.8.1" }, + { name = "pyqt6-qt6", marker = "extra == 'qt66'", specifier = "==6.6.2" }, + { name = "pyqt6-qt6", marker = "extra == 'qt67'", specifier = "==6.7.3" }, + { name = "pyqt6-sip", marker = "extra == 'qt'", specifier = "==13.10.2" }, + { name = "pyqt6-sip", marker = "extra == 'qt66'", specifier = "==13.6.0" }, + { name = "pyqt6-sip", marker = "extra == 'qt67'", specifier = "==13.10.2" }, + { name = "pyqt6-webengine", specifier = ">=6.2" }, + { name = "pyqt6-webengine", marker = "extra == 'qt'", specifier = "==6.8.0" }, + { name = "pyqt6-webengine", marker = "extra == 'qt66'", specifier = "==6.6.0" }, + { name = "pyqt6-webengine", marker = "extra == 'qt67'", specifier = "==6.7.0" }, + { name = "pyqt6-webengine-qt6", marker = "extra == 'qt'", specifier = "==6.8.1" }, + { name = "pyqt6-webengine-qt6", marker = "extra == 'qt66'", specifier = "==6.6.2" }, + { name = "pyqt6-webengine-qt6", marker = "extra == 'qt67'", specifier = "==6.7.3" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "requests" }, + { name = "send2trash" }, + { name = "types-decorator" }, + { name = "types-flask" }, + { name = "types-flask-cors" }, + { name = "types-markdown" }, + { name = "types-pywin32" }, + { name = "types-waitress" }, + { name = "waitress", specifier = ">=2.0.0" }, +] +provides-extras = ["audio", "qt66", "qt67", "qt"] + +[[package]] +name = "astroid" +version = "3.3.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/c2/9b2de9ed027f9fe5734a6c0c0a601289d796b3caaf1e372e23fa88a73047/astroid-3.3.10.tar.gz", hash = "sha256:c332157953060c6deb9caa57303ae0d20b0fbdb2e59b4a4f2a6ba49d0a7961ce", size = 398941, upload-time = "2025-05-10T13:33:10.405Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/58/5260205b9968c20b6457ed82f48f9e3d6edf2f1f95103161798b73aeccf0/astroid-3.3.10-py3-none-any.whl", hash = "sha256:104fb9cb9b27ea95e847a94c003be03a9e039334a8ebca5ee27dafaf5c5711eb", size = 275388, upload-time = "2025-05-10T13:33:08.391Z" }, +] + +[[package]] +name = "attrs" +version = "25.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, +] + +[[package]] +name = "babel" +version = "2.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d8/e4/0c4c39e18fd76d6a628d4dd8da40543d136ce2d1752bd6eeeab0791f4d6b/beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195", size = 621067, upload-time = "2025-04-15T17:05:13.836Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/cd/30110dc0ffcf3b131156077b90e9f60ed75711223f306da4db08eff8403b/beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b", size = 187285, upload-time = "2025-04-15T17:05:12.221Z" }, +] + +[[package]] +name = "black" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449, upload-time = "2025-01-29T04:15:40.373Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/3b/4ba3f93ac8d90410423fdd31d7541ada9bcee1df32fb90d26de41ed40e1d/black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32", size = 1629419, upload-time = "2025-01-29T05:37:06.642Z" }, + { url = "https://files.pythonhosted.org/packages/b4/02/0bde0485146a8a5e694daed47561785e8b77a0466ccc1f3e485d5ef2925e/black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da", size = 1461080, upload-time = "2025-01-29T05:37:09.321Z" }, + { url = "https://files.pythonhosted.org/packages/52/0e/abdf75183c830eaca7589144ff96d49bce73d7ec6ad12ef62185cc0f79a2/black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7", size = 1766886, upload-time = "2025-01-29T04:18:24.432Z" }, + { url = "https://files.pythonhosted.org/packages/dc/a6/97d8bb65b1d8a41f8a6736222ba0a334db7b7b77b8023ab4568288f23973/black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9", size = 1419404, upload-time = "2025-01-29T04:19:04.296Z" }, + { url = "https://files.pythonhosted.org/packages/7e/4f/87f596aca05c3ce5b94b8663dbfe242a12843caaa82dd3f85f1ffdc3f177/black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0", size = 1614372, upload-time = "2025-01-29T05:37:11.71Z" }, + { url = "https://files.pythonhosted.org/packages/e7/d0/2c34c36190b741c59c901e56ab7f6e54dad8df05a6272a9747ecef7c6036/black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299", size = 1442865, upload-time = "2025-01-29T05:37:14.309Z" }, + { url = "https://files.pythonhosted.org/packages/21/d4/7518c72262468430ead45cf22bd86c883a6448b9eb43672765d69a8f1248/black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096", size = 1749699, upload-time = "2025-01-29T04:18:17.688Z" }, + { url = "https://files.pythonhosted.org/packages/58/db/4f5beb989b547f79096e035c4981ceb36ac2b552d0ac5f2620e941501c99/black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2", size = 1428028, upload-time = "2025-01-29T04:18:51.711Z" }, + { url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988, upload-time = "2025-01-29T05:37:16.707Z" }, + { url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985, upload-time = "2025-01-29T05:37:18.273Z" }, + { url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816, upload-time = "2025-01-29T04:18:33.823Z" }, + { url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860, upload-time = "2025-01-29T04:19:12.944Z" }, + { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673, upload-time = "2025-01-29T05:37:20.574Z" }, + { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190, upload-time = "2025-01-29T05:37:22.106Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926, upload-time = "2025-01-29T04:18:58.564Z" }, + { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613, upload-time = "2025-01-29T04:19:27.63Z" }, + { url = "https://files.pythonhosted.org/packages/d3/b6/ae7507470a4830dbbfe875c701e84a4a5fb9183d1497834871a715716a92/black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0", size = 1628593, upload-time = "2025-01-29T05:37:23.672Z" }, + { url = "https://files.pythonhosted.org/packages/24/c1/ae36fa59a59f9363017ed397750a0cd79a470490860bc7713967d89cdd31/black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f", size = 1460000, upload-time = "2025-01-29T05:37:25.829Z" }, + { url = "https://files.pythonhosted.org/packages/ac/b6/98f832e7a6c49aa3a464760c67c7856363aa644f2f3c74cf7d624168607e/black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e", size = 1765963, upload-time = "2025-01-29T04:18:38.116Z" }, + { url = "https://files.pythonhosted.org/packages/ce/e9/2cb0a017eb7024f70e0d2e9bdb8c5a5b078c5740c7f8816065d06f04c557/black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355", size = 1419419, upload-time = "2025-01-29T04:18:30.191Z" }, + { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" }, +] + +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, +] + +[[package]] +name = "certifi" +version = "2025.6.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/73/f7/f14b46d4bcd21092d7d3ccef689615220d8a08fb25e564b65d20738e672e/certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b", size = 158753, upload-time = "2025-06-15T02:45:51.329Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", size = 157650, upload-time = "2025-06-15T02:45:49.977Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" }, + { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" }, + { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" }, + { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" }, + { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" }, + { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" }, + { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" }, + { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" }, + { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" }, + { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" }, + { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, + { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, + { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, + { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, + { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, + { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, + { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, + { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, + { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, + { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, + { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, + { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, + { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, + { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, + { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, + { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, + { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, + { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, + { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, + { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, + { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/28/f8/dfb01ff6cc9af38552c69c9027501ff5a5117c4cc18dcd27cb5259fa1888/charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4", size = 201671, upload-time = "2025-05-02T08:34:12.696Z" }, + { url = "https://files.pythonhosted.org/packages/32/fb/74e26ee556a9dbfe3bd264289b67be1e6d616329403036f6507bb9f3f29c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7", size = 144744, upload-time = "2025-05-02T08:34:14.665Z" }, + { url = "https://files.pythonhosted.org/packages/ad/06/8499ee5aa7addc6f6d72e068691826ff093329fe59891e83b092ae4c851c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836", size = 154993, upload-time = "2025-05-02T08:34:17.134Z" }, + { url = "https://files.pythonhosted.org/packages/f1/a2/5e4c187680728219254ef107a6949c60ee0e9a916a5dadb148c7ae82459c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597", size = 147382, upload-time = "2025-05-02T08:34:19.081Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fe/56aca740dda674f0cc1ba1418c4d84534be51f639b5f98f538b332dc9a95/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7", size = 149536, upload-time = "2025-05-02T08:34:21.073Z" }, + { url = "https://files.pythonhosted.org/packages/53/13/db2e7779f892386b589173dd689c1b1e304621c5792046edd8a978cbf9e0/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f", size = 151349, upload-time = "2025-05-02T08:34:23.193Z" }, + { url = "https://files.pythonhosted.org/packages/69/35/e52ab9a276186f729bce7a0638585d2982f50402046e4b0faa5d2c3ef2da/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba", size = 146365, upload-time = "2025-05-02T08:34:25.187Z" }, + { url = "https://files.pythonhosted.org/packages/a6/d8/af7333f732fc2e7635867d56cb7c349c28c7094910c72267586947561b4b/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12", size = 154499, upload-time = "2025-05-02T08:34:27.359Z" }, + { url = "https://files.pythonhosted.org/packages/7a/3d/a5b2e48acef264d71e036ff30bcc49e51bde80219bb628ba3e00cf59baac/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518", size = 157735, upload-time = "2025-05-02T08:34:29.798Z" }, + { url = "https://files.pythonhosted.org/packages/85/d8/23e2c112532a29f3eef374375a8684a4f3b8e784f62b01da931186f43494/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5", size = 154786, upload-time = "2025-05-02T08:34:31.858Z" }, + { url = "https://files.pythonhosted.org/packages/c7/57/93e0169f08ecc20fe82d12254a200dfaceddc1c12a4077bf454ecc597e33/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3", size = 150203, upload-time = "2025-05-02T08:34:33.88Z" }, + { url = "https://files.pythonhosted.org/packages/2c/9d/9bf2b005138e7e060d7ebdec7503d0ef3240141587651f4b445bdf7286c2/charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471", size = 98436, upload-time = "2025-05-02T08:34:35.907Z" }, + { url = "https://files.pythonhosted.org/packages/6d/24/5849d46cf4311bbf21b424c443b09b459f5b436b1558c04e45dbb7cc478b/charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e", size = 105772, upload-time = "2025-05-02T08:34:37.935Z" }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "colorama", marker = "(python_full_version < '3.10' and sys_platform == 'win32') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, +] + +[[package]] +name = "click" +version = "8.2.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "colorama", marker = "(python_full_version >= '3.10' and sys_platform == 'win32') or (python_full_version < '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (python_full_version < '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (python_full_version < '3.10' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "decorator" +version = "5.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, +] + +[[package]] +name = "dill" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976, upload-time = "2025-04-16T00:41:48.867Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668, upload-time = "2025-04-16T00:41:47.671Z" }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, +] + +[[package]] +name = "docutils" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, +] + +[[package]] +name = "flask" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blinker" }, + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "itsdangerous" }, + { name = "jinja2" }, + { name = "markupsafe" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/de/e47735752347f4128bcf354e0da07ef311a78244eba9e3dc1d4a5ab21a98/flask-3.1.1.tar.gz", hash = "sha256:284c7b8f2f58cb737f0cf1c30fd7eaf0ccfcde196099d24ecede3fc2005aa59e", size = 753440, upload-time = "2025-05-13T15:01:17.447Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/68/9d4508e893976286d2ead7f8f571314af6c2037af34853a30fd769c02e9d/flask-3.1.1-py3-none-any.whl", hash = "sha256:07aae2bb5eaf77993ef57e357491839f5fd9f4dc281593a81a9e4d79a24f295c", size = 103305, upload-time = "2025-05-13T15:01:15.591Z" }, +] + +[[package]] +name = "flask-cors" +version = "6.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flask" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/37/bcfa6c7d5eec777c4c7cf45ce6b27631cebe5230caf88d85eadd63edd37a/flask_cors-6.0.1.tar.gz", hash = "sha256:d81bcb31f07b0985be7f48406247e9243aced229b7747219160a0559edd678db", size = 13463, upload-time = "2025-06-11T01:32:08.518Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/f8/01bf35a3afd734345528f98d0353f2a978a476528ad4d7e78b70c4d149dd/flask_cors-6.0.1-py3-none-any.whl", hash = "sha256:c7b2cbfb1a31aa0d2e5341eea03a6805349f7a61647daee1a15c46bbe981494c", size = 13244, upload-time = "2025-06-11T01:32:07.352Z" }, +] + +[[package]] +name = "hatchling" +version = "1.27.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "pathspec" }, + { name = "pluggy" }, + { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "trove-classifiers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/8a/cc1debe3514da292094f1c3a700e4ca25442489731ef7c0814358816bb03/hatchling-1.27.0.tar.gz", hash = "sha256:971c296d9819abb3811112fc52c7a9751c8d381898f36533bb16f9791e941fd6", size = 54983, upload-time = "2024-12-15T17:08:11.894Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/e7/ae38d7a6dfba0533684e0b2136817d667588ae3ec984c1a4e5df5eb88482/hatchling-1.27.0-py3-none-any.whl", hash = "sha256:d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b", size = 75794, upload-time = "2024-12-15T17:08:10.364Z" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026, upload-time = "2022-07-01T12:21:05.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769, upload-time = "2022-07-01T12:21:02.467Z" }, +] + +[[package]] +name = "importlib-metadata" +version = "8.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, +] + +[[package]] +name = "isort" +version = "6.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b8/21/1e2a441f74a653a144224d7d21afe8f4169e6c7c20bb13aec3a2dc3815e0/isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450", size = 821955, upload-time = "2025-02-26T21:13:16.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/11/114d0a5f4dabbdcedc1125dee0888514c3c3b16d3e9facad87ed96fad97c/isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615", size = 94186, upload-time = "2025-02-26T21:13:14.911Z" }, +] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.24.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/d3/1cf5326b923a53515d8f3a2cd442e6d7e94fcc444716e879ea70a0ce3177/jsonschema-4.24.0.tar.gz", hash = "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196", size = 353480, upload-time = "2025-05-26T18:48:10.459Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/3d/023389198f69c722d039351050738d6755376c8fd343e91dc493ea485905/jsonschema-4.24.0-py3-none-any.whl", hash = "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d", size = 88709, upload-time = "2025-05-26T18:48:08.417Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size = 15513, upload-time = "2025-04-23T12:34:07.418Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437, upload-time = "2025-04-23T12:34:05.422Z" }, +] + +[[package]] +name = "markdown" +version = "3.8.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/7c/0738e5ff0adccd0b4e02c66d0446c03a3c557e02bb49b7c263d7ab56c57d/markdown-3.8.1.tar.gz", hash = "sha256:a2e2f01cead4828ee74ecca9623045f62216aef2212a7685d6eb9163f590b8c1", size = 361280, upload-time = "2025-06-18T14:50:49.618Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/34/3d1ff0cb4843a33817d06800e9383a2b2a2df4d508e37f53a40e829905d9/markdown-3.8.1-py3-none-any.whl", hash = "sha256:46cc0c0f1e5211ab2e9d453582f0b28a1bfaf058a9f7d5c50386b99b588d8811", size = 106642, upload-time = "2025-06-18T14:50:48.52Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357, upload-time = "2024-10-18T15:20:51.44Z" }, + { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393, upload-time = "2024-10-18T15:20:52.426Z" }, + { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732, upload-time = "2024-10-18T15:20:53.578Z" }, + { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866, upload-time = "2024-10-18T15:20:55.06Z" }, + { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964, upload-time = "2024-10-18T15:20:55.906Z" }, + { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977, upload-time = "2024-10-18T15:20:57.189Z" }, + { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366, upload-time = "2024-10-18T15:20:58.235Z" }, + { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091, upload-time = "2024-10-18T15:20:59.235Z" }, + { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065, upload-time = "2024-10-18T15:21:00.307Z" }, + { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514, upload-time = "2024-10-18T15:21:01.122Z" }, + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353, upload-time = "2024-10-18T15:21:02.187Z" }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392, upload-time = "2024-10-18T15:21:02.941Z" }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984, upload-time = "2024-10-18T15:21:03.953Z" }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120, upload-time = "2024-10-18T15:21:06.495Z" }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032, upload-time = "2024-10-18T15:21:07.295Z" }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057, upload-time = "2024-10-18T15:21:08.073Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359, upload-time = "2024-10-18T15:21:09.318Z" }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306, upload-time = "2024-10-18T15:21:10.185Z" }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload-time = "2024-10-18T15:21:11.005Z" }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload-time = "2024-10-18T15:21:12.911Z" }, + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, + { url = "https://files.pythonhosted.org/packages/a7/ea/9b1530c3fdeeca613faeb0fb5cbcf2389d816072fab72a71b45749ef6062/MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", size = 14344, upload-time = "2024-10-18T15:21:43.721Z" }, + { url = "https://files.pythonhosted.org/packages/4b/c2/fbdbfe48848e7112ab05e627e718e854d20192b674952d9042ebd8c9e5de/MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", size = 12389, upload-time = "2024-10-18T15:21:44.666Z" }, + { url = "https://files.pythonhosted.org/packages/f0/25/7a7c6e4dbd4f867d95d94ca15449e91e52856f6ed1905d58ef1de5e211d0/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", size = 21607, upload-time = "2024-10-18T15:21:45.452Z" }, + { url = "https://files.pythonhosted.org/packages/53/8f/f339c98a178f3c1e545622206b40986a4c3307fe39f70ccd3d9df9a9e425/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", size = 20728, upload-time = "2024-10-18T15:21:46.295Z" }, + { url = "https://files.pythonhosted.org/packages/1a/03/8496a1a78308456dbd50b23a385c69b41f2e9661c67ea1329849a598a8f9/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", size = 20826, upload-time = "2024-10-18T15:21:47.134Z" }, + { url = "https://files.pythonhosted.org/packages/e6/cf/0a490a4bd363048c3022f2f475c8c05582179bb179defcee4766fb3dcc18/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", size = 21843, upload-time = "2024-10-18T15:21:48.334Z" }, + { url = "https://files.pythonhosted.org/packages/19/a3/34187a78613920dfd3cdf68ef6ce5e99c4f3417f035694074beb8848cd77/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", size = 21219, upload-time = "2024-10-18T15:21:49.587Z" }, + { url = "https://files.pythonhosted.org/packages/17/d8/5811082f85bb88410ad7e452263af048d685669bbbfb7b595e8689152498/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", size = 20946, upload-time = "2024-10-18T15:21:50.441Z" }, + { url = "https://files.pythonhosted.org/packages/7c/31/bd635fb5989440d9365c5e3c47556cfea121c7803f5034ac843e8f37c2f2/MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", size = 15063, upload-time = "2024-10-18T15:21:51.385Z" }, + { url = "https://files.pythonhosted.org/packages/b3/73/085399401383ce949f727afec55ec3abd76648d04b9f22e1c0e99cb4bec3/MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", size = 15506, upload-time = "2024-10-18T15:21:52.974Z" }, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, +] + +[[package]] +name = "mock" +version = "5.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/07/8c/14c2ae915e5f9dca5a22edd68b35be94400719ccfa068a03e0fb63d0f6f6/mock-5.2.0.tar.gz", hash = "sha256:4e460e818629b4b173f32d08bf30d3af8123afbb8e04bb5707a1fd4799e503f0", size = 92796, upload-time = "2025-03-03T12:31:42.911Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/d9/617e6af809bf3a1d468e0d58c3997b1dc219a9a9202e650d30c2fc85d481/mock-5.2.0-py3-none-any.whl", hash = "sha256:7ba87f72ca0e915175596069dbbcc7c75af7b5e9b9bc107ad6349ede0819982f", size = 31617, upload-time = "2025-03-03T12:31:41.518Z" }, +] + +[[package]] +name = "mypy" +version = "1.16.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/69/92c7fa98112e4d9eb075a239caa4ef4649ad7d441545ccffbd5e34607cbb/mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab", size = 3324747, upload-time = "2025-06-16T16:51:35.145Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/12/2bf23a80fcef5edb75de9a1e295d778e0f46ea89eb8b115818b663eff42b/mypy-1.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4f0fed1022a63c6fec38f28b7fc77fca47fd490445c69d0a66266c59dd0b88a", size = 10958644, upload-time = "2025-06-16T16:51:11.649Z" }, + { url = "https://files.pythonhosted.org/packages/08/50/bfe47b3b278eacf348291742fd5e6613bbc4b3434b72ce9361896417cfe5/mypy-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86042bbf9f5a05ea000d3203cf87aa9d0ccf9a01f73f71c58979eb9249f46d72", size = 10087033, upload-time = "2025-06-16T16:35:30.089Z" }, + { url = "https://files.pythonhosted.org/packages/21/de/40307c12fe25675a0776aaa2cdd2879cf30d99eec91b898de00228dc3ab5/mypy-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7469ee5902c95542bea7ee545f7006508c65c8c54b06dc2c92676ce526f3ea", size = 11875645, upload-time = "2025-06-16T16:35:48.49Z" }, + { url = "https://files.pythonhosted.org/packages/a6/d8/85bdb59e4a98b7a31495bd8f1a4445d8ffc86cde4ab1f8c11d247c11aedc/mypy-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:352025753ef6a83cb9e7f2427319bb7875d1fdda8439d1e23de12ab164179574", size = 12616986, upload-time = "2025-06-16T16:48:39.526Z" }, + { url = "https://files.pythonhosted.org/packages/0e/d0/bb25731158fa8f8ee9e068d3e94fcceb4971fedf1424248496292512afe9/mypy-1.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff9fa5b16e4c1364eb89a4d16bcda9987f05d39604e1e6c35378a2987c1aac2d", size = 12878632, upload-time = "2025-06-16T16:36:08.195Z" }, + { url = "https://files.pythonhosted.org/packages/2d/11/822a9beb7a2b825c0cb06132ca0a5183f8327a5e23ef89717c9474ba0bc6/mypy-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:1256688e284632382f8f3b9e2123df7d279f603c561f099758e66dd6ed4e8bd6", size = 9484391, upload-time = "2025-06-16T16:37:56.151Z" }, + { url = "https://files.pythonhosted.org/packages/9a/61/ec1245aa1c325cb7a6c0f8570a2eee3bfc40fa90d19b1267f8e50b5c8645/mypy-1.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:472e4e4c100062488ec643f6162dd0d5208e33e2f34544e1fc931372e806c0cc", size = 10890557, upload-time = "2025-06-16T16:37:21.421Z" }, + { url = "https://files.pythonhosted.org/packages/6b/bb/6eccc0ba0aa0c7a87df24e73f0ad34170514abd8162eb0c75fd7128171fb/mypy-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea16e2a7d2714277e349e24d19a782a663a34ed60864006e8585db08f8ad1782", size = 10012921, upload-time = "2025-06-16T16:51:28.659Z" }, + { url = "https://files.pythonhosted.org/packages/5f/80/b337a12e2006715f99f529e732c5f6a8c143bb58c92bb142d5ab380963a5/mypy-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08e850ea22adc4d8a4014651575567b0318ede51e8e9fe7a68f25391af699507", size = 11802887, upload-time = "2025-06-16T16:50:53.627Z" }, + { url = "https://files.pythonhosted.org/packages/d9/59/f7af072d09793d581a745a25737c7c0a945760036b16aeb620f658a017af/mypy-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22d76a63a42619bfb90122889b903519149879ddbf2ba4251834727944c8baca", size = 12531658, upload-time = "2025-06-16T16:33:55.002Z" }, + { url = "https://files.pythonhosted.org/packages/82/c4/607672f2d6c0254b94a646cfc45ad589dd71b04aa1f3d642b840f7cce06c/mypy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c7ce0662b6b9dc8f4ed86eb7a5d505ee3298c04b40ec13b30e572c0e5ae17c4", size = 12732486, upload-time = "2025-06-16T16:37:03.301Z" }, + { url = "https://files.pythonhosted.org/packages/b6/5e/136555ec1d80df877a707cebf9081bd3a9f397dedc1ab9750518d87489ec/mypy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:211287e98e05352a2e1d4e8759c5490925a7c784ddc84207f4714822f8cf99b6", size = 9479482, upload-time = "2025-06-16T16:47:37.48Z" }, + { url = "https://files.pythonhosted.org/packages/b4/d6/39482e5fcc724c15bf6280ff5806548c7185e0c090712a3736ed4d07e8b7/mypy-1.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:af4792433f09575d9eeca5c63d7d90ca4aeceda9d8355e136f80f8967639183d", size = 11066493, upload-time = "2025-06-16T16:47:01.683Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e5/26c347890efc6b757f4d5bb83f4a0cf5958b8cf49c938ac99b8b72b420a6/mypy-1.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66df38405fd8466ce3517eda1f6640611a0b8e70895e2a9462d1d4323c5eb4b9", size = 10081687, upload-time = "2025-06-16T16:48:19.367Z" }, + { url = "https://files.pythonhosted.org/packages/44/c7/b5cb264c97b86914487d6a24bd8688c0172e37ec0f43e93b9691cae9468b/mypy-1.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44e7acddb3c48bd2713994d098729494117803616e116032af192871aed80b79", size = 11839723, upload-time = "2025-06-16T16:49:20.912Z" }, + { url = "https://files.pythonhosted.org/packages/15/f8/491997a9b8a554204f834ed4816bda813aefda31cf873bb099deee3c9a99/mypy-1.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ab5eca37b50188163fa7c1b73c685ac66c4e9bdee4a85c9adac0e91d8895e15", size = 12722980, upload-time = "2025-06-16T16:37:40.929Z" }, + { url = "https://files.pythonhosted.org/packages/df/f0/2bd41e174b5fd93bc9de9a28e4fb673113633b8a7f3a607fa4a73595e468/mypy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb6229b2c9086247e21a83c309754b9058b438704ad2f6807f0d8227f6ebdd", size = 12903328, upload-time = "2025-06-16T16:34:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/61/81/5572108a7bec2c46b8aff7e9b524f371fe6ab5efb534d38d6b37b5490da8/mypy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:1f0435cf920e287ff68af3d10a118a73f212deb2ce087619eb4e648116d1fe9b", size = 9562321, upload-time = "2025-06-16T16:48:58.823Z" }, + { url = "https://files.pythonhosted.org/packages/28/e3/96964af4a75a949e67df4b95318fe2b7427ac8189bbc3ef28f92a1c5bc56/mypy-1.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddc91eb318c8751c69ddb200a5937f1232ee8efb4e64e9f4bc475a33719de438", size = 11063480, upload-time = "2025-06-16T16:47:56.205Z" }, + { url = "https://files.pythonhosted.org/packages/f5/4d/cd1a42b8e5be278fab7010fb289d9307a63e07153f0ae1510a3d7b703193/mypy-1.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87ff2c13d58bdc4bbe7dc0dedfe622c0f04e2cb2a492269f3b418df2de05c536", size = 10090538, upload-time = "2025-06-16T16:46:43.92Z" }, + { url = "https://files.pythonhosted.org/packages/c9/4f/c3c6b4b66374b5f68bab07c8cabd63a049ff69796b844bc759a0ca99bb2a/mypy-1.16.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a7cfb0fe29fe5a9841b7c8ee6dffb52382c45acdf68f032145b75620acfbd6f", size = 11836839, upload-time = "2025-06-16T16:36:28.039Z" }, + { url = "https://files.pythonhosted.org/packages/b4/7e/81ca3b074021ad9775e5cb97ebe0089c0f13684b066a750b7dc208438403/mypy-1.16.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051e1677689c9d9578b9c7f4d206d763f9bbd95723cd1416fad50db49d52f359", size = 12715634, upload-time = "2025-06-16T16:50:34.441Z" }, + { url = "https://files.pythonhosted.org/packages/e9/95/bdd40c8be346fa4c70edb4081d727a54d0a05382d84966869738cfa8a497/mypy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5d2309511cc56c021b4b4e462907c2b12f669b2dbeb68300110ec27723971be", size = 12895584, upload-time = "2025-06-16T16:34:54.857Z" }, + { url = "https://files.pythonhosted.org/packages/5a/fd/d486a0827a1c597b3b48b1bdef47228a6e9ee8102ab8c28f944cb83b65dc/mypy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f58ac32771341e38a853c5d0ec0dfe27e18e27da9cdb8bbc882d2249c71a3ee", size = 9573886, upload-time = "2025-06-16T16:36:43.589Z" }, + { url = "https://files.pythonhosted.org/packages/49/5e/ed1e6a7344005df11dfd58b0fdd59ce939a0ba9f7ed37754bf20670b74db/mypy-1.16.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7fc688329af6a287567f45cc1cefb9db662defeb14625213a5b7da6e692e2069", size = 10959511, upload-time = "2025-06-16T16:47:21.945Z" }, + { url = "https://files.pythonhosted.org/packages/30/88/a7cbc2541e91fe04f43d9e4577264b260fecedb9bccb64ffb1a34b7e6c22/mypy-1.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e198ab3f55924c03ead626ff424cad1732d0d391478dfbf7bb97b34602395da", size = 10075555, upload-time = "2025-06-16T16:50:14.084Z" }, + { url = "https://files.pythonhosted.org/packages/93/f7/c62b1e31a32fbd1546cca5e0a2e5f181be5761265ad1f2e94f2a306fa906/mypy-1.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09aa4f91ada245f0a45dbc47e548fd94e0dd5a8433e0114917dc3b526912a30c", size = 11874169, upload-time = "2025-06-16T16:49:42.276Z" }, + { url = "https://files.pythonhosted.org/packages/c8/15/db580a28034657fb6cb87af2f8996435a5b19d429ea4dcd6e1c73d418e60/mypy-1.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13c7cd5b1cb2909aa318a90fd1b7e31f17c50b242953e7dd58345b2a814f6383", size = 12610060, upload-time = "2025-06-16T16:34:15.215Z" }, + { url = "https://files.pythonhosted.org/packages/ec/78/c17f48f6843048fa92d1489d3095e99324f2a8c420f831a04ccc454e2e51/mypy-1.16.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:58e07fb958bc5d752a280da0e890c538f1515b79a65757bbdc54252ba82e0b40", size = 12875199, upload-time = "2025-06-16T16:35:14.448Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d6/ed42167d0a42680381653fd251d877382351e1bd2c6dd8a818764be3beb1/mypy-1.16.1-cp39-cp39-win_amd64.whl", hash = "sha256:f895078594d918f93337a505f8add9bd654d1a24962b4c6ed9390e12531eb31b", size = 9487033, upload-time = "2025-06-16T16:49:57.907Z" }, + { url = "https://files.pythonhosted.org/packages/cf/d3/53e684e78e07c1a2bf7105715e5edd09ce951fc3f47cf9ed095ec1b7a037/mypy-1.16.1-py3-none-any.whl", hash = "sha256:5fc2ac4027d0ef28d6ba69a0343737a23c4d1b83672bf38d1fe237bdc0643b37", size = 2265923, upload-time = "2025-06-16T16:48:02.366Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "mypy-protobuf" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, + { name = "types-protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/6f/282d64d66bf48ce60e38a6560753f784e0f88ab245ac2fb5e93f701a36cd/mypy-protobuf-3.6.0.tar.gz", hash = "sha256:02f242eb3409f66889f2b1a3aa58356ec4d909cdd0f93115622e9e70366eca3c", size = 24445, upload-time = "2024-04-01T20:24:42.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/73/d6b999782ae22f16971cc05378b3b33f6a89ede3b9619e8366aa23484bca/mypy_protobuf-3.6.0-py3-none-any.whl", hash = "sha256:56176e4d569070e7350ea620262478b49b7efceba4103d468448f1d21492fd6c", size = 16434, upload-time = "2024-04-01T20:24:40.583Z" }, +] + +[[package]] +name = "orjson" +version = "3.10.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/0b/fea456a3ffe74e70ba30e01ec183a9b26bec4d497f61dcfce1b601059c60/orjson-3.10.18.tar.gz", hash = "sha256:e8da3947d92123eda795b68228cafe2724815621fe35e8e320a9e9593a4bcd53", size = 5422810, upload-time = "2025-04-29T23:30:08.423Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/16/2ceb9fb7bc2b11b1e4a3ea27794256e93dee2309ebe297fd131a778cd150/orjson-3.10.18-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a45e5d68066b408e4bc383b6e4ef05e717c65219a9e1390abc6155a520cac402", size = 248927, upload-time = "2025-04-29T23:28:08.643Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e1/d3c0a2bba5b9906badd121da449295062b289236c39c3a7801f92c4682b0/orjson-3.10.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be3b9b143e8b9db05368b13b04c84d37544ec85bb97237b3a923f076265ec89c", size = 136995, upload-time = "2025-04-29T23:28:11.503Z" }, + { url = "https://files.pythonhosted.org/packages/d7/51/698dd65e94f153ee5ecb2586c89702c9e9d12f165a63e74eb9ea1299f4e1/orjson-3.10.18-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9b0aa09745e2c9b3bf779b096fa71d1cc2d801a604ef6dd79c8b1bfef52b2f92", size = 132893, upload-time = "2025-04-29T23:28:12.751Z" }, + { url = "https://files.pythonhosted.org/packages/b3/e5/155ce5a2c43a85e790fcf8b985400138ce5369f24ee6770378ee6b691036/orjson-3.10.18-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53a245c104d2792e65c8d225158f2b8262749ffe64bc7755b00024757d957a13", size = 137017, upload-time = "2025-04-29T23:28:14.498Z" }, + { url = "https://files.pythonhosted.org/packages/46/bb/6141ec3beac3125c0b07375aee01b5124989907d61c72c7636136e4bd03e/orjson-3.10.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9495ab2611b7f8a0a8a505bcb0f0cbdb5469caafe17b0e404c3c746f9900469", size = 138290, upload-time = "2025-04-29T23:28:16.211Z" }, + { url = "https://files.pythonhosted.org/packages/77/36/6961eca0b66b7809d33c4ca58c6bd4c23a1b914fb23aba2fa2883f791434/orjson-3.10.18-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73be1cbcebadeabdbc468f82b087df435843c809cd079a565fb16f0f3b23238f", size = 142828, upload-time = "2025-04-29T23:28:18.065Z" }, + { url = "https://files.pythonhosted.org/packages/8b/2f/0c646d5fd689d3be94f4d83fa9435a6c4322c9b8533edbb3cd4bc8c5f69a/orjson-3.10.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8936ee2679e38903df158037a2f1c108129dee218975122e37847fb1d4ac68", size = 132806, upload-time = "2025-04-29T23:28:19.782Z" }, + { url = "https://files.pythonhosted.org/packages/ea/af/65907b40c74ef4c3674ef2bcfa311c695eb934710459841b3c2da212215c/orjson-3.10.18-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7115fcbc8525c74e4c2b608129bef740198e9a120ae46184dac7683191042056", size = 135005, upload-time = "2025-04-29T23:28:21.367Z" }, + { url = "https://files.pythonhosted.org/packages/c7/d1/68bd20ac6a32cd1f1b10d23e7cc58ee1e730e80624e3031d77067d7150fc/orjson-3.10.18-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:771474ad34c66bc4d1c01f645f150048030694ea5b2709b87d3bda273ffe505d", size = 413418, upload-time = "2025-04-29T23:28:23.097Z" }, + { url = "https://files.pythonhosted.org/packages/31/31/c701ec0bcc3e80e5cb6e319c628ef7b768aaa24b0f3b4c599df2eaacfa24/orjson-3.10.18-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c14047dbbea52886dd87169f21939af5d55143dad22d10db6a7514f058156a8", size = 153288, upload-time = "2025-04-29T23:28:25.02Z" }, + { url = "https://files.pythonhosted.org/packages/d9/31/5e1aa99a10893a43cfc58009f9da840990cc8a9ebb75aa452210ba18587e/orjson-3.10.18-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:641481b73baec8db14fdf58f8967e52dc8bda1f2aba3aa5f5c1b07ed6df50b7f", size = 137181, upload-time = "2025-04-29T23:28:26.318Z" }, + { url = "https://files.pythonhosted.org/packages/bf/8c/daba0ac1b8690011d9242a0f37235f7d17df6d0ad941021048523b76674e/orjson-3.10.18-cp310-cp310-win32.whl", hash = "sha256:607eb3ae0909d47280c1fc657c4284c34b785bae371d007595633f4b1a2bbe06", size = 142694, upload-time = "2025-04-29T23:28:28.092Z" }, + { url = "https://files.pythonhosted.org/packages/16/62/8b687724143286b63e1d0fab3ad4214d54566d80b0ba9d67c26aaf28a2f8/orjson-3.10.18-cp310-cp310-win_amd64.whl", hash = "sha256:8770432524ce0eca50b7efc2a9a5f486ee0113a5fbb4231526d414e6254eba92", size = 134600, upload-time = "2025-04-29T23:28:29.422Z" }, + { url = "https://files.pythonhosted.org/packages/97/c7/c54a948ce9a4278794f669a353551ce7db4ffb656c69a6e1f2264d563e50/orjson-3.10.18-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e0a183ac3b8e40471e8d843105da6fbe7c070faab023be3b08188ee3f85719b8", size = 248929, upload-time = "2025-04-29T23:28:30.716Z" }, + { url = "https://files.pythonhosted.org/packages/9e/60/a9c674ef1dd8ab22b5b10f9300e7e70444d4e3cda4b8258d6c2488c32143/orjson-3.10.18-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5ef7c164d9174362f85238d0cd4afdeeb89d9e523e4651add6a5d458d6f7d42d", size = 133364, upload-time = "2025-04-29T23:28:32.392Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4e/f7d1bdd983082216e414e6d7ef897b0c2957f99c545826c06f371d52337e/orjson-3.10.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd14c5d99cdc7bf93f22b12ec3b294931518aa019e2a147e8aa2f31fd3240f7", size = 136995, upload-time = "2025-04-29T23:28:34.024Z" }, + { url = "https://files.pythonhosted.org/packages/17/89/46b9181ba0ea251c9243b0c8ce29ff7c9796fa943806a9c8b02592fce8ea/orjson-3.10.18-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b672502323b6cd133c4af6b79e3bea36bad2d16bca6c1f645903fce83909a7a", size = 132894, upload-time = "2025-04-29T23:28:35.318Z" }, + { url = "https://files.pythonhosted.org/packages/ca/dd/7bce6fcc5b8c21aef59ba3c67f2166f0a1a9b0317dcca4a9d5bd7934ecfd/orjson-3.10.18-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51f8c63be6e070ec894c629186b1c0fe798662b8687f3d9fdfa5e401c6bd7679", size = 137016, upload-time = "2025-04-29T23:28:36.674Z" }, + { url = "https://files.pythonhosted.org/packages/1c/4a/b8aea1c83af805dcd31c1f03c95aabb3e19a016b2a4645dd822c5686e94d/orjson-3.10.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f9478ade5313d724e0495d167083c6f3be0dd2f1c9c8a38db9a9e912cdaf947", size = 138290, upload-time = "2025-04-29T23:28:38.3Z" }, + { url = "https://files.pythonhosted.org/packages/36/d6/7eb05c85d987b688707f45dcf83c91abc2251e0dd9fb4f7be96514f838b1/orjson-3.10.18-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:187aefa562300a9d382b4b4eb9694806e5848b0cedf52037bb5c228c61bb66d4", size = 142829, upload-time = "2025-04-29T23:28:39.657Z" }, + { url = "https://files.pythonhosted.org/packages/d2/78/ddd3ee7873f2b5f90f016bc04062713d567435c53ecc8783aab3a4d34915/orjson-3.10.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da552683bc9da222379c7a01779bddd0ad39dd699dd6300abaf43eadee38334", size = 132805, upload-time = "2025-04-29T23:28:40.969Z" }, + { url = "https://files.pythonhosted.org/packages/8c/09/c8e047f73d2c5d21ead9c180203e111cddeffc0848d5f0f974e346e21c8e/orjson-3.10.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e450885f7b47a0231979d9c49b567ed1c4e9f69240804621be87c40bc9d3cf17", size = 135008, upload-time = "2025-04-29T23:28:42.284Z" }, + { url = "https://files.pythonhosted.org/packages/0c/4b/dccbf5055ef8fb6eda542ab271955fc1f9bf0b941a058490293f8811122b/orjson-3.10.18-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5e3c9cc2ba324187cd06287ca24f65528f16dfc80add48dc99fa6c836bb3137e", size = 413419, upload-time = "2025-04-29T23:28:43.673Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f3/1eac0c5e2d6d6790bd2025ebfbefcbd37f0d097103d76f9b3f9302af5a17/orjson-3.10.18-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:50ce016233ac4bfd843ac5471e232b865271d7d9d44cf9d33773bcd883ce442b", size = 153292, upload-time = "2025-04-29T23:28:45.573Z" }, + { url = "https://files.pythonhosted.org/packages/1f/b4/ef0abf64c8f1fabf98791819ab502c2c8c1dc48b786646533a93637d8999/orjson-3.10.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b3ceff74a8f7ffde0b2785ca749fc4e80e4315c0fd887561144059fb1c138aa7", size = 137182, upload-time = "2025-04-29T23:28:47.229Z" }, + { url = "https://files.pythonhosted.org/packages/a9/a3/6ea878e7b4a0dc5c888d0370d7752dcb23f402747d10e2257478d69b5e63/orjson-3.10.18-cp311-cp311-win32.whl", hash = "sha256:fdba703c722bd868c04702cac4cb8c6b8ff137af2623bc0ddb3b3e6a2c8996c1", size = 142695, upload-time = "2025-04-29T23:28:48.564Z" }, + { url = "https://files.pythonhosted.org/packages/79/2a/4048700a3233d562f0e90d5572a849baa18ae4e5ce4c3ba6247e4ece57b0/orjson-3.10.18-cp311-cp311-win_amd64.whl", hash = "sha256:c28082933c71ff4bc6ccc82a454a2bffcef6e1d7379756ca567c772e4fb3278a", size = 134603, upload-time = "2025-04-29T23:28:50.442Z" }, + { url = "https://files.pythonhosted.org/packages/03/45/10d934535a4993d27e1c84f1810e79ccf8b1b7418cef12151a22fe9bb1e1/orjson-3.10.18-cp311-cp311-win_arm64.whl", hash = "sha256:a6c7c391beaedd3fa63206e5c2b7b554196f14debf1ec9deb54b5d279b1b46f5", size = 131400, upload-time = "2025-04-29T23:28:51.838Z" }, + { url = "https://files.pythonhosted.org/packages/21/1a/67236da0916c1a192d5f4ccbe10ec495367a726996ceb7614eaa687112f2/orjson-3.10.18-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:50c15557afb7f6d63bc6d6348e0337a880a04eaa9cd7c9d569bcb4e760a24753", size = 249184, upload-time = "2025-04-29T23:28:53.612Z" }, + { url = "https://files.pythonhosted.org/packages/b3/bc/c7f1db3b1d094dc0c6c83ed16b161a16c214aaa77f311118a93f647b32dc/orjson-3.10.18-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:356b076f1662c9813d5fa56db7d63ccceef4c271b1fb3dd522aca291375fcf17", size = 133279, upload-time = "2025-04-29T23:28:55.055Z" }, + { url = "https://files.pythonhosted.org/packages/af/84/664657cd14cc11f0d81e80e64766c7ba5c9b7fc1ec304117878cc1b4659c/orjson-3.10.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:559eb40a70a7494cd5beab2d73657262a74a2c59aff2068fdba8f0424ec5b39d", size = 136799, upload-time = "2025-04-29T23:28:56.828Z" }, + { url = "https://files.pythonhosted.org/packages/9a/bb/f50039c5bb05a7ab024ed43ba25d0319e8722a0ac3babb0807e543349978/orjson-3.10.18-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3c29eb9a81e2fbc6fd7ddcfba3e101ba92eaff455b8d602bf7511088bbc0eae", size = 132791, upload-time = "2025-04-29T23:28:58.751Z" }, + { url = "https://files.pythonhosted.org/packages/93/8c/ee74709fc072c3ee219784173ddfe46f699598a1723d9d49cbc78d66df65/orjson-3.10.18-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6612787e5b0756a171c7d81ba245ef63a3533a637c335aa7fcb8e665f4a0966f", size = 137059, upload-time = "2025-04-29T23:29:00.129Z" }, + { url = "https://files.pythonhosted.org/packages/6a/37/e6d3109ee004296c80426b5a62b47bcadd96a3deab7443e56507823588c5/orjson-3.10.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ac6bd7be0dcab5b702c9d43d25e70eb456dfd2e119d512447468f6405b4a69c", size = 138359, upload-time = "2025-04-29T23:29:01.704Z" }, + { url = "https://files.pythonhosted.org/packages/4f/5d/387dafae0e4691857c62bd02839a3bf3fa648eebd26185adfac58d09f207/orjson-3.10.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9f72f100cee8dde70100406d5c1abba515a7df926d4ed81e20a9730c062fe9ad", size = 142853, upload-time = "2025-04-29T23:29:03.576Z" }, + { url = "https://files.pythonhosted.org/packages/27/6f/875e8e282105350b9a5341c0222a13419758545ae32ad6e0fcf5f64d76aa/orjson-3.10.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dca85398d6d093dd41dc0983cbf54ab8e6afd1c547b6b8a311643917fbf4e0c", size = 133131, upload-time = "2025-04-29T23:29:05.753Z" }, + { url = "https://files.pythonhosted.org/packages/48/b2/73a1f0b4790dcb1e5a45f058f4f5dcadc8a85d90137b50d6bbc6afd0ae50/orjson-3.10.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22748de2a07fcc8781a70edb887abf801bb6142e6236123ff93d12d92db3d406", size = 134834, upload-time = "2025-04-29T23:29:07.35Z" }, + { url = "https://files.pythonhosted.org/packages/56/f5/7ed133a5525add9c14dbdf17d011dd82206ca6840811d32ac52a35935d19/orjson-3.10.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3a83c9954a4107b9acd10291b7f12a6b29e35e8d43a414799906ea10e75438e6", size = 413368, upload-time = "2025-04-29T23:29:09.301Z" }, + { url = "https://files.pythonhosted.org/packages/11/7c/439654221ed9c3324bbac7bdf94cf06a971206b7b62327f11a52544e4982/orjson-3.10.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:303565c67a6c7b1f194c94632a4a39918e067bd6176a48bec697393865ce4f06", size = 153359, upload-time = "2025-04-29T23:29:10.813Z" }, + { url = "https://files.pythonhosted.org/packages/48/e7/d58074fa0cc9dd29a8fa2a6c8d5deebdfd82c6cfef72b0e4277c4017563a/orjson-3.10.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:86314fdb5053a2f5a5d881f03fca0219bfdf832912aa88d18676a5175c6916b5", size = 137466, upload-time = "2025-04-29T23:29:12.26Z" }, + { url = "https://files.pythonhosted.org/packages/57/4d/fe17581cf81fb70dfcef44e966aa4003360e4194d15a3f38cbffe873333a/orjson-3.10.18-cp312-cp312-win32.whl", hash = "sha256:187ec33bbec58c76dbd4066340067d9ece6e10067bb0cc074a21ae3300caa84e", size = 142683, upload-time = "2025-04-29T23:29:13.865Z" }, + { url = "https://files.pythonhosted.org/packages/e6/22/469f62d25ab5f0f3aee256ea732e72dc3aab6d73bac777bd6277955bceef/orjson-3.10.18-cp312-cp312-win_amd64.whl", hash = "sha256:f9f94cf6d3f9cd720d641f8399e390e7411487e493962213390d1ae45c7814fc", size = 134754, upload-time = "2025-04-29T23:29:15.338Z" }, + { url = "https://files.pythonhosted.org/packages/10/b0/1040c447fac5b91bc1e9c004b69ee50abb0c1ffd0d24406e1350c58a7fcb/orjson-3.10.18-cp312-cp312-win_arm64.whl", hash = "sha256:3d600be83fe4514944500fa8c2a0a77099025ec6482e8087d7659e891f23058a", size = 131218, upload-time = "2025-04-29T23:29:17.324Z" }, + { url = "https://files.pythonhosted.org/packages/04/f0/8aedb6574b68096f3be8f74c0b56d36fd94bcf47e6c7ed47a7bd1474aaa8/orjson-3.10.18-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:69c34b9441b863175cc6a01f2935de994025e773f814412030f269da4f7be147", size = 249087, upload-time = "2025-04-29T23:29:19.083Z" }, + { url = "https://files.pythonhosted.org/packages/bc/f7/7118f965541aeac6844fcb18d6988e111ac0d349c9b80cda53583e758908/orjson-3.10.18-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:1ebeda919725f9dbdb269f59bc94f861afbe2a27dce5608cdba2d92772364d1c", size = 133273, upload-time = "2025-04-29T23:29:20.602Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d9/839637cc06eaf528dd8127b36004247bf56e064501f68df9ee6fd56a88ee/orjson-3.10.18-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5adf5f4eed520a4959d29ea80192fa626ab9a20b2ea13f8f6dc58644f6927103", size = 136779, upload-time = "2025-04-29T23:29:22.062Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6d/f226ecfef31a1f0e7d6bf9a31a0bbaf384c7cbe3fce49cc9c2acc51f902a/orjson-3.10.18-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7592bb48a214e18cd670974f289520f12b7aed1fa0b2e2616b8ed9e069e08595", size = 132811, upload-time = "2025-04-29T23:29:23.602Z" }, + { url = "https://files.pythonhosted.org/packages/73/2d/371513d04143c85b681cf8f3bce743656eb5b640cb1f461dad750ac4b4d4/orjson-3.10.18-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f872bef9f042734110642b7a11937440797ace8c87527de25e0c53558b579ccc", size = 137018, upload-time = "2025-04-29T23:29:25.094Z" }, + { url = "https://files.pythonhosted.org/packages/69/cb/a4d37a30507b7a59bdc484e4a3253c8141bf756d4e13fcc1da760a0b00cb/orjson-3.10.18-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0315317601149c244cb3ecef246ef5861a64824ccbcb8018d32c66a60a84ffbc", size = 138368, upload-time = "2025-04-29T23:29:26.609Z" }, + { url = "https://files.pythonhosted.org/packages/1e/ae/cd10883c48d912d216d541eb3db8b2433415fde67f620afe6f311f5cd2ca/orjson-3.10.18-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0da26957e77e9e55a6c2ce2e7182a36a6f6b180ab7189315cb0995ec362e049", size = 142840, upload-time = "2025-04-29T23:29:28.153Z" }, + { url = "https://files.pythonhosted.org/packages/6d/4c/2bda09855c6b5f2c055034c9eda1529967b042ff8d81a05005115c4e6772/orjson-3.10.18-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb70d489bc79b7519e5803e2cc4c72343c9dc1154258adf2f8925d0b60da7c58", size = 133135, upload-time = "2025-04-29T23:29:29.726Z" }, + { url = "https://files.pythonhosted.org/packages/13/4a/35971fd809a8896731930a80dfff0b8ff48eeb5d8b57bb4d0d525160017f/orjson-3.10.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9e86a6af31b92299b00736c89caf63816f70a4001e750bda179e15564d7a034", size = 134810, upload-time = "2025-04-29T23:29:31.269Z" }, + { url = "https://files.pythonhosted.org/packages/99/70/0fa9e6310cda98365629182486ff37a1c6578e34c33992df271a476ea1cd/orjson-3.10.18-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:c382a5c0b5931a5fc5405053d36c1ce3fd561694738626c77ae0b1dfc0242ca1", size = 413491, upload-time = "2025-04-29T23:29:33.315Z" }, + { url = "https://files.pythonhosted.org/packages/32/cb/990a0e88498babddb74fb97855ae4fbd22a82960e9b06eab5775cac435da/orjson-3.10.18-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8e4b2ae732431127171b875cb2668f883e1234711d3c147ffd69fe5be51a8012", size = 153277, upload-time = "2025-04-29T23:29:34.946Z" }, + { url = "https://files.pythonhosted.org/packages/92/44/473248c3305bf782a384ed50dd8bc2d3cde1543d107138fd99b707480ca1/orjson-3.10.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d808e34ddb24fc29a4d4041dcfafbae13e129c93509b847b14432717d94b44f", size = 137367, upload-time = "2025-04-29T23:29:36.52Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fd/7f1d3edd4ffcd944a6a40e9f88af2197b619c931ac4d3cfba4798d4d3815/orjson-3.10.18-cp313-cp313-win32.whl", hash = "sha256:ad8eacbb5d904d5591f27dee4031e2c1db43d559edb8f91778efd642d70e6bea", size = 142687, upload-time = "2025-04-29T23:29:38.292Z" }, + { url = "https://files.pythonhosted.org/packages/4b/03/c75c6ad46be41c16f4cfe0352a2d1450546f3c09ad2c9d341110cd87b025/orjson-3.10.18-cp313-cp313-win_amd64.whl", hash = "sha256:aed411bcb68bf62e85588f2a7e03a6082cc42e5a2796e06e72a962d7c6310b52", size = 134794, upload-time = "2025-04-29T23:29:40.349Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/f53038a5a72cc4fd0b56c1eafb4ef64aec9685460d5ac34de98ca78b6e29/orjson-3.10.18-cp313-cp313-win_arm64.whl", hash = "sha256:f54c1385a0e6aba2f15a40d703b858bedad36ded0491e55d35d905b2c34a4cc3", size = 131186, upload-time = "2025-04-29T23:29:41.922Z" }, + { url = "https://files.pythonhosted.org/packages/df/db/69488acaa2316788b7e171f024912c6fe8193aa2e24e9cfc7bc41c3669ba/orjson-3.10.18-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c95fae14225edfd699454e84f61c3dd938df6629a00c6ce15e704f57b58433bb", size = 249301, upload-time = "2025-04-29T23:29:44.719Z" }, + { url = "https://files.pythonhosted.org/packages/23/21/d816c44ec5d1482c654e1d23517d935bb2716e1453ff9380e861dc6efdd3/orjson-3.10.18-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5232d85f177f98e0cefabb48b5e7f60cff6f3f0365f9c60631fecd73849b2a82", size = 136786, upload-time = "2025-04-29T23:29:46.517Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9f/f68d8a9985b717e39ba7bf95b57ba173fcd86aeca843229ec60d38f1faa7/orjson-3.10.18-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2783e121cafedf0d85c148c248a20470018b4ffd34494a68e125e7d5857655d1", size = 132711, upload-time = "2025-04-29T23:29:48.605Z" }, + { url = "https://files.pythonhosted.org/packages/b5/63/447f5955439bf7b99bdd67c38a3f689d140d998ac58e3b7d57340520343c/orjson-3.10.18-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e54ee3722caf3db09c91f442441e78f916046aa58d16b93af8a91500b7bbf273", size = 136841, upload-time = "2025-04-29T23:29:50.31Z" }, + { url = "https://files.pythonhosted.org/packages/68/9e/4855972f2be74097242e4681ab6766d36638a079e09d66f3d6a5d1188ce7/orjson-3.10.18-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2daf7e5379b61380808c24f6fc182b7719301739e4271c3ec88f2984a2d61f89", size = 138082, upload-time = "2025-04-29T23:29:51.992Z" }, + { url = "https://files.pythonhosted.org/packages/08/0f/e68431e53a39698d2355faf1f018c60a3019b4b54b4ea6be9dc6b8208a3d/orjson-3.10.18-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f39b371af3add20b25338f4b29a8d6e79a8c7ed0e9dd49e008228a065d07781", size = 142618, upload-time = "2025-04-29T23:29:53.642Z" }, + { url = "https://files.pythonhosted.org/packages/32/da/bdcfff239ddba1b6ef465efe49d7e43cc8c30041522feba9fd4241d47c32/orjson-3.10.18-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b819ed34c01d88c6bec290e6842966f8e9ff84b7694632e88341363440d4cc0", size = 132627, upload-time = "2025-04-29T23:29:55.318Z" }, + { url = "https://files.pythonhosted.org/packages/0c/28/bc634da09bbe972328f615b0961f1e7d91acb3cc68bddbca9e8dd64e8e24/orjson-3.10.18-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2f6c57debaef0b1aa13092822cbd3698a1fb0209a9ea013a969f4efa36bdea57", size = 134832, upload-time = "2025-04-29T23:29:56.985Z" }, + { url = "https://files.pythonhosted.org/packages/1d/d2/e8ac0c2d0ec782ed8925b4eb33f040cee1f1fbd1d8b268aeb84b94153e49/orjson-3.10.18-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:755b6d61ffdb1ffa1e768330190132e21343757c9aa2308c67257cc81a1a6f5a", size = 413161, upload-time = "2025-04-29T23:29:59.148Z" }, + { url = "https://files.pythonhosted.org/packages/28/f0/397e98c352a27594566e865999dc6b88d6f37d5bbb87b23c982af24114c4/orjson-3.10.18-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ce8d0a875a85b4c8579eab5ac535fb4b2a50937267482be402627ca7e7570ee3", size = 153012, upload-time = "2025-04-29T23:30:01.066Z" }, + { url = "https://files.pythonhosted.org/packages/93/bf/2c7334caeb48bdaa4cae0bde17ea417297ee136598653b1da7ae1f98c785/orjson-3.10.18-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57b5d0673cbd26781bebc2bf86f99dd19bd5a9cb55f71cc4f66419f6b50f3d77", size = 136999, upload-time = "2025-04-29T23:30:02.93Z" }, + { url = "https://files.pythonhosted.org/packages/35/72/4827b1c0c31621c2aa1e661a899cdd2cfac0565c6cd7131890daa4ef7535/orjson-3.10.18-cp39-cp39-win32.whl", hash = "sha256:951775d8b49d1d16ca8818b1f20c4965cae9157e7b562a2ae34d3967b8f21c8e", size = 142560, upload-time = "2025-04-29T23:30:04.805Z" }, + { url = "https://files.pythonhosted.org/packages/72/91/ef8e76868e7eed478887c82f60607a8abf58dadd24e95817229a4b2e2639/orjson-3.10.18-cp39-cp39-win_amd64.whl", hash = "sha256:fdd9d68f83f0bc4406610b1ac68bdcded8c5ee58605cc69e643a06f4d075f429", size = 134455, upload-time = "2025-04-29T23:30:06.588Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "pip" +version = "25.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/59/de/241caa0ca606f2ec5fe0c1f4261b0465df78d786a38da693864a116c37f4/pip-25.1.1.tar.gz", hash = "sha256:3de45d411d308d5054c2168185d8da7f9a2cd753dbac8acbfa88a8909ecd9077", size = 1940155, upload-time = "2025-05-02T15:14:02.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/a2/d40fb2460e883eca5199c62cfc2463fd261f760556ae6290f88488c362c0/pip-25.1.1-py3-none-any.whl", hash = "sha256:2913a38a2abf4ea6b64ab507bd9e967f3b53dc1ede74b01b0931e1ce548751af", size = 1825227, upload-time = "2025-05-02T15:13:59.102Z" }, +] + +[[package]] +name = "pip-system-certs" +version = "4.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "wrapt", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/27/9a/4e949d0a281c5dd45c8d5b02b03fe32044936234675e967de49317a1daee/pip_system_certs-4.0.tar.gz", hash = "sha256:db8e6a31388d9795ec9139957df1a89fa5274fb66164456fd091a5d3e94c350c", size = 5622, upload-time = "2022-11-04T11:01:12.537Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/70/82/78c30a18858d484acd13a3aea22ead89c66f200e118d1aa4b4bae392efee/pip_system_certs-4.0-py2.py3-none-any.whl", hash = "sha256:47202b9403a6f40783a9674bbc8873f5fc86544ec01a49348fa913e99e2ff68b", size = 6070, upload-time = "2022-11-04T11:01:11.169Z" }, +] + +[[package]] +name = "pip-system-certs" +version = "5.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "pip", marker = "python_full_version >= '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/0c/a338ae5d49192861cf54da4d5c2af0efe47edbaa0827995b284005366ca5/pip_system_certs-5.2.tar.gz", hash = "sha256:80b776b5cf17191bf99d313699b7fce2fdb84eb7bbb225fd134109a82706406f", size = 5408, upload-time = "2025-06-17T23:33:15.322Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/ce/608bbe82759363d6e752dd370daf066be3be8e7ffdb79838501ed6104173/pip_system_certs-5.2-py3-none-any.whl", hash = "sha256:e6ef3e106d4d02313e33955c2bcc4c2b143b2da07ef91e28a6805a0c1c512126", size = 5866, upload-time = "2025-06-17T23:33:14.554Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "protobuf" +version = "6.31.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/f3/b9655a711b32c19720253f6f06326faf90580834e2e83f840472d752bc8b/protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a", size = 441797, upload-time = "2025-05-28T19:25:54.947Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/6f/6ab8e4bf962fd5570d3deaa2d5c38f0a363f57b4501047b5ebeb83ab1125/protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9", size = 423603, upload-time = "2025-05-28T19:25:41.198Z" }, + { url = "https://files.pythonhosted.org/packages/44/3a/b15c4347dd4bf3a1b0ee882f384623e2063bb5cf9fa9d57990a4f7df2fb6/protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447", size = 435283, upload-time = "2025-05-28T19:25:44.275Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c9/b9689a2a250264a84e66c46d8862ba788ee7a641cdca39bccf64f59284b7/protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402", size = 425604, upload-time = "2025-05-28T19:25:45.702Z" }, + { url = "https://files.pythonhosted.org/packages/76/a1/7a5a94032c83375e4fe7e7f56e3976ea6ac90c5e85fac8576409e25c39c3/protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39", size = 322115, upload-time = "2025-05-28T19:25:47.128Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b1/b59d405d64d31999244643d88c45c8241c58f17cc887e73bcb90602327f8/protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6", size = 321070, upload-time = "2025-05-28T19:25:50.036Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f0/4160dbd205eee8fdf8647d154e7ceaa9d25b3a877b6311274eb6dc896b75/protobuf-6.31.1-cp39-cp39-win32.whl", hash = "sha256:0414e3aa5a5f3ff423828e1e6a6e907d6c65c1d5b7e6e975793d5590bdeecc16", size = 423626, upload-time = "2025-05-28T19:25:51.355Z" }, + { url = "https://files.pythonhosted.org/packages/09/34/13989eb9f482409ed821bfa3e34e6a3878b42607c38e7f7572b4cc825091/protobuf-6.31.1-cp39-cp39-win_amd64.whl", hash = "sha256:8764cf4587791e7564051b35524b72844f845ad0bb011704c3736cce762d8fe9", size = 435347, upload-time = "2025-05-28T19:25:52.932Z" }, + { url = "https://files.pythonhosted.org/packages/f7/af/ab3c51ab7507a7325e98ffe691d9495ee3d3aa5f589afad65ec920d39821/protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e", size = 168724, upload-time = "2025-05-28T19:25:53.926Z" }, +] + +[[package]] +name = "psutil" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003, upload-time = "2025-02-13T21:54:07.946Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051, upload-time = "2025-02-13T21:54:12.36Z" }, + { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535, upload-time = "2025-02-13T21:54:16.07Z" }, + { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004, upload-time = "2025-02-13T21:54:18.662Z" }, + { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986, upload-time = "2025-02-13T21:54:21.811Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544, upload-time = "2025-02-13T21:54:24.68Z" }, + { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053, upload-time = "2025-02-13T21:54:34.31Z" }, + { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885, upload-time = "2025-02-13T21:54:37.486Z" }, +] + +[[package]] +name = "pychromedevtools" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, + { name = "websocket-client" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/04/572466be7e93af410d68cc0142850560cc139a254c659409765fd5a5547f/PyChromeDevTools-1.0.4.tar.gz", hash = "sha256:cc9e552f22032fce48ae3113ebb62e138ed5e1ea126011fd43262b7075970f0f", size = 8108, upload-time = "2025-05-29T09:24:23.841Z" } + +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" }, +] + +[[package]] +name = "pylint" +version = "3.3.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astroid" }, + { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "dill" }, + { name = "isort" }, + { name = "mccabe" }, + { name = "platformdirs" }, + { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "tomlkit" }, + { name = "typing-extensions", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/e4/83e487d3ddd64ab27749b66137b26dc0c5b5c161be680e6beffdc99070b3/pylint-3.3.7.tar.gz", hash = "sha256:2b11de8bde49f9c5059452e0c310c079c746a0a8eeaa789e5aa966ecc23e4559", size = 1520709, upload-time = "2025-05-04T17:07:51.089Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/83/bff755d09e31b5d25cc7fdc4bf3915d1a404e181f1abf0359af376845c24/pylint-3.3.7-py3-none-any.whl", hash = "sha256:43860aafefce92fca4cf6b61fe199cdc5ae54ea28f9bf4cd49de267b5195803d", size = 522565, upload-time = "2025-05-04T17:07:48.714Z" }, +] + +[[package]] +name = "pyqt6" +version = "6.6.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +dependencies = [ + { name = "pyqt6-qt6", version = "6.6.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, + { name = "pyqt6-sip", version = "13.6.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/2b/6fe0409501798abc780a70cab48c39599742ab5a8168e682107eaab78fca/PyQt6-6.6.1.tar.gz", hash = "sha256:9f158aa29d205142c56f0f35d07784b8df0be28378d20a97bcda8bd64ffd0379", size = 1043203, upload-time = "2023-12-04T10:37:27.406Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/b2/130408edb21b2bf889d175465711ffcc4aa2f0df152718505e458888646d/PyQt6-6.6.1-cp38-abi3-macosx_10_14_universal2.whl", hash = "sha256:6b43878d0bbbcf8b7de165d305ec0cb87113c8930c92de748a11c473a6db5085", size = 11642552, upload-time = "2023-12-04T10:37:09.363Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5a/51f4762b9f314b5577d17704bc1280532a725ba359d6cc177ab6de692035/PyQt6-6.6.1-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5aa0e833cb5a79b93813f8181d9f145517dd5a46f4374544bcd1e93a8beec537", size = 7898775, upload-time = "2023-12-04T10:37:16.831Z" }, + { url = "https://files.pythonhosted.org/packages/6d/40/e91a88d5c716e2982eb2eef5d4c314add196951e7d430e90eb0fe8fb81a1/PyQt6-6.6.1-cp38-abi3-win_amd64.whl", hash = "sha256:03a656d5dc5ac31b6a9ad200f7f4f7ef49fa00ad7ce7a991b9bb691617141d12", size = 6549432, upload-time = "2023-12-04T10:37:24.633Z" }, +] + +[[package]] +name = "pyqt6" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +dependencies = [ + { name = "pyqt6-qt6", version = "6.7.3", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra != 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, + { name = "pyqt6-sip", version = "13.10.2", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra != 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/f9/b0c2ba758b14a7219e076138ea1e738c068bf388e64eee68f3df4fc96f5a/PyQt6-6.7.1.tar.gz", hash = "sha256:3672a82ccd3a62e99ab200a13903421e2928e399fda25ced98d140313ad59cb9", size = 1051212, upload-time = "2024-07-19T08:49:58.247Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/b0/20a05cfe287a1bc5a034cfed002bb1999f71c15e53a6ab7886c010ea0ba3/PyQt6-6.7.1-1-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7f397f4b38b23b5588eb2c0933510deb953d96b1f0323a916c4839c2a66ccccc", size = 8020146, upload-time = "2024-07-31T09:50:04.992Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d3/8789879c05cfe06127c4b59258632bd175fcdd9eaaadaf0c897b458fb91d/PyQt6-6.7.1-1-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2f202b7941aa74e5c7e1463a6f27d9131dbc1e6cabe85571d7364f5b3de7397", size = 8227345, upload-time = "2024-07-31T09:50:13.511Z" }, + { url = "https://files.pythonhosted.org/packages/15/2b/a0c516931697214dcb93b24a62f54b7467194ba1c76f3f7a55cb3a120cc9/PyQt6-6.7.1-cp38-abi3-macosx_11_0_universal2.whl", hash = "sha256:f053378e3aef6248fa612c8afddda17f942fb63f9fe8a9aeb2a6b6b4cbb0eba9", size = 11871174, upload-time = "2024-07-19T08:49:21.831Z" }, + { url = "https://files.pythonhosted.org/packages/59/8c/3b528f5fa8dfc3d0ba07d8da37ea72dfc59352d80804a12507d7080efb30/PyQt6-6.7.1-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0adb7914c732ad1dee46d9cec838a98cb2b11bc38cc3b7b36fbd8701ae64bf47", size = 7999939, upload-time = "2024-07-19T08:49:30.82Z" }, + { url = "https://files.pythonhosted.org/packages/d8/58/5082dd3654da2b17de19057f181526df566f38af90f517cb8a541bea0890/PyQt6-6.7.1-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2d771fa0981514cb1ee937633dfa64f14caa902707d9afffab66677f3a73e3da", size = 8177790, upload-time = "2024-07-19T08:49:48.394Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/99d22ee685c08a99fcf2048d366fe6173ba6e43ee13b95a3a2ac2911c52c/PyQt6-6.7.1-cp38-abi3-win_amd64.whl", hash = "sha256:fa3954698233fe286a8afc477b84d8517f0788eb46b74da69d3ccc0170d3714c", size = 6596360, upload-time = "2024-07-19T08:49:55.594Z" }, +] + +[[package]] +name = "pyqt6" +version = "6.8.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +dependencies = [ + { name = "pyqt6-qt6", version = "6.8.1", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-sip", version = "13.10.2", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/0a/accbebed526158ab2aedd5c84d238159754bd99f481082b3fe7f374c6a3b/PyQt6-6.8.0.tar.gz", hash = "sha256:6d8628de4c2a050f0b74462e4c9cb97f839bf6ffabbca91711722ffb281570d9", size = 1061357, upload-time = "2024-12-12T15:30:42.021Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/ae/30f6f40c8cdf3d5319ec7add820c6edf96d8ed2461984fd548af9e2d9b51/PyQt6-6.8.0-cp39-abi3-macosx_10_14_universal2.whl", hash = "sha256:8c5c05f5fdff31a5887dbc29b27615b09df467631238d7b449283809ffca6228", size = 12104917, upload-time = "2024-12-12T15:30:09.647Z" }, + { url = "https://files.pythonhosted.org/packages/06/07/08229a8ae164a0538b2c1ab16c47d39979eb306feab641280194b3d7938b/PyQt6-6.8.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3a4354816f11e812b727206a9ea6e79ff3774f1bb7228ad4b9318442d2c64ff9", size = 8165074, upload-time = "2024-12-28T22:40:55.438Z" }, + { url = "https://files.pythonhosted.org/packages/b4/7a/a3ecac3327345dde8f34e781d6db1339e4e3b8411ac8ae9747822a0d44e9/PyQt6-6.8.0-cp39-abi3-manylinux_2_35_x86_64.whl", hash = "sha256:452bae5840077bf0f146c798d7777f70d7bdd0c7dcfa9ee7a415c1daf2d10038", size = 8322144, upload-time = "2024-12-12T15:30:19.225Z" }, + { url = "https://files.pythonhosted.org/packages/d0/52/1e60e38d216c232af83ac16bedf1ece419812368220485fc25af80c7a5d7/PyQt6-6.8.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:cf7123caea14e7ecf10bd12cae48e8d9970ef7caf627bc7d7988b0baa209adb3", size = 8101586, upload-time = "2024-12-12T15:30:27.089Z" }, + { url = "https://files.pythonhosted.org/packages/eb/73/9bae1b8daedf2acff5a3668eae84783720ff513e402b0448a906ab3d5479/PyQt6-6.8.0-cp39-abi3-win_amd64.whl", hash = "sha256:a9913d479f1ffee804bf7f232079baea4fb4b221a8f4890117588917a54ea30d", size = 6661265, upload-time = "2024-12-12T15:30:32.996Z" }, + { url = "https://files.pythonhosted.org/packages/23/a0/f564279285ed92b4fe38ed7b2a8fcddab414512391088b6a0f67a1107f24/PyQt6-6.8.0-cp39-abi3-win_arm64.whl", hash = "sha256:48bace7b87676bba5e6114482f3a20ca20be90c7f261b5d340464313f5f2bf5e", size = 5409032, upload-time = "2024-12-12T15:30:38.859Z" }, +] + +[[package]] +name = "pyqt6-qt6" +version = "6.6.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/3a/ce7fb7defd78499c00b3af7501d9a842a5c7cf89bb8847dd90f116850a0e/PyQt6_Qt6-6.6.2-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:7ef446d3ffc678a8586ff6dc9f0d27caf4dff05dea02c353540d2f614386faf9", size = 54829987, upload-time = "2024-02-17T18:30:43.483Z" }, + { url = "https://files.pythonhosted.org/packages/60/b4/fd4ba0ed6209642e12e90f830e77242ac5e4e7a11651e4c215bbc879eeea/PyQt6_Qt6-6.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b8363d88623342a72ac17da9127dc12f259bb3148796ea029762aa2d499778d9", size = 50647628, upload-time = "2024-02-17T18:34:06.088Z" }, + { url = "https://files.pythonhosted.org/packages/54/77/f00699bcecca636e90b22db6d5eef06a75f142ca0aaaacce87e35ffe05bf/PyQt6_Qt6-6.6.2-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:8d7f674a4ec43ca00191e14945ca4129acbe37a2172ed9d08214ad58b170bc11", size = 67634019, upload-time = "2024-02-17T18:44:55.828Z" }, + { url = "https://files.pythonhosted.org/packages/77/a7/7dbc3df460fe342982bc5ac769129a030772752d4a063a50a722668825c4/PyQt6_Qt6-6.6.2-py3-none-win_amd64.whl", hash = "sha256:5a41fe9d53b9e29e9ec5c23f3c5949dba160f90ca313ee8b96b8ffe6a5059387", size = 62428125, upload-time = "2024-02-17T18:47:01.572Z" }, +] + +[[package]] +name = "pyqt6-qt6" +version = "6.7.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/a2/9ef7c001068da2d3c8c37fe0e1e0451b1073d47c6ef4e44abf5883559963/PyQt6_Qt6-6.7.3-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:f517a93b6b1a814d4aa6587adc312e812ebaf4d70415bb15cfb44268c5ad3f5f", size = 49136114, upload-time = "2024-09-29T16:25:15.055Z" }, + { url = "https://files.pythonhosted.org/packages/ec/63/a85bdd7c66800208f0af417bb4d07cb1543a75384021e4594e66d919f855/PyQt6_Qt6-6.7.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8551732984fb36a5f4f3db51eafc4e8e6caf18617365830285306f2db17a94c2", size = 45762813, upload-time = "2024-09-29T16:25:20.956Z" }, + { url = "https://files.pythonhosted.org/packages/8a/6c/4f329f83a6082a7b4c1dc6046e2c48edb72e0d6d0ca3f8d0701fe134dccf/PyQt6_Qt6-6.7.3-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:50c7482bcdcf2bb78af257fb10ed8b582f8daf91d829782393bc50ac5a0a900c", size = 63801442, upload-time = "2024-09-29T16:25:26.637Z" }, + { url = "https://files.pythonhosted.org/packages/88/4d/26ca7239f7223e5b95b58a58537a09b069582ebb4dfa38234113a9f898ab/PyQt6_Qt6-6.7.3-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:cb525fdd393332de60887953029276a44de480fce1d785251ae639580f5e7246", size = 74366973, upload-time = "2024-09-29T16:25:33.595Z" }, + { url = "https://files.pythonhosted.org/packages/7e/57/3b44f6af1020fa543bd564c5bd346ba4aab1f1be0b861c2e8a0ad88cf3ca/PyQt6_Qt6-6.7.3-py3-none-win_amd64.whl", hash = "sha256:36ea0892b8caeb983af3f285f45fb8dfbb93cfd972439f4e01b7efb2868f6230", size = 58467498, upload-time = "2024-09-29T16:25:39.569Z" }, +] + +[[package]] +name = "pyqt6-qt6" +version = "6.8.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/19/b89eb6cecbdf1e65a44658a083693a967e9d428370026711b624e928a8ca/PyQt6_Qt6-6.8.1-1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:2f4b8b55b1414b93f340f22e8c88d25550efcdebc4b65a3927dd947b73bd4358", size = 80877444, upload-time = "2025-01-18T12:41:27.157Z" }, + { url = "https://files.pythonhosted.org/packages/87/1b/94d3710ee7ef93ee99c1dac512f631de5e310f6b21e43f474ef269f840b6/PyQt6_Qt6-6.8.1-1-py3-none-manylinux_2_39_aarch64.whl", hash = "sha256:98aa99fe38ae68c5318284cd28f3479ba538c40bf6ece293980abae0925c1b24", size = 79473622, upload-time = "2025-01-18T12:42:07.39Z" }, + { url = "https://files.pythonhosted.org/packages/df/0a/c47a1cc272b418faff8af79b121f0cecd32b09d634253254e3a990432220/PyQt6_Qt6-6.8.1-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:1eb8460a1fdb38d0b2458c2974c01d471c1e59e4eb19ea63fc447aaba3ad530e", size = 65754939, upload-time = "2024-12-06T13:51:58.697Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e6/cc4fbc97a7d0955185e33add3ce00480f0023424d17ac6f864a504f60251/PyQt6_Qt6-6.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9f3790c4ce4dc576e48b8718d55fb8743057e6cbd53a6ca1dd253ffbac9b7287", size = 59956028, upload-time = "2024-12-06T13:52:04.335Z" }, + { url = "https://files.pythonhosted.org/packages/01/22/c2997fe76d765d9ba960e9a099238cb419a316362bdde50fedacc23e7c7d/PyQt6_Qt6-6.8.1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:d6ca5d2b9d2ec0ee4a814b2175f641a5c4299cb80b45e0f5f8356632663f89b3", size = 72561636, upload-time = "2024-12-22T13:21:34.831Z" }, + { url = "https://files.pythonhosted.org/packages/a1/24/907c7451901e56e5b713d70c6f65191d80a3a3caac1095fea7f2a4b0b25b/PyQt6_Qt6-6.8.1-py3-none-manylinux_2_35_x86_64.whl", hash = "sha256:08065d595f1e6fc2dde9f4450eeff89082f4bad26f600a8e9b9cc5966716bfcf", size = 72573711, upload-time = "2024-12-06T13:52:10.236Z" }, + { url = "https://files.pythonhosted.org/packages/2b/a8/d942bb9f83ad4abd4c296cd4bbf752d1147eaa14523cc132a2b8511a3960/PyQt6_Qt6-6.8.1-py3-none-manylinux_2_39_aarch64.whl", hash = "sha256:20843cb86bd94942d1cd99e39bf1aeabb875b241a35a8ab273e4bbbfa63776db", size = 71077917, upload-time = "2024-12-06T13:52:16.588Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2c/0d45a74ea8eedf5a2a5a2628396fb2e26107ae07f6c73d6d22c7fa3d4403/PyQt6_Qt6-6.8.1-py3-none-win_amd64.whl", hash = "sha256:006d786693d0511fbcf184a862edbd339c6ed1bb3bd9de363d73a19ed4b23dff", size = 71647964, upload-time = "2024-12-06T13:52:23.512Z" }, + { url = "https://files.pythonhosted.org/packages/c9/0d/b86b64f0ac5e08b1bd18edac9e949897a4626196725e9a86af4e9f628c80/PyQt6_Qt6-6.8.1-py3-none-win_arm64.whl", hash = "sha256:a8bc2ed4ee5e7c6ff4dd1c7db0b27705d151fee5dc232bbd1bf17618f937f515", size = 47836492, upload-time = "2024-12-06T13:52:29.511Z" }, +] + +[[package]] +name = "pyqt6-sip" +version = "13.6.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/98/23/e54e02a44afc357ccab1b88575b90729664164358ceffde43e4f2e549daa/PyQt6_sip-13.6.0.tar.gz", hash = "sha256:2486e1588071943d4f6657ba09096dc9fffd2322ad2c30041e78ea3f037b5778", size = 111576, upload-time = "2023-10-14T10:23:13.472Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/b5/260c8a98dfa546a05d5e72857332c05e84ea3e4cbbea6f61fa94bab02776/PyQt6_sip-13.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d6b5f699aaed0ac1fcd23e8fbca70d8a77965831b7c1ce474b81b1678817a49d", size = 132017, upload-time = "2023-10-14T10:22:37.125Z" }, + { url = "https://files.pythonhosted.org/packages/f9/99/752149e3171dc64b109b9a6ec303a07b7c51a46aacf2142baa93607db6da/PyQt6_sip-13.6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:8c282062125eea5baf830c6998587d98c50be7c3a817a057fb95fef647184012", size = 299258, upload-time = "2023-10-14T10:22:40.652Z" }, + { url = "https://files.pythonhosted.org/packages/f9/45/7e4bfd4fe7027e514ec7c0f82dbd5c66974e77edee1484d0706e6e1731aa/PyQt6_sip-13.6.0-cp310-cp310-win32.whl", hash = "sha256:fa759b6339ff7e25f9afe2a6b651b775f0a36bcb3f5fa85e81a90d3b033c83f4", size = 65157, upload-time = "2023-10-14T10:22:42.765Z" }, + { url = "https://files.pythonhosted.org/packages/cf/26/769b79b48da4162b1913c589b1de8c8b067a84c56f731c3439adf624710b/PyQt6_sip-13.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:8f9df9f7ccd8a9f0f1d36948c686f03ce1a1281543a3e636b7b7d5e086e1a436", size = 72806, upload-time = "2023-10-14T10:22:44.34Z" }, + { url = "https://files.pythonhosted.org/packages/4c/df/8ad3c2e8cc151fdb4293dcd6c2bb4375eb49b626c34a37e657aafcb8e245/PyQt6_sip-13.6.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b9c6b6f9cfccb48cbb78a59603145a698fb4ffd176764d7083e5bf47631d8df", size = 132109, upload-time = "2023-10-14T10:22:45.791Z" }, + { url = "https://files.pythonhosted.org/packages/97/50/cab92a609b0da9740b0c12071bc3c31315cc87db45db8095665a22ced266/PyQt6_sip-13.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:86a7b67c64436e32bffa9c28c9f21bf14a9faa54991520b12c3f6f435f24df7f", size = 308305, upload-time = "2023-10-14T10:22:47.525Z" }, + { url = "https://files.pythonhosted.org/packages/0b/3b/cfc7d1c9acbfa832c612751f443e260e1a569ac077d913887db2b2445bce/PyQt6_sip-13.6.0-cp311-cp311-win32.whl", hash = "sha256:58f68a48400e0b3d1ccb18090090299bad26e3aed7ccb7057c65887b79b8aeea", size = 65165, upload-time = "2023-10-14T10:22:49.982Z" }, + { url = "https://files.pythonhosted.org/packages/20/42/9e31de556d6991f041268aac913b7359f6d54d2000b0182f8051869ab140/PyQt6_sip-13.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:0dfd22cfedd87e96f9d51e0778ca2ba3dc0be83e424e9e0f98f6994d8d9c90f0", size = 72814, upload-time = "2023-10-14T10:22:51.216Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ed/676969afd6834c11c86a05c0b72b285b596253fd2ee583f50a4ee134158a/PyQt6_sip-13.6.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:3bf03e130fbfd75c9c06e687b86ba375410c7a9e835e4e03285889e61dd4b0c4", size = 133122, upload-time = "2023-10-14T10:22:52.852Z" }, + { url = "https://files.pythonhosted.org/packages/24/9d/fcd2c6c088719bb45ab80bfda6ab294b125ffd0d8ae3440f5370b2ee0b89/PyQt6_sip-13.6.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:43fb8551796030aae3d66d6e35e277494071ec6172cd182c9569ab7db268a2f5", size = 319963, upload-time = "2023-10-14T10:22:55.415Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fe/dab9fa97cd61635d70ad204f4718db434d7197f7efed30b250372ac7c000/PyQt6_sip-13.6.0-cp312-cp312-win32.whl", hash = "sha256:13885361ca2cb2f5085d50359ba61b3fabd41b139fb58f37332acbe631ef2357", size = 65443, upload-time = "2023-10-14T10:22:56.763Z" }, + { url = "https://files.pythonhosted.org/packages/6e/c9/37ff56008c9d23f94ab76630f449ba1a3a76a49cedac4ffcba891f4692fd/PyQt6_sip-13.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:24441032a29791e82beb7dfd76878339058def0e97fdb7c1cea517f3a0e6e96b", size = 73131, upload-time = "2023-10-14T10:22:58.174Z" }, + { url = "https://files.pythonhosted.org/packages/e1/f7/94665dafb3488fe2d2ab5409b4761976b1b8ea95c6d52caaa9e081ad978c/PyQt6_sip-13.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:98bf954103b087162fa63b3a78f30b0b63da22fd6450b610ec1b851dbb798228", size = 132052, upload-time = "2023-10-14T10:23:06.234Z" }, + { url = "https://files.pythonhosted.org/packages/e2/21/be11813fb40cce933afa2a3e523aa998ca1089f1678bdb5ab11d19b305e2/PyQt6_sip-13.6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:39854dba35f8e5a4288da26ecb5f40b4c5ec1932efffb3f49d5ea435a7f37fb3", size = 297293, upload-time = "2023-10-14T10:23:08.518Z" }, + { url = "https://files.pythonhosted.org/packages/d9/29/34b3d088c266ca781db0e0693e529181c55ee45e85f177076a249c0d9207/PyQt6_sip-13.6.0-cp39-cp39-win32.whl", hash = "sha256:747f6ca44af81777a2c696bd501bc4815a53ec6fc94d4e25830e10bc1391f8ab", size = 65035, upload-time = "2023-10-14T10:23:10.251Z" }, + { url = "https://files.pythonhosted.org/packages/af/77/aa2f0fb038efbff40aa7aacb9e7a9ffeaf270a7f49e03b78d09e87b7cc12/PyQt6_sip-13.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:33ea771fe777eb0d1a2c3ef35bcc3f7a286eb3ff09cd5b2fdd3d87d1f392d7e8", size = 73027, upload-time = "2023-10-14T10:23:11.711Z" }, +] + +[[package]] +name = "pyqt6-sip" +version = "13.10.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/4a/96daf6c2e4f689faae9bd8cebb52754e76522c58a6af9b5ec86a2e8ec8b4/pyqt6_sip-13.10.2.tar.gz", hash = "sha256:464ad156bf526500ce6bd05cac7a82280af6309974d816739b4a9a627156fafe", size = 92548, upload-time = "2025-05-23T12:26:49.901Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/a8/9eb019525f26801cf91ba38c8493ef641ee943d3b77885e78ac9fab11870/pyqt6_sip-13.10.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8132ec1cbbecc69d23dcff23916ec07218f1a9bbbc243bf6f1df967117ce303e", size = 110689, upload-time = "2025-05-23T12:26:21.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/29/79a2dba1cc6ec02c927dd0ffd596ca15ba0a2968123143bc00fc35f0173b/pyqt6_sip-13.10.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07f77e89d93747dda71b60c3490b00d754451729fbcbcec840e42084bf061655", size = 305804, upload-time = "2025-05-23T12:26:23.297Z" }, + { url = "https://files.pythonhosted.org/packages/bb/4f/fa8468f055679905d0e38d471ae16b5968896ee1d951477e162d9d0a712d/pyqt6_sip-13.10.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:4ffa71ddff6ef031d52cd4f88b8bba08b3516313c023c7e5825cf4a0ba598712", size = 284059, upload-time = "2025-05-23T12:26:24.507Z" }, + { url = "https://files.pythonhosted.org/packages/e1/4e/abc995daaafe5ac55e00df0f42c4a5ee81473425a3250a20dc4301399842/pyqt6_sip-13.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:e907394795e61f1174134465c889177f584336a98d7a10beade2437bf5942244", size = 53410, upload-time = "2025-05-23T12:26:25.62Z" }, + { url = "https://files.pythonhosted.org/packages/75/9c/ea9ba7786f471ce025dff71653eec4a6c067d24d36d28cced457dd31314c/pyqt6_sip-13.10.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1a6c2f168773af9e6c7ef5e52907f16297d4efd346e4c958eda54ea9135be18e", size = 110707, upload-time = "2025-05-23T12:26:26.666Z" }, + { url = "https://files.pythonhosted.org/packages/d6/00/984a94f14ba378c802a8e304803bb6dc6961cd9f24befa1bf3987731f0c3/pyqt6_sip-13.10.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1d3cc9015a1bd8c8d3e86a009591e897d4d46b0c514aede7d2970a2208749cd", size = 317301, upload-time = "2025-05-23T12:26:28.182Z" }, + { url = "https://files.pythonhosted.org/packages/0d/b1/c3b433ebcee2503571d71be025de5dab4489d7153007fd5ae79c543eeedb/pyqt6_sip-13.10.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ddd578a8d975bfb5fef83751829bf09a97a1355fa1de098e4fb4d1b74ee872fc", size = 294277, upload-time = "2025-05-23T12:26:29.406Z" }, + { url = "https://files.pythonhosted.org/packages/24/96/4e909f0a4f7a9ad0076a0e200c10f96a5a09492efb683f3d66c885f9aba4/pyqt6_sip-13.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:061d4a2eb60a603d8be7db6c7f27eb29d9cea97a09aa4533edc1662091ce4f03", size = 53418, upload-time = "2025-05-23T12:26:30.536Z" }, + { url = "https://files.pythonhosted.org/packages/37/96/153c418d8c167fc56f2e62372b8862d577f3ece41b24c5205a05b0c2b0cd/pyqt6_sip-13.10.2-cp311-cp311-win_arm64.whl", hash = "sha256:45ac06f0380b7aa4fcffd89f9e8c00d1b575dc700c603446a9774fda2dcfc0de", size = 44969, upload-time = "2025-05-23T12:26:31.498Z" }, + { url = "https://files.pythonhosted.org/packages/22/5b/1240017e0d59575289ba52b58fd7f95e7ddf0ed2ede95f3f7e2dc845d337/pyqt6_sip-13.10.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:83e6a56d3e715f748557460600ec342cbd77af89ec89c4f2a68b185fa14ea46c", size = 112199, upload-time = "2025-05-23T12:26:32.503Z" }, + { url = "https://files.pythonhosted.org/packages/51/11/1fc3bae02a12a3ac8354aa579b56206286e8b5ca9586677b1058c81c2f74/pyqt6_sip-13.10.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ccf197f8fa410e076936bee28ad9abadb450931d5be5625446fd20e0d8b27a6", size = 322757, upload-time = "2025-05-23T12:26:33.752Z" }, + { url = "https://files.pythonhosted.org/packages/21/40/de9491213f480a27199690616959a17a0f234962b86aa1dd4ca2584e922d/pyqt6_sip-13.10.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:37af463dcce39285e686d49523d376994d8a2508b9acccb7616c4b117c9c4ed7", size = 304251, upload-time = "2025-05-23T12:26:35.66Z" }, + { url = "https://files.pythonhosted.org/packages/02/21/cc80e03f1052408c62c341e9fe9b81454c94184f4bd8a95d29d2ec86df92/pyqt6_sip-13.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:c7b34a495b92790c70eae690d9e816b53d3b625b45eeed6ae2c0fe24075a237e", size = 53519, upload-time = "2025-05-23T12:26:36.797Z" }, + { url = "https://files.pythonhosted.org/packages/77/cf/53bd0863252b260a502659cb3124d9c9fe38047df9360e529b437b4ac890/pyqt6_sip-13.10.2-cp312-cp312-win_arm64.whl", hash = "sha256:c80cc059d772c632f5319632f183e7578cd0976b9498682833035b18a3483e92", size = 45349, upload-time = "2025-05-23T12:26:37.729Z" }, + { url = "https://files.pythonhosted.org/packages/a1/1e/979ea64c98ca26979d8ce11e9a36579e17d22a71f51d7366d6eec3c82c13/pyqt6_sip-13.10.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8b5d06a0eac36038fa8734657d99b5fe92263ae7a0cd0a67be6acfe220a063e1", size = 112227, upload-time = "2025-05-23T12:26:38.758Z" }, + { url = "https://files.pythonhosted.org/packages/d9/21/84c230048e3bfef4a9209d16e56dcd2ae10590d03a31556ae8b5f1dcc724/pyqt6_sip-13.10.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad376a6078da37b049fdf9d6637d71b52727e65c4496a80b753ddc8d27526aca", size = 322920, upload-time = "2025-05-23T12:26:39.856Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/c6a28a142f14e735088534cc92951c3f48cccd77cdd4f3b10d7996be420f/pyqt6_sip-13.10.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3dde8024d055f496eba7d44061c5a1ba4eb72fc95e5a9d7a0dbc908317e0888b", size = 303833, upload-time = "2025-05-23T12:26:41.075Z" }, + { url = "https://files.pythonhosted.org/packages/89/63/e5adf350c1c3123d4865c013f164c5265512fa79f09ad464fb2fdf9f9e61/pyqt6_sip-13.10.2-cp313-cp313-win_amd64.whl", hash = "sha256:0b097eb58b4df936c4a2a88a2f367c8bb5c20ff049a45a7917ad75d698e3b277", size = 53527, upload-time = "2025-05-23T12:26:42.625Z" }, + { url = "https://files.pythonhosted.org/packages/58/74/2df4195306d050fbf4963fb5636108a66e5afa6dc05fd9e81e51ec96c384/pyqt6_sip-13.10.2-cp313-cp313-win_arm64.whl", hash = "sha256:cc6a1dfdf324efaac6e7b890a608385205e652845c62130de919fd73a6326244", size = 45373, upload-time = "2025-05-23T12:26:43.536Z" }, + { url = "https://files.pythonhosted.org/packages/d1/39/4693dfad856ee9613fbf325916d980a76d5823f4da87fed76f00b48ee8ee/pyqt6_sip-13.10.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38b5823dca93377f8a4efac3cbfaa1d20229aa5b640c31cf6ebbe5c586333808", size = 110676, upload-time = "2025-05-23T12:26:44.593Z" }, + { url = "https://files.pythonhosted.org/packages/f0/42/6f7c2006871b20cf3e5073e3ffaa0bede0f8e2f8ccc2105c02e8d523c7d7/pyqt6_sip-13.10.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5506b9a795098df3b023cc7d0a37f93d3224a9c040c43804d4bc06e0b2b742b0", size = 303064, upload-time = "2025-05-23T12:26:46.19Z" }, + { url = "https://files.pythonhosted.org/packages/00/1c/38068f79d583fc9c2992553445634171e8b0bee6682be22cb8d4d18e7da6/pyqt6_sip-13.10.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e455a181d45a28ee8d18d42243d4f470d269e6ccdee60f2546e6e71218e05bb4", size = 281774, upload-time = "2025-05-23T12:26:47.413Z" }, + { url = "https://files.pythonhosted.org/packages/aa/97/70cad9a770a56a2efb30c120fb1619ed81a6058c014cdcda5133429ad033/pyqt6_sip-13.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:9c67ed66e21b11e04ffabe0d93bc21df22e0a5d7e2e10ebc8c1d77d2f5042991", size = 53630, upload-time = "2025-05-23T12:26:48.534Z" }, +] + +[[package]] +name = "pyqt6-webengine" +version = "6.6.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +dependencies = [ + { name = "pyqt6", version = "6.6.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, + { name = "pyqt6-sip", version = "13.6.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, + { name = "pyqt6-webengine-qt6", version = "6.6.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/9a/69db3a2ab1ba43f762144a66f0375540e195e107a1049d7263ab48ebc9cc/PyQt6_WebEngine-6.6.0.tar.gz", hash = "sha256:d50b984c3f85e409e692b156132721522d4e8cf9b6c25e0cf927eea2dfb39487", size = 31817, upload-time = "2023-10-30T10:57:13.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/5d/9902206734550a9d7efcf0a20c22666c3fd2643b1202d12082ddae7b2ba9/PyQt6_WebEngine-6.6.0-cp37-abi3-macosx_10_14_universal2.whl", hash = "sha256:cb7793f06525ca054fcc6039afd93e23b82228b880d0b1301ce635f7f3ed2edf", size = 381778, upload-time = "2023-10-30T10:57:02.035Z" }, + { url = "https://files.pythonhosted.org/packages/36/91/50982f28730c893a8c68b0e92debc761e8a475cf1df686dee22a4c255316/PyQt6_WebEngine-6.6.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:fded35fba636c4916fec84aa7c6840ad2e75d211462feb3e966f9545a59d56e6", size = 257595, upload-time = "2023-10-30T10:57:10.273Z" }, + { url = "https://files.pythonhosted.org/packages/5b/6a/50a2c81f88ebe41ef9f27fc20ed74ea9d1394a60f09917d9d6d18e8d70b3/PyQt6_WebEngine-6.6.0-cp37-abi3-win_amd64.whl", hash = "sha256:9d542738ed6e11c1978ce59035c07627def7c63eef0f59581d327f01209141bc", size = 209371, upload-time = "2023-10-30T10:57:11.944Z" }, +] + +[[package]] +name = "pyqt6-webengine" +version = "6.7.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +dependencies = [ + { name = "pyqt6", version = "6.7.1", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra != 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, + { name = "pyqt6-sip", version = "13.10.2", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra != 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, + { name = "pyqt6-webengine-qt6", version = "6.7.3", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra != 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/87/88/230ec599944edf941f4cca8d1439e3a9c8c546715434eee05dce7ff032ed/PyQt6_WebEngine-6.7.0.tar.gz", hash = "sha256:68edc7adb6d9e275f5de956881e79cca0d71fad439abeaa10d823bff5ac55001", size = 32593, upload-time = "2024-04-26T08:37:08.355Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/74/10eb5db10e50584096c136f2ed15cee782da19344822dafff02a70de60ad/PyQt6_WebEngine-6.7.0-cp38-abi3-macosx_10_14_universal2.whl", hash = "sha256:1a3df9d6ac2dfa1bb3b826f3926c13db5b6d427e96e8d574c5aa5445a8b13db8", size = 388489, upload-time = "2024-04-26T08:35:59.855Z" }, + { url = "https://files.pythonhosted.org/packages/76/a2/c5d699f5637f2808609baad563690a64b2773420535eb72ffc9992e62ebe/PyQt6_WebEngine-6.7.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b4272943ef8c71b77d2421409982ddef56ff4c97be415b808f09b3507991460b", size = 268337, upload-time = "2024-07-19T08:58:17.441Z" }, + { url = "https://files.pythonhosted.org/packages/66/ea/9b8d93a5c81b536378c817b69ae373ab0c09e43878013a0aa5cd092e6f5e/PyQt6_WebEngine-6.7.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:098b5f8d5903a3091010098694f84dd786cca878ee28a5a3ce651507d5d65468", size = 268626, upload-time = "2024-04-26T08:36:01.659Z" }, + { url = "https://files.pythonhosted.org/packages/f9/28/495a8908b7d27cea17c9e1a3257e6165f90b793543a8e4787d70e57731fe/PyQt6_WebEngine-6.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:e79191e626303b11ae9a3ee98e70160f50bc90a9c4df92df17bade8290e139e8", size = 215995, upload-time = "2024-04-26T08:36:04.463Z" }, +] + +[[package]] +name = "pyqt6-webengine" +version = "6.8.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +dependencies = [ + { name = "pyqt6", version = "6.8.0", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-sip", version = "13.10.2", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-webengine-qt6", version = "6.8.1", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/c8/cadaa950eaf97f29e48c435e274ea5a81c051e745a3e2f5d9d994b7a6cda/PyQt6_WebEngine-6.8.0.tar.gz", hash = "sha256:64045ea622b6a41882c2b18f55ae9714b8660acff06a54e910eb72822c2f3ff2", size = 34203, upload-time = "2024-12-12T15:34:35.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/e0/cc792002fc0af923d8c2d50bf7d164cee8ab32ed54107867321e2eef6c5d/PyQt6_WebEngine-6.8.0-cp39-abi3-macosx_10_14_universal2.whl", hash = "sha256:c7a5731923112acf23fbf93efad91f7b1545221063572106273e34c15a029fe7", size = 432675, upload-time = "2024-12-12T15:34:28.592Z" }, + { url = "https://files.pythonhosted.org/packages/db/68/6ca1671a36f2098504634de19fcdfc8cad6b6c2fba86f1b40b335b3aa895/PyQt6_WebEngine-6.8.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5b5090dcc71dd36172ca8370db7dcaadfa0a022a8e58f6e172301289036c666b", size = 295421, upload-time = "2024-12-28T22:45:45.772Z" }, + { url = "https://files.pythonhosted.org/packages/34/89/6afb90e085c9a6ad593d7c820f9e3ec5923e7bfa3a9ccc658ab7d7634eaf/PyQt6_WebEngine-6.8.0-cp39-abi3-manylinux_2_35_x86_64.whl", hash = "sha256:5b9231b58014965b72504e49f39a6dbc3ecd05d4d725af011d75e6c8a7e2d5f7", size = 297339, upload-time = "2024-12-12T15:34:30.213Z" }, + { url = "https://files.pythonhosted.org/packages/87/51/43830c9ba425f499f3c0279b0febd48f1b647419256b101923b96009587b/PyQt6_WebEngine-6.8.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:c549f0f72c285eeea94000f6764dfaebf6bb3b13224580c7169a409bf1bf1bb7", size = 293678, upload-time = "2024-12-12T15:34:32.315Z" }, + { url = "https://files.pythonhosted.org/packages/1e/74/9b20e505737ceefe2ffb47355633c84b7d5d7d592f32165425b3e0ce7dd9/PyQt6_WebEngine-6.8.0-cp39-abi3-win_amd64.whl", hash = "sha256:d7366809d681bcc096fa565f2a81d0ab040f7da5bb4f12f78e834a2b173c87d1", size = 234566, upload-time = "2024-12-12T15:34:33.59Z" }, +] + +[[package]] +name = "pyqt6-webengine-qt6" +version = "6.6.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/0b/9aa53f5924d889671381a249ad4fe0f8577462f303f212167728ba3496e5/PyQt6_WebEngine_Qt6-6.6.2-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:27b1b6a6f4ea115b3dd300d2df906d542009d9eb0e62b05e6b7cb85dfe68e9c3", size = 96776704, upload-time = "2024-02-17T22:08:37.135Z" }, + { url = "https://files.pythonhosted.org/packages/38/1c/a97cc4d2c3258f0d75079f63d078e9fe2aea00b74c81c8881a44250c81f5/PyQt6_WebEngine_Qt6-6.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f2364dfa3a6e751ead71b7ba759081be677fcf1c6bbd8a2a2a250eb5f06432e8", size = 87690629, upload-time = "2024-02-17T22:10:48.315Z" }, + { url = "https://files.pythonhosted.org/packages/f1/66/142656307b59774abaf872f44268cda8fee1d46329900bd90162055a56d2/PyQt6_WebEngine_Qt6-6.6.2-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:3da4db9ddd984b647d0b79fa10fc6cf65364dfe283cd702b12cb7164be2307cd", size = 91492747, upload-time = "2024-02-17T22:13:09.102Z" }, + { url = "https://files.pythonhosted.org/packages/15/45/33393682fbd67bbc2f12000a7f54d66877a6bd198fb2f303a07a1f190daa/PyQt6_WebEngine_Qt6-6.6.2-py3-none-win_amd64.whl", hash = "sha256:5d6f3ae521115cee77fea22b0248e7b219995390b951b51e4d519aef9c304ca8", size = 82648352, upload-time = "2024-02-17T22:16:57.929Z" }, +] + +[[package]] +name = "pyqt6-webengine-qt6" +version = "6.7.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +dependencies = [ + { name = "pyqt6-webenginesubwheel-qt6", marker = "(extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra != 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/35/570d072bec7c114b5d155d990e2b8339223e230e9276bdf806a20f71e50d/PyQt6_WebEngine_Qt6-6.7.3-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:68812b2a5d0d417ce32dc4d11a304e7838e02c51013712e7533faf03448672d9", size = 26214456, upload-time = "2024-09-29T16:27:20.257Z" }, + { url = "https://files.pythonhosted.org/packages/10/a8/afb5e8ea9e19bcdead6c1e48b697a061ec64ee093e713e0ca65ff74b7c93/PyQt6_WebEngine_Qt6-6.7.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:304b0eda7cbd0bb6dd20428a5ac6bb627d7836e3e1baad483f48dcd0faa862c8", size = 26207595, upload-time = "2024-09-29T16:27:23.7Z" }, + { url = "https://files.pythonhosted.org/packages/cd/77/c3783f854e8286792d151d8b60d5509b29e25c478338480d37ca2b205cf7/PyQt6_WebEngine_Qt6-6.7.3-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:acbc53c350d8154bbd3c159918379dba24096caa790c6dd5721c67577e63f0af", size = 26058207, upload-time = "2024-09-29T16:27:27.899Z" }, + { url = "https://files.pythonhosted.org/packages/65/a6/488cf37fe43e8e84d75f283a27b482dde6055307e11d74eee967db371314/PyQt6_WebEngine_Qt6-6.7.3-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:eaba7cc77178449f59f1301925c48846f0cd0318036e3866631d910bd2c97275", size = 26066070, upload-time = "2024-09-29T16:27:31.305Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f3/750da5396163bf1da3004d4180ebb93480af0fc16b0839e0c19a056b4191/PyQt6_WebEngine_Qt6-6.7.3-py3-none-win_amd64.whl", hash = "sha256:8f254cf647e90c3a7d43ee6ccb57c4e3b260620d60ca77ac1829f3030de57d12", size = 26871320, upload-time = "2024-09-29T16:27:35.341Z" }, +] + +[[package]] +name = "pyqt6-webengine-qt6" +version = "6.8.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/94/d81bc5cc07b8bfb26b939f47e9f1ac84f19c97a5848613cb56c001cd2274/PyQt6_WebEngine_Qt6-6.8.1-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:79f67a459ecb452f865e04f19122a1d6f30c83d9a1ffd06e7e6f0d652204083a", size = 113139940, upload-time = "2024-12-06T13:48:07.964Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f0/567afc399514d2c81ee6f86563a7f8c4b880b9a8b44ae77e521ba9ed357c/PyQt6_WebEngine_Qt6-6.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e36574aa55b30633a12aa000835f01e488a0f0c13513fd9a0d50c2281e0a9068", size = 101998864, upload-time = "2024-12-06T13:46:47.128Z" }, + { url = "https://files.pythonhosted.org/packages/88/77/69ae7fd51a18edecb5c2ffcb3827a71281839c998a2c2c94179490f727e4/PyQt6_WebEngine_Qt6-6.8.1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:a375dbb34e03707b0ab4830b61e4d77a31dc3ef880421c8936472f2af34a3f80", size = 105208443, upload-time = "2024-12-22T13:23:20.512Z" }, + { url = "https://files.pythonhosted.org/packages/25/5b/3128529503babe9f44bce0a72be2ef8a847c091843a6d4a0f40f17fb3010/PyQt6_WebEngine_Qt6-6.8.1-py3-none-manylinux_2_35_x86_64.whl", hash = "sha256:0405b6ce35f406affb27547c6c3608dc82405568af71505fefae4081c8b4ac39", size = 105220519, upload-time = "2024-12-06T13:47:14.837Z" }, + { url = "https://files.pythonhosted.org/packages/0e/f5/1fb7e548e77b458ebca8c8e0f3b04e3628fe1951363b3abd68139f7ea6f8/PyQt6_WebEngine_Qt6-6.8.1-py3-none-manylinux_2_39_aarch64.whl", hash = "sha256:8059118591641cc9da6616343d893c77fbd065bef3e0764679543345e2c75123", size = 101199242, upload-time = "2024-12-06T13:48:33.363Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b5/a641ebe3e5113bee23d911c58fdd2e65061a6e3786a26b068468b988e5d2/PyQt6_WebEngine_Qt6-6.8.1-py3-none-win_amd64.whl", hash = "sha256:0ced2a10433da2571cfa29ed882698e0e164184d54068d17ba73799c45af5f0f", size = 95657750, upload-time = "2024-12-06T13:47:43.048Z" }, +] + +[[package]] +name = "pyqt6-webenginesubwheel-qt6" +version = "6.7.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/2a/cc74597ee5fa7025000bbbfa84e067bc90fca73788e3ca0322d6420f40e6/PyQt6_WebEngineSubwheel_Qt6-6.7.3-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:60a82726a5f87d788455fe7609df2c791507da64a9b6cefd5e9b590978da184b", size = 81835549, upload-time = "2024-09-29T16:26:16.768Z" }, + { url = "https://files.pythonhosted.org/packages/f3/31/7e51f7de32d1f12e29f5eed8fdfc187b09bff3791cdf297bd5ed51fbb28d/PyQt6_WebEngineSubwheel_Qt6-6.7.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1746049e382471c1346a3d1a034c4c34a994b66f833c79945127e562c6c044a8", size = 72457223, upload-time = "2024-09-29T16:26:24.456Z" }, + { url = "https://files.pythonhosted.org/packages/7f/94/53d4af41a2e8a84bd3019b1e221fda903b94a97389d663e6ecb5052948df/PyQt6_WebEngineSubwheel_Qt6-6.7.3-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:92f95f4adf9c12014088054800bb42e4fa63abfedf480b82e83cfb7a25714539", size = 70355555, upload-time = "2024-09-29T16:26:31.235Z" }, + { url = "https://files.pythonhosted.org/packages/16/a7/ddfef98bacd44e78af62427e5ec9a806145faf453013db5207c11c5c21e0/PyQt6_WebEngineSubwheel_Qt6-6.7.3-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:e400ce9a4b661f0cc074cf2eaf3643412d8d7b68c010630b7a12a8d2a5e1d980", size = 76078480, upload-time = "2024-09-29T16:26:38.237Z" }, + { url = "https://files.pythonhosted.org/packages/c3/66/4abb305204355f08b3fd6414ea9ee0240a00a7af6e04e5990118059be148/PyQt6_WebEngineSubwheel_Qt6-6.7.3-py3-none-win_amd64.whl", hash = "sha256:59414eca4c67a3cd2b97f993f8df284480817b0d69ea45ca9922be2157b2a87f", size = 66281783, upload-time = "2024-09-29T16:26:44.512Z" }, +] + +[[package]] +name = "pysocks" +version = "1.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429, upload-time = "2019-09-20T02:07:35.714Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725, upload-time = "2019-09-20T02:06:22.938Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, +] + +[[package]] +name = "pywin32" +version = "310" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/da/a5f38fffbba2fb99aa4aa905480ac4b8e83ca486659ac8c95bce47fb5276/pywin32-310-cp310-cp310-win32.whl", hash = "sha256:6dd97011efc8bf51d6793a82292419eba2c71cf8e7250cfac03bba284454abc1", size = 8848240, upload-time = "2025-03-17T00:55:46.783Z" }, + { url = "https://files.pythonhosted.org/packages/aa/fe/d873a773324fa565619ba555a82c9dabd677301720f3660a731a5d07e49a/pywin32-310-cp310-cp310-win_amd64.whl", hash = "sha256:c3e78706e4229b915a0821941a84e7ef420bf2b77e08c9dae3c76fd03fd2ae3d", size = 9601854, upload-time = "2025-03-17T00:55:48.783Z" }, + { url = "https://files.pythonhosted.org/packages/3c/84/1a8e3d7a15490d28a5d816efa229ecb4999cdc51a7c30dd8914f669093b8/pywin32-310-cp310-cp310-win_arm64.whl", hash = "sha256:33babed0cf0c92a6f94cc6cc13546ab24ee13e3e800e61ed87609ab91e4c8213", size = 8522963, upload-time = "2025-03-17T00:55:50.969Z" }, + { url = "https://files.pythonhosted.org/packages/f7/b1/68aa2986129fb1011dabbe95f0136f44509afaf072b12b8f815905a39f33/pywin32-310-cp311-cp311-win32.whl", hash = "sha256:1e765f9564e83011a63321bb9d27ec456a0ed90d3732c4b2e312b855365ed8bd", size = 8784284, upload-time = "2025-03-17T00:55:53.124Z" }, + { url = "https://files.pythonhosted.org/packages/b3/bd/d1592635992dd8db5bb8ace0551bc3a769de1ac8850200cfa517e72739fb/pywin32-310-cp311-cp311-win_amd64.whl", hash = "sha256:126298077a9d7c95c53823934f000599f66ec9296b09167810eb24875f32689c", size = 9520748, upload-time = "2025-03-17T00:55:55.203Z" }, + { url = "https://files.pythonhosted.org/packages/90/b1/ac8b1ffce6603849eb45a91cf126c0fa5431f186c2e768bf56889c46f51c/pywin32-310-cp311-cp311-win_arm64.whl", hash = "sha256:19ec5fc9b1d51c4350be7bb00760ffce46e6c95eaf2f0b2f1150657b1a43c582", size = 8455941, upload-time = "2025-03-17T00:55:57.048Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ec/4fdbe47932f671d6e348474ea35ed94227fb5df56a7c30cbbb42cd396ed0/pywin32-310-cp312-cp312-win32.whl", hash = "sha256:8a75a5cc3893e83a108c05d82198880704c44bbaee4d06e442e471d3c9ea4f3d", size = 8796239, upload-time = "2025-03-17T00:55:58.807Z" }, + { url = "https://files.pythonhosted.org/packages/e3/e5/b0627f8bb84e06991bea89ad8153a9e50ace40b2e1195d68e9dff6b03d0f/pywin32-310-cp312-cp312-win_amd64.whl", hash = "sha256:bf5c397c9a9a19a6f62f3fb821fbf36cac08f03770056711f765ec1503972060", size = 9503839, upload-time = "2025-03-17T00:56:00.8Z" }, + { url = "https://files.pythonhosted.org/packages/1f/32/9ccf53748df72301a89713936645a664ec001abd35ecc8578beda593d37d/pywin32-310-cp312-cp312-win_arm64.whl", hash = "sha256:2349cc906eae872d0663d4d6290d13b90621eaf78964bb1578632ff20e152966", size = 8459470, upload-time = "2025-03-17T00:56:02.601Z" }, + { url = "https://files.pythonhosted.org/packages/1c/09/9c1b978ffc4ae53999e89c19c77ba882d9fce476729f23ef55211ea1c034/pywin32-310-cp313-cp313-win32.whl", hash = "sha256:5d241a659c496ada3253cd01cfaa779b048e90ce4b2b38cd44168ad555ce74ab", size = 8794384, upload-time = "2025-03-17T00:56:04.383Z" }, + { url = "https://files.pythonhosted.org/packages/45/3c/b4640f740ffebadd5d34df35fecba0e1cfef8fde9f3e594df91c28ad9b50/pywin32-310-cp313-cp313-win_amd64.whl", hash = "sha256:667827eb3a90208ddbdcc9e860c81bde63a135710e21e4cb3348968e4bd5249e", size = 9503039, upload-time = "2025-03-17T00:56:06.207Z" }, + { url = "https://files.pythonhosted.org/packages/b4/f4/f785020090fb050e7fb6d34b780f2231f302609dc964672f72bfaeb59a28/pywin32-310-cp313-cp313-win_arm64.whl", hash = "sha256:e308f831de771482b7cf692a1f308f8fca701b2d8f9dde6cc440c7da17e47b33", size = 8458152, upload-time = "2025-03-17T00:56:07.819Z" }, + { url = "https://files.pythonhosted.org/packages/a2/cd/d09d434630edb6a0c44ad5079611279a67530296cfe0451e003de7f449ff/pywin32-310-cp39-cp39-win32.whl", hash = "sha256:851c8d927af0d879221e616ae1f66145253537bbdd321a77e8ef701b443a9a1a", size = 8848099, upload-time = "2025-03-17T00:55:42.415Z" }, + { url = "https://files.pythonhosted.org/packages/93/ff/2a8c10315ffbdee7b3883ac0d1667e267ca8b3f6f640d81d43b87a82c0c7/pywin32-310-cp39-cp39-win_amd64.whl", hash = "sha256:96867217335559ac619f00ad70e513c0fcf84b8a3af9fc2bba3b59b97da70475", size = 9602031, upload-time = "2025-03-17T00:55:44.512Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, + { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777, upload-time = "2024-08-06T20:33:25.896Z" }, + { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318, upload-time = "2024-08-06T20:33:27.212Z" }, + { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891, upload-time = "2024-08-06T20:33:28.974Z" }, + { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614, upload-time = "2024-08-06T20:33:34.157Z" }, + { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360, upload-time = "2024-08-06T20:33:35.84Z" }, + { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006, upload-time = "2024-08-06T20:33:37.501Z" }, + { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577, upload-time = "2024-08-06T20:33:39.389Z" }, + { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593, upload-time = "2024-08-06T20:33:46.63Z" }, + { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312, upload-time = "2024-08-06T20:33:49.073Z" }, +] + +[[package]] +name = "referencing" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" }, +] + +[[package]] +name = "requests" +version = "2.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, +] + +[package.optional-dependencies] +socks = [ + { name = "pysocks" }, +] + +[[package]] +name = "roman-numerals-py" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/76/48fd56d17c5bdbdf65609abbc67288728a98ed4c02919428d4f52d23b24b/roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d", size = 9017, upload-time = "2025-02-22T07:34:54.333Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c", size = 7742, upload-time = "2025-02-22T07:34:52.422Z" }, +] + +[[package]] +name = "rpds-py" +version = "0.25.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/a6/60184b7fc00dd3ca80ac635dd5b8577d444c57e8e8742cecabfacb829921/rpds_py-0.25.1.tar.gz", hash = "sha256:8960b6dac09b62dac26e75d7e2c4a22efb835d827a7278c34f72b2b84fa160e3", size = 27304, upload-time = "2025-05-21T12:46:12.502Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/09/e1158988e50905b7f8306487a576b52d32aa9a87f79f7ab24ee8db8b6c05/rpds_py-0.25.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f4ad628b5174d5315761b67f212774a32f5bad5e61396d38108bd801c0a8f5d9", size = 373140, upload-time = "2025-05-21T12:42:38.834Z" }, + { url = "https://files.pythonhosted.org/packages/e0/4b/a284321fb3c45c02fc74187171504702b2934bfe16abab89713eedfe672e/rpds_py-0.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c742af695f7525e559c16f1562cf2323db0e3f0fbdcabdf6865b095256b2d40", size = 358860, upload-time = "2025-05-21T12:42:41.394Z" }, + { url = "https://files.pythonhosted.org/packages/4e/46/8ac9811150c75edeae9fc6fa0e70376c19bc80f8e1f7716981433905912b/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:605ffe7769e24b1800b4d024d24034405d9404f0bc2f55b6db3362cd34145a6f", size = 386179, upload-time = "2025-05-21T12:42:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ec/87eb42d83e859bce91dcf763eb9f2ab117142a49c9c3d17285440edb5b69/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ccc6f3ddef93243538be76f8e47045b4aad7a66a212cd3a0f23e34469473d36b", size = 400282, upload-time = "2025-05-21T12:42:44.92Z" }, + { url = "https://files.pythonhosted.org/packages/68/c8/2a38e0707d7919c8c78e1d582ab15cf1255b380bcb086ca265b73ed6db23/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f70316f760174ca04492b5ab01be631a8ae30cadab1d1081035136ba12738cfa", size = 521824, upload-time = "2025-05-21T12:42:46.856Z" }, + { url = "https://files.pythonhosted.org/packages/5e/2c/6a92790243569784dde84d144bfd12bd45102f4a1c897d76375076d730ab/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1dafef8df605fdb46edcc0bf1573dea0d6d7b01ba87f85cd04dc855b2b4479e", size = 411644, upload-time = "2025-05-21T12:42:48.838Z" }, + { url = "https://files.pythonhosted.org/packages/eb/76/66b523ffc84cf47db56efe13ae7cf368dee2bacdec9d89b9baca5e2e6301/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0701942049095741a8aeb298a31b203e735d1c61f4423511d2b1a41dcd8a16da", size = 386955, upload-time = "2025-05-21T12:42:50.835Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b9/a362d7522feaa24dc2b79847c6175daa1c642817f4a19dcd5c91d3e2c316/rpds_py-0.25.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e87798852ae0b37c88babb7f7bbbb3e3fecc562a1c340195b44c7e24d403e380", size = 421039, upload-time = "2025-05-21T12:42:52.348Z" }, + { url = "https://files.pythonhosted.org/packages/0f/c4/b5b6f70b4d719b6584716889fd3413102acf9729540ee76708d56a76fa97/rpds_py-0.25.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3bcce0edc1488906c2d4c75c94c70a0417e83920dd4c88fec1078c94843a6ce9", size = 563290, upload-time = "2025-05-21T12:42:54.404Z" }, + { url = "https://files.pythonhosted.org/packages/87/a3/2e6e816615c12a8f8662c9d8583a12eb54c52557521ef218cbe3095a8afa/rpds_py-0.25.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e2f6a2347d3440ae789505693a02836383426249d5293541cd712e07e7aecf54", size = 592089, upload-time = "2025-05-21T12:42:55.976Z" }, + { url = "https://files.pythonhosted.org/packages/c0/08/9b8e1050e36ce266135994e2c7ec06e1841f1c64da739daeb8afe9cb77a4/rpds_py-0.25.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4fd52d3455a0aa997734f3835cbc4c9f32571345143960e7d7ebfe7b5fbfa3b2", size = 558400, upload-time = "2025-05-21T12:42:58.032Z" }, + { url = "https://files.pythonhosted.org/packages/f2/df/b40b8215560b8584baccd839ff5c1056f3c57120d79ac41bd26df196da7e/rpds_py-0.25.1-cp310-cp310-win32.whl", hash = "sha256:3f0b1798cae2bbbc9b9db44ee068c556d4737911ad53a4e5093d09d04b3bbc24", size = 219741, upload-time = "2025-05-21T12:42:59.479Z" }, + { url = "https://files.pythonhosted.org/packages/10/99/e4c58be18cf5d8b40b8acb4122bc895486230b08f978831b16a3916bd24d/rpds_py-0.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:3ebd879ab996537fc510a2be58c59915b5dd63bccb06d1ef514fee787e05984a", size = 231553, upload-time = "2025-05-21T12:43:01.425Z" }, + { url = "https://files.pythonhosted.org/packages/95/e1/df13fe3ddbbea43567e07437f097863b20c99318ae1f58a0fe389f763738/rpds_py-0.25.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5f048bbf18b1f9120685c6d6bb70cc1a52c8cc11bdd04e643d28d3be0baf666d", size = 373341, upload-time = "2025-05-21T12:43:02.978Z" }, + { url = "https://files.pythonhosted.org/packages/7a/58/deef4d30fcbcbfef3b6d82d17c64490d5c94585a2310544ce8e2d3024f83/rpds_py-0.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fbb0dbba559959fcb5d0735a0f87cdbca9e95dac87982e9b95c0f8f7ad10255", size = 359111, upload-time = "2025-05-21T12:43:05.128Z" }, + { url = "https://files.pythonhosted.org/packages/bb/7e/39f1f4431b03e96ebaf159e29a0f82a77259d8f38b2dd474721eb3a8ac9b/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4ca54b9cf9d80b4016a67a0193ebe0bcf29f6b0a96f09db942087e294d3d4c2", size = 386112, upload-time = "2025-05-21T12:43:07.13Z" }, + { url = "https://files.pythonhosted.org/packages/db/e7/847068a48d63aec2ae695a1646089620b3b03f8ccf9f02c122ebaf778f3c/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ee3e26eb83d39b886d2cb6e06ea701bba82ef30a0de044d34626ede51ec98b0", size = 400362, upload-time = "2025-05-21T12:43:08.693Z" }, + { url = "https://files.pythonhosted.org/packages/3b/3d/9441d5db4343d0cee759a7ab4d67420a476cebb032081763de934719727b/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89706d0683c73a26f76a5315d893c051324d771196ae8b13e6ffa1ffaf5e574f", size = 522214, upload-time = "2025-05-21T12:43:10.694Z" }, + { url = "https://files.pythonhosted.org/packages/a2/ec/2cc5b30d95f9f1a432c79c7a2f65d85e52812a8f6cbf8768724571710786/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2013ee878c76269c7b557a9a9c042335d732e89d482606990b70a839635feb7", size = 411491, upload-time = "2025-05-21T12:43:12.739Z" }, + { url = "https://files.pythonhosted.org/packages/dc/6c/44695c1f035077a017dd472b6a3253553780837af2fac9b6ac25f6a5cb4d/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45e484db65e5380804afbec784522de84fa95e6bb92ef1bd3325d33d13efaebd", size = 386978, upload-time = "2025-05-21T12:43:14.25Z" }, + { url = "https://files.pythonhosted.org/packages/b1/74/b4357090bb1096db5392157b4e7ed8bb2417dc7799200fcbaee633a032c9/rpds_py-0.25.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:48d64155d02127c249695abb87d39f0faf410733428d499867606be138161d65", size = 420662, upload-time = "2025-05-21T12:43:15.8Z" }, + { url = "https://files.pythonhosted.org/packages/26/dd/8cadbebf47b96e59dfe8b35868e5c38a42272699324e95ed522da09d3a40/rpds_py-0.25.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:048893e902132fd6548a2e661fb38bf4896a89eea95ac5816cf443524a85556f", size = 563385, upload-time = "2025-05-21T12:43:17.78Z" }, + { url = "https://files.pythonhosted.org/packages/c3/ea/92960bb7f0e7a57a5ab233662f12152085c7dc0d5468534c65991a3d48c9/rpds_py-0.25.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0317177b1e8691ab5879f4f33f4b6dc55ad3b344399e23df2e499de7b10a548d", size = 592047, upload-time = "2025-05-21T12:43:19.457Z" }, + { url = "https://files.pythonhosted.org/packages/61/ad/71aabc93df0d05dabcb4b0c749277881f8e74548582d96aa1bf24379493a/rpds_py-0.25.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bffcf57826d77a4151962bf1701374e0fc87f536e56ec46f1abdd6a903354042", size = 557863, upload-time = "2025-05-21T12:43:21.69Z" }, + { url = "https://files.pythonhosted.org/packages/93/0f/89df0067c41f122b90b76f3660028a466eb287cbe38efec3ea70e637ca78/rpds_py-0.25.1-cp311-cp311-win32.whl", hash = "sha256:cda776f1967cb304816173b30994faaf2fd5bcb37e73118a47964a02c348e1bc", size = 219627, upload-time = "2025-05-21T12:43:23.311Z" }, + { url = "https://files.pythonhosted.org/packages/7c/8d/93b1a4c1baa903d0229374d9e7aa3466d751f1d65e268c52e6039c6e338e/rpds_py-0.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:dc3c1ff0abc91444cd20ec643d0f805df9a3661fcacf9c95000329f3ddf268a4", size = 231603, upload-time = "2025-05-21T12:43:25.145Z" }, + { url = "https://files.pythonhosted.org/packages/cb/11/392605e5247bead2f23e6888e77229fbd714ac241ebbebb39a1e822c8815/rpds_py-0.25.1-cp311-cp311-win_arm64.whl", hash = "sha256:5a3ddb74b0985c4387719fc536faced33cadf2172769540c62e2a94b7b9be1c4", size = 223967, upload-time = "2025-05-21T12:43:26.566Z" }, + { url = "https://files.pythonhosted.org/packages/7f/81/28ab0408391b1dc57393653b6a0cf2014cc282cc2909e4615e63e58262be/rpds_py-0.25.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b5ffe453cde61f73fea9430223c81d29e2fbf412a6073951102146c84e19e34c", size = 364647, upload-time = "2025-05-21T12:43:28.559Z" }, + { url = "https://files.pythonhosted.org/packages/2c/9a/7797f04cad0d5e56310e1238434f71fc6939d0bc517192a18bb99a72a95f/rpds_py-0.25.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:115874ae5e2fdcfc16b2aedc95b5eef4aebe91b28e7e21951eda8a5dc0d3461b", size = 350454, upload-time = "2025-05-21T12:43:30.615Z" }, + { url = "https://files.pythonhosted.org/packages/69/3c/93d2ef941b04898011e5d6eaa56a1acf46a3b4c9f4b3ad1bbcbafa0bee1f/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a714bf6e5e81b0e570d01f56e0c89c6375101b8463999ead3a93a5d2a4af91fa", size = 389665, upload-time = "2025-05-21T12:43:32.629Z" }, + { url = "https://files.pythonhosted.org/packages/c1/57/ad0e31e928751dde8903a11102559628d24173428a0f85e25e187defb2c1/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:35634369325906bcd01577da4c19e3b9541a15e99f31e91a02d010816b49bfda", size = 403873, upload-time = "2025-05-21T12:43:34.576Z" }, + { url = "https://files.pythonhosted.org/packages/16/ad/c0c652fa9bba778b4f54980a02962748479dc09632e1fd34e5282cf2556c/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4cb2b3ddc16710548801c6fcc0cfcdeeff9dafbc983f77265877793f2660309", size = 525866, upload-time = "2025-05-21T12:43:36.123Z" }, + { url = "https://files.pythonhosted.org/packages/2a/39/3e1839bc527e6fcf48d5fec4770070f872cdee6c6fbc9b259932f4e88a38/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9ceca1cf097ed77e1a51f1dbc8d174d10cb5931c188a4505ff9f3e119dfe519b", size = 416886, upload-time = "2025-05-21T12:43:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/7a/95/dd6b91cd4560da41df9d7030a038298a67d24f8ca38e150562644c829c48/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c2cd1a4b0c2b8c5e31ffff50d09f39906fe351389ba143c195566056c13a7ea", size = 390666, upload-time = "2025-05-21T12:43:40.065Z" }, + { url = "https://files.pythonhosted.org/packages/64/48/1be88a820e7494ce0a15c2d390ccb7c52212370badabf128e6a7bb4cb802/rpds_py-0.25.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1de336a4b164c9188cb23f3703adb74a7623ab32d20090d0e9bf499a2203ad65", size = 425109, upload-time = "2025-05-21T12:43:42.263Z" }, + { url = "https://files.pythonhosted.org/packages/cf/07/3e2a17927ef6d7720b9949ec1b37d1e963b829ad0387f7af18d923d5cfa5/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9fca84a15333e925dd59ce01da0ffe2ffe0d6e5d29a9eeba2148916d1824948c", size = 567244, upload-time = "2025-05-21T12:43:43.846Z" }, + { url = "https://files.pythonhosted.org/packages/d2/e5/76cf010998deccc4f95305d827847e2eae9c568099c06b405cf96384762b/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:88ec04afe0c59fa64e2f6ea0dd9657e04fc83e38de90f6de201954b4d4eb59bd", size = 596023, upload-time = "2025-05-21T12:43:45.932Z" }, + { url = "https://files.pythonhosted.org/packages/52/9a/df55efd84403736ba37a5a6377b70aad0fd1cb469a9109ee8a1e21299a1c/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8bd2f19e312ce3e1d2c635618e8a8d8132892bb746a7cf74780a489f0f6cdcb", size = 561634, upload-time = "2025-05-21T12:43:48.263Z" }, + { url = "https://files.pythonhosted.org/packages/ab/aa/dc3620dd8db84454aaf9374bd318f1aa02578bba5e567f5bf6b79492aca4/rpds_py-0.25.1-cp312-cp312-win32.whl", hash = "sha256:e5e2f7280d8d0d3ef06f3ec1b4fd598d386cc6f0721e54f09109a8132182fbfe", size = 222713, upload-time = "2025-05-21T12:43:49.897Z" }, + { url = "https://files.pythonhosted.org/packages/a3/7f/7cef485269a50ed5b4e9bae145f512d2a111ca638ae70cc101f661b4defd/rpds_py-0.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:db58483f71c5db67d643857404da360dce3573031586034b7d59f245144cc192", size = 235280, upload-time = "2025-05-21T12:43:51.893Z" }, + { url = "https://files.pythonhosted.org/packages/99/f2/c2d64f6564f32af913bf5f3f7ae41c7c263c5ae4c4e8f1a17af8af66cd46/rpds_py-0.25.1-cp312-cp312-win_arm64.whl", hash = "sha256:6d50841c425d16faf3206ddbba44c21aa3310a0cebc3c1cdfc3e3f4f9f6f5728", size = 225399, upload-time = "2025-05-21T12:43:53.351Z" }, + { url = "https://files.pythonhosted.org/packages/2b/da/323848a2b62abe6a0fec16ebe199dc6889c5d0a332458da8985b2980dffe/rpds_py-0.25.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:659d87430a8c8c704d52d094f5ba6fa72ef13b4d385b7e542a08fc240cb4a559", size = 364498, upload-time = "2025-05-21T12:43:54.841Z" }, + { url = "https://files.pythonhosted.org/packages/1f/b4/4d3820f731c80fd0cd823b3e95b9963fec681ae45ba35b5281a42382c67d/rpds_py-0.25.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68f6f060f0bbdfb0245267da014d3a6da9be127fe3e8cc4a68c6f833f8a23bb1", size = 350083, upload-time = "2025-05-21T12:43:56.428Z" }, + { url = "https://files.pythonhosted.org/packages/d5/b1/3a8ee1c9d480e8493619a437dec685d005f706b69253286f50f498cbdbcf/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:083a9513a33e0b92cf6e7a6366036c6bb43ea595332c1ab5c8ae329e4bcc0a9c", size = 389023, upload-time = "2025-05-21T12:43:57.995Z" }, + { url = "https://files.pythonhosted.org/packages/3b/31/17293edcfc934dc62c3bf74a0cb449ecd549531f956b72287203e6880b87/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:816568614ecb22b18a010c7a12559c19f6fe993526af88e95a76d5a60b8b75fb", size = 403283, upload-time = "2025-05-21T12:43:59.546Z" }, + { url = "https://files.pythonhosted.org/packages/d1/ca/e0f0bc1a75a8925024f343258c8ecbd8828f8997ea2ac71e02f67b6f5299/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c6564c0947a7f52e4792983f8e6cf9bac140438ebf81f527a21d944f2fd0a40", size = 524634, upload-time = "2025-05-21T12:44:01.087Z" }, + { url = "https://files.pythonhosted.org/packages/3e/03/5d0be919037178fff33a6672ffc0afa04ea1cfcb61afd4119d1b5280ff0f/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c4a128527fe415d73cf1f70a9a688d06130d5810be69f3b553bf7b45e8acf79", size = 416233, upload-time = "2025-05-21T12:44:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/05/7c/8abb70f9017a231c6c961a8941403ed6557664c0913e1bf413cbdc039e75/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a49e1d7a4978ed554f095430b89ecc23f42014a50ac385eb0c4d163ce213c325", size = 390375, upload-time = "2025-05-21T12:44:04.162Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ac/a87f339f0e066b9535074a9f403b9313fd3892d4a164d5d5f5875ac9f29f/rpds_py-0.25.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d74ec9bc0e2feb81d3f16946b005748119c0f52a153f6db6a29e8cd68636f295", size = 424537, upload-time = "2025-05-21T12:44:06.175Z" }, + { url = "https://files.pythonhosted.org/packages/1f/8f/8d5c1567eaf8c8afe98a838dd24de5013ce6e8f53a01bd47fe8bb06b5533/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3af5b4cc10fa41e5bc64e5c198a1b2d2864337f8fcbb9a67e747e34002ce812b", size = 566425, upload-time = "2025-05-21T12:44:08.242Z" }, + { url = "https://files.pythonhosted.org/packages/95/33/03016a6be5663b389c8ab0bbbcca68d9e96af14faeff0a04affcb587e776/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:79dc317a5f1c51fd9c6a0c4f48209c6b8526d0524a6904fc1076476e79b00f98", size = 595197, upload-time = "2025-05-21T12:44:10.449Z" }, + { url = "https://files.pythonhosted.org/packages/33/8d/da9f4d3e208c82fda311bff0cf0a19579afceb77cf456e46c559a1c075ba/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1521031351865e0181bc585147624d66b3b00a84109b57fcb7a779c3ec3772cd", size = 561244, upload-time = "2025-05-21T12:44:12.387Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b3/39d5dcf7c5f742ecd6dbc88f6f84ae54184b92f5f387a4053be2107b17f1/rpds_py-0.25.1-cp313-cp313-win32.whl", hash = "sha256:5d473be2b13600b93a5675d78f59e63b51b1ba2d0476893415dfbb5477e65b31", size = 222254, upload-time = "2025-05-21T12:44:14.261Z" }, + { url = "https://files.pythonhosted.org/packages/5f/19/2d6772c8eeb8302c5f834e6d0dfd83935a884e7c5ce16340c7eaf89ce925/rpds_py-0.25.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7b74e92a3b212390bdce1d93da9f6488c3878c1d434c5e751cbc202c5e09500", size = 234741, upload-time = "2025-05-21T12:44:16.236Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/145ada26cfaf86018d0eb304fe55eafdd4f0b6b84530246bb4a7c4fb5c4b/rpds_py-0.25.1-cp313-cp313-win_arm64.whl", hash = "sha256:dd326a81afe332ede08eb39ab75b301d5676802cdffd3a8f287a5f0b694dc3f5", size = 224830, upload-time = "2025-05-21T12:44:17.749Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ca/d435844829c384fd2c22754ff65889c5c556a675d2ed9eb0e148435c6690/rpds_py-0.25.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:a58d1ed49a94d4183483a3ce0af22f20318d4a1434acee255d683ad90bf78129", size = 359668, upload-time = "2025-05-21T12:44:19.322Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/b056f21db3a09f89410d493d2f6614d87bb162499f98b649d1dbd2a81988/rpds_py-0.25.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f251bf23deb8332823aef1da169d5d89fa84c89f67bdfb566c49dea1fccfd50d", size = 345649, upload-time = "2025-05-21T12:44:20.962Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0f/e0d00dc991e3d40e03ca36383b44995126c36b3eafa0ccbbd19664709c88/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dbd586bfa270c1103ece2109314dd423df1fa3d9719928b5d09e4840cec0d72", size = 384776, upload-time = "2025-05-21T12:44:22.516Z" }, + { url = "https://files.pythonhosted.org/packages/9f/a2/59374837f105f2ca79bde3c3cd1065b2f8c01678900924949f6392eab66d/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6d273f136e912aa101a9274c3145dcbddbe4bac560e77e6d5b3c9f6e0ed06d34", size = 395131, upload-time = "2025-05-21T12:44:24.147Z" }, + { url = "https://files.pythonhosted.org/packages/9c/dc/48e8d84887627a0fe0bac53f0b4631e90976fd5d35fff8be66b8e4f3916b/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:666fa7b1bd0a3810a7f18f6d3a25ccd8866291fbbc3c9b912b917a6715874bb9", size = 520942, upload-time = "2025-05-21T12:44:25.915Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f5/ee056966aeae401913d37befeeab57a4a43a4f00099e0a20297f17b8f00c/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:921954d7fbf3fccc7de8f717799304b14b6d9a45bbeec5a8d7408ccbf531faf5", size = 411330, upload-time = "2025-05-21T12:44:27.638Z" }, + { url = "https://files.pythonhosted.org/packages/ab/74/b2cffb46a097cefe5d17f94ede7a174184b9d158a0aeb195f39f2c0361e8/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3d86373ff19ca0441ebeb696ef64cb58b8b5cbacffcda5a0ec2f3911732a194", size = 387339, upload-time = "2025-05-21T12:44:29.292Z" }, + { url = "https://files.pythonhosted.org/packages/7f/9a/0ff0b375dcb5161c2b7054e7d0b7575f1680127505945f5cabaac890bc07/rpds_py-0.25.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c8980cde3bb8575e7c956a530f2c217c1d6aac453474bf3ea0f9c89868b531b6", size = 418077, upload-time = "2025-05-21T12:44:30.877Z" }, + { url = "https://files.pythonhosted.org/packages/0d/a1/fda629bf20d6b698ae84c7c840cfb0e9e4200f664fc96e1f456f00e4ad6e/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8eb8c84ecea987a2523e057c0d950bcb3f789696c0499290b8d7b3107a719d78", size = 562441, upload-time = "2025-05-21T12:44:32.541Z" }, + { url = "https://files.pythonhosted.org/packages/20/15/ce4b5257f654132f326f4acd87268e1006cc071e2c59794c5bdf4bebbb51/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:e43a005671a9ed5a650f3bc39e4dbccd6d4326b24fb5ea8be5f3a43a6f576c72", size = 590750, upload-time = "2025-05-21T12:44:34.557Z" }, + { url = "https://files.pythonhosted.org/packages/fb/ab/e04bf58a8d375aeedb5268edcc835c6a660ebf79d4384d8e0889439448b0/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58f77c60956501a4a627749a6dcb78dac522f249dd96b5c9f1c6af29bfacfb66", size = 558891, upload-time = "2025-05-21T12:44:37.358Z" }, + { url = "https://files.pythonhosted.org/packages/90/82/cb8c6028a6ef6cd2b7991e2e4ced01c854b6236ecf51e81b64b569c43d73/rpds_py-0.25.1-cp313-cp313t-win32.whl", hash = "sha256:2cb9e5b5e26fc02c8a4345048cd9998c2aca7c2712bd1b36da0c72ee969a3523", size = 218718, upload-time = "2025-05-21T12:44:38.969Z" }, + { url = "https://files.pythonhosted.org/packages/b6/97/5a4b59697111c89477d20ba8a44df9ca16b41e737fa569d5ae8bff99e650/rpds_py-0.25.1-cp313-cp313t-win_amd64.whl", hash = "sha256:401ca1c4a20cc0510d3435d89c069fe0a9ae2ee6495135ac46bdd49ec0495763", size = 232218, upload-time = "2025-05-21T12:44:40.512Z" }, + { url = "https://files.pythonhosted.org/packages/89/74/716d42058ef501e2c08f27aa3ff455f6fc1bbbd19a6ab8dea07e6322d217/rpds_py-0.25.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ce4c8e485a3c59593f1a6f683cf0ea5ab1c1dc94d11eea5619e4fb5228b40fbd", size = 373475, upload-time = "2025-05-21T12:44:42.136Z" }, + { url = "https://files.pythonhosted.org/packages/e1/21/3faa9c523e2496a2505d7440b6f24c9166f37cb7ac027cac6cfbda9b4b5f/rpds_py-0.25.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d8222acdb51a22929c3b2ddb236b69c59c72af4019d2cba961e2f9add9b6e634", size = 359349, upload-time = "2025-05-21T12:44:43.813Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1c/c747fe568d21b1d679079b52b926ebc4d1497457510a1773dc5fd4b7b4e2/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4593c4eae9b27d22df41cde518b4b9e4464d139e4322e2127daa9b5b981b76be", size = 386526, upload-time = "2025-05-21T12:44:45.452Z" }, + { url = "https://files.pythonhosted.org/packages/0b/cc/4a41703de4fb291f13660fa3d882cbd39db5d60497c6e7fa7f5142e5e69f/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd035756830c712b64725a76327ce80e82ed12ebab361d3a1cdc0f51ea21acb0", size = 400526, upload-time = "2025-05-21T12:44:47.011Z" }, + { url = "https://files.pythonhosted.org/packages/f1/78/60c980bedcad8418b614f0b4d6d420ecf11225b579cec0cb4e84d168b4da/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:114a07e85f32b125404f28f2ed0ba431685151c037a26032b213c882f26eb908", size = 525726, upload-time = "2025-05-21T12:44:48.838Z" }, + { url = "https://files.pythonhosted.org/packages/3f/37/f2f36b7f1314b3c3200d663decf2f8e29480492a39ab22447112aead4693/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dec21e02e6cc932538b5203d3a8bd6aa1480c98c4914cb88eea064ecdbc6396a", size = 412045, upload-time = "2025-05-21T12:44:50.433Z" }, + { url = "https://files.pythonhosted.org/packages/df/96/e03783e87a775b1242477ccbc35895f8e9b2bbdb60e199034a6da03c2687/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09eab132f41bf792c7a0ea1578e55df3f3e7f61888e340779b06050a9a3f16e9", size = 386953, upload-time = "2025-05-21T12:44:52.092Z" }, + { url = "https://files.pythonhosted.org/packages/7c/7d/1418f4b69bfb4b40481a3d84782113ad7d4cca0b38ae70b982dd5b20102a/rpds_py-0.25.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c98f126c4fc697b84c423e387337d5b07e4a61e9feac494362a59fd7a2d9ed80", size = 421144, upload-time = "2025-05-21T12:44:53.734Z" }, + { url = "https://files.pythonhosted.org/packages/b3/0e/61469912c6493ee3808012e60f4930344b974fcb6b35c4348e70b6be7bc7/rpds_py-0.25.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0e6a327af8ebf6baba1c10fadd04964c1965d375d318f4435d5f3f9651550f4a", size = 563730, upload-time = "2025-05-21T12:44:55.846Z" }, + { url = "https://files.pythonhosted.org/packages/f6/86/6d0a5cc56481ac61977b7c839677ed5c63d38cf0fcb3e2280843a8a6f476/rpds_py-0.25.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bc120d1132cff853ff617754196d0ac0ae63befe7c8498bd67731ba368abe451", size = 592321, upload-time = "2025-05-21T12:44:57.514Z" }, + { url = "https://files.pythonhosted.org/packages/5d/87/d1e2453fe336f71e6aa296452a8c85c2118b587b1d25ce98014f75838a60/rpds_py-0.25.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:140f61d9bed7839446bdd44852e30195c8e520f81329b4201ceead4d64eb3a9f", size = 558162, upload-time = "2025-05-21T12:44:59.564Z" }, + { url = "https://files.pythonhosted.org/packages/ad/92/349f04b1644c5cef3e2e6c53b7168a28531945f9e6fca7425f6d20ddbc3c/rpds_py-0.25.1-cp39-cp39-win32.whl", hash = "sha256:9c006f3aadeda131b438c3092124bd196b66312f0caa5823ef09585a669cf449", size = 219920, upload-time = "2025-05-21T12:45:01.186Z" }, + { url = "https://files.pythonhosted.org/packages/f2/84/3969bef883a3f37ff2213795257cb7b7e93a115829670befb8de0e003031/rpds_py-0.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:a61d0b2c7c9a0ae45732a77844917b427ff16ad5464b4d4f5e4adb955f582890", size = 231452, upload-time = "2025-05-21T12:45:02.85Z" }, + { url = "https://files.pythonhosted.org/packages/78/ff/566ce53529b12b4f10c0a348d316bd766970b7060b4fd50f888be3b3b281/rpds_py-0.25.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b24bf3cd93d5b6ecfbedec73b15f143596c88ee249fa98cefa9a9dc9d92c6f28", size = 373931, upload-time = "2025-05-21T12:45:05.01Z" }, + { url = "https://files.pythonhosted.org/packages/83/5d/deba18503f7c7878e26aa696e97f051175788e19d5336b3b0e76d3ef9256/rpds_py-0.25.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:0eb90e94f43e5085623932b68840b6f379f26db7b5c2e6bcef3179bd83c9330f", size = 359074, upload-time = "2025-05-21T12:45:06.714Z" }, + { url = "https://files.pythonhosted.org/packages/0d/74/313415c5627644eb114df49c56a27edba4d40cfd7c92bd90212b3604ca84/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d50e4864498a9ab639d6d8854b25e80642bd362ff104312d9770b05d66e5fb13", size = 387255, upload-time = "2025-05-21T12:45:08.669Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c8/c723298ed6338963d94e05c0f12793acc9b91d04ed7c4ba7508e534b7385/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c9409b47ba0650544b0bb3c188243b83654dfe55dcc173a86832314e1a6a35d", size = 400714, upload-time = "2025-05-21T12:45:10.39Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/51f1f6aa653c2e110ed482ef2ae94140d56c910378752a1b483af11019ee/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:796ad874c89127c91970652a4ee8b00d56368b7e00d3477f4415fe78164c8000", size = 523105, upload-time = "2025-05-21T12:45:12.273Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a4/7873d15c088ad3bff36910b29ceb0f178e4b3232c2adbe9198de68a41e63/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:85608eb70a659bf4c1142b2781083d4b7c0c4e2c90eff11856a9754e965b2540", size = 411499, upload-time = "2025-05-21T12:45:13.95Z" }, + { url = "https://files.pythonhosted.org/packages/90/f3/0ce1437befe1410766d11d08239333ac1b2d940f8a64234ce48a7714669c/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4feb9211d15d9160bc85fa72fed46432cdc143eb9cf6d5ca377335a921ac37b", size = 387918, upload-time = "2025-05-21T12:45:15.649Z" }, + { url = "https://files.pythonhosted.org/packages/94/d4/5551247988b2a3566afb8a9dba3f1d4a3eea47793fd83000276c1a6c726e/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ccfa689b9246c48947d31dd9d8b16d89a0ecc8e0e26ea5253068efb6c542b76e", size = 421705, upload-time = "2025-05-21T12:45:17.788Z" }, + { url = "https://files.pythonhosted.org/packages/b0/25/5960f28f847bf736cc7ee3c545a7e1d2f3b5edaf82c96fb616c2f5ed52d0/rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:3c5b317ecbd8226887994852e85de562f7177add602514d4ac40f87de3ae45a8", size = 564489, upload-time = "2025-05-21T12:45:19.466Z" }, + { url = "https://files.pythonhosted.org/packages/02/66/1c99884a0d44e8c2904d3c4ec302f995292d5dde892c3bf7685ac1930146/rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:454601988aab2c6e8fd49e7634c65476b2b919647626208e376afcd22019eeb8", size = 592557, upload-time = "2025-05-21T12:45:21.362Z" }, + { url = "https://files.pythonhosted.org/packages/55/ae/4aeac84ebeffeac14abb05b3bb1d2f728d00adb55d3fb7b51c9fa772e760/rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:1c0c434a53714358532d13539272db75a5ed9df75a4a090a753ac7173ec14e11", size = 558691, upload-time = "2025-05-21T12:45:23.084Z" }, + { url = "https://files.pythonhosted.org/packages/41/b3/728a08ff6f5e06fe3bb9af2e770e9d5fd20141af45cff8dfc62da4b2d0b3/rpds_py-0.25.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f73ce1512e04fbe2bc97836e89830d6b4314c171587a99688082d090f934d20a", size = 231651, upload-time = "2025-05-21T12:45:24.72Z" }, + { url = "https://files.pythonhosted.org/packages/49/74/48f3df0715a585cbf5d34919c9c757a4c92c1a9eba059f2d334e72471f70/rpds_py-0.25.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ee86d81551ec68a5c25373c5643d343150cc54672b5e9a0cafc93c1870a53954", size = 374208, upload-time = "2025-05-21T12:45:26.306Z" }, + { url = "https://files.pythonhosted.org/packages/55/b0/9b01bb11ce01ec03d05e627249cc2c06039d6aa24ea5a22a39c312167c10/rpds_py-0.25.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89c24300cd4a8e4a51e55c31a8ff3918e6651b241ee8876a42cc2b2a078533ba", size = 359262, upload-time = "2025-05-21T12:45:28.322Z" }, + { url = "https://files.pythonhosted.org/packages/a9/eb/5395621618f723ebd5116c53282052943a726dba111b49cd2071f785b665/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:771c16060ff4e79584dc48902a91ba79fd93eade3aa3a12d6d2a4aadaf7d542b", size = 387366, upload-time = "2025-05-21T12:45:30.42Z" }, + { url = "https://files.pythonhosted.org/packages/68/73/3d51442bdb246db619d75039a50ea1cf8b5b4ee250c3e5cd5c3af5981cd4/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:785ffacd0ee61c3e60bdfde93baa6d7c10d86f15655bd706c89da08068dc5038", size = 400759, upload-time = "2025-05-21T12:45:32.516Z" }, + { url = "https://files.pythonhosted.org/packages/b7/4c/3a32d5955d7e6cb117314597bc0f2224efc798428318b13073efe306512a/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a40046a529cc15cef88ac5ab589f83f739e2d332cb4d7399072242400ed68c9", size = 523128, upload-time = "2025-05-21T12:45:34.396Z" }, + { url = "https://files.pythonhosted.org/packages/be/95/1ffccd3b0bb901ae60b1dd4b1be2ab98bb4eb834cd9b15199888f5702f7b/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:85fc223d9c76cabe5d0bff82214459189720dc135db45f9f66aa7cffbf9ff6c1", size = 411597, upload-time = "2025-05-21T12:45:36.164Z" }, + { url = "https://files.pythonhosted.org/packages/ef/6d/6e6cd310180689db8b0d2de7f7d1eabf3fb013f239e156ae0d5a1a85c27f/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0be9965f93c222fb9b4cc254235b3b2b215796c03ef5ee64f995b1b69af0762", size = 388053, upload-time = "2025-05-21T12:45:38.45Z" }, + { url = "https://files.pythonhosted.org/packages/4a/87/ec4186b1fe6365ced6fa470960e68fc7804bafbe7c0cf5a36237aa240efa/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8378fa4a940f3fb509c081e06cb7f7f2adae8cf46ef258b0e0ed7519facd573e", size = 421821, upload-time = "2025-05-21T12:45:40.732Z" }, + { url = "https://files.pythonhosted.org/packages/7a/60/84f821f6bf4e0e710acc5039d91f8f594fae0d93fc368704920d8971680d/rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:33358883a4490287e67a2c391dfaea4d9359860281db3292b6886bf0be3d8692", size = 564534, upload-time = "2025-05-21T12:45:42.672Z" }, + { url = "https://files.pythonhosted.org/packages/41/3a/bc654eb15d3b38f9330fe0f545016ba154d89cdabc6177b0295910cd0ebe/rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1d1fadd539298e70cac2f2cb36f5b8a65f742b9b9f1014dd4ea1f7785e2470bf", size = 592674, upload-time = "2025-05-21T12:45:44.533Z" }, + { url = "https://files.pythonhosted.org/packages/2e/ba/31239736f29e4dfc7a58a45955c5db852864c306131fd6320aea214d5437/rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a46c2fb2545e21181445515960006e85d22025bd2fe6db23e76daec6eb689fe", size = 558781, upload-time = "2025-05-21T12:45:46.281Z" }, + { url = "https://files.pythonhosted.org/packages/78/b2/198266f070c6760e0e8cd00f9f2b9c86133ceebbe7c6d114bdcfea200180/rpds_py-0.25.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:50f2c501a89c9a5f4e454b126193c5495b9fb441a75b298c60591d8a2eb92e1b", size = 373973, upload-time = "2025-05-21T12:45:48.081Z" }, + { url = "https://files.pythonhosted.org/packages/13/79/1265eae618f88aa5d5e7122bd32dd41700bafe5a8bcea404e998848cd844/rpds_py-0.25.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d779b325cc8238227c47fbc53964c8cc9a941d5dbae87aa007a1f08f2f77b23", size = 359326, upload-time = "2025-05-21T12:45:49.825Z" }, + { url = "https://files.pythonhosted.org/packages/30/ab/6913b96f3ac072e87e76e45fe938263b0ab0d78b6b2cef3f2e56067befc0/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:036ded36bedb727beeabc16dc1dad7cb154b3fa444e936a03b67a86dc6a5066e", size = 387544, upload-time = "2025-05-21T12:45:51.764Z" }, + { url = "https://files.pythonhosted.org/packages/b0/23/129ed12d25229acc6deb8cbe90baadd8762e563c267c9594eb2fcc15be0c/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:245550f5a1ac98504147cba96ffec8fabc22b610742e9150138e5d60774686d7", size = 400240, upload-time = "2025-05-21T12:45:54.061Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e0/6811a38a5efa46b7ee6ed2103c95cb9abb16991544c3b69007aa679b6944/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff7c23ba0a88cb7b104281a99476cccadf29de2a0ef5ce864959a52675b1ca83", size = 525599, upload-time = "2025-05-21T12:45:56.457Z" }, + { url = "https://files.pythonhosted.org/packages/6c/10/2dc88bcaa0d86bdb59e017a330b1972ffeeb7f5061bb5a180c9a2bb73bbf/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e37caa8cdb3b7cf24786451a0bdb853f6347b8b92005eeb64225ae1db54d1c2b", size = 411154, upload-time = "2025-05-21T12:45:58.525Z" }, + { url = "https://files.pythonhosted.org/packages/cf/d1/a72d522eb7d934fb33e9c501e6ecae00e2035af924d4ff37d964e9a3959b/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2f48ab00181600ee266a095fe815134eb456163f7d6699f525dee471f312cf", size = 388297, upload-time = "2025-05-21T12:46:00.264Z" }, + { url = "https://files.pythonhosted.org/packages/55/90/0dd7169ec74f042405b6b73512200d637a3088c156f64e1c07c18aa2fe59/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e5fc7484fa7dce57e25063b0ec9638ff02a908304f861d81ea49273e43838c1", size = 421894, upload-time = "2025-05-21T12:46:02.065Z" }, + { url = "https://files.pythonhosted.org/packages/37/e9/45170894add451783ed839c5c4a495e050aa8baa06d720364d9dff394dac/rpds_py-0.25.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d3c10228d6cf6fe2b63d2e7985e94f6916fa46940df46b70449e9ff9297bd3d1", size = 564409, upload-time = "2025-05-21T12:46:03.891Z" }, + { url = "https://files.pythonhosted.org/packages/59/d0/31cece9090e76fbdb50c758c165d40da604b03b37c3ba53f010bbfeb130a/rpds_py-0.25.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:5d9e40f32745db28c1ef7aad23f6fc458dc1e29945bd6781060f0d15628b8ddf", size = 592681, upload-time = "2025-05-21T12:46:06.009Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4c/22ef535efb2beec614ba7be83e62b439eb83b0b0d7b1775e22d35af3f9b5/rpds_py-0.25.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:35a8d1a24b5936b35c5003313bc177403d8bdef0f8b24f28b1c4a255f94ea992", size = 558744, upload-time = "2025-05-21T12:46:07.78Z" }, + { url = "https://files.pythonhosted.org/packages/79/ff/f2150efc8daf0581d4dfaf0a2a30b08088b6df900230ee5ae4f7c8cd5163/rpds_py-0.25.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:6099263f526efff9cf3883dfef505518730f7a7a93049b1d90d42e50a22b4793", size = 231305, upload-time = "2025-05-21T12:46:10.52Z" }, +] + +[[package]] +name = "send2trash" +version = "1.8.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/3a/aec9b02217bb79b87bbc1a21bc6abc51e3d5dcf65c30487ac96c0908c722/Send2Trash-1.8.3.tar.gz", hash = "sha256:b18e7a3966d99871aefeb00cfbcfdced55ce4871194810fc71f4aa484b953abf", size = 17394, upload-time = "2024-04-07T00:01:09.267Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/b0/4562db6223154aa4e22f939003cb92514c79f3d4dccca3444253fd17f902/Send2Trash-1.8.3-py3-none-any.whl", hash = "sha256:0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9", size = 18072, upload-time = "2024-04-07T00:01:07.438Z" }, +] + +[[package]] +name = "snowballstemmer" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" }, +] + +[[package]] +name = "soupsieve" +version = "2.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/f4/4a80cd6ef364b2e8b65b15816a843c0980f7a5a2b4dc701fc574952aa19f/soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a", size = 103418, upload-time = "2025-04-20T18:50:08.518Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/9c/0e6afc12c269578be5c0c1c9f4b49a8d32770a080260c333ac04cc1c832d/soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4", size = 36677, upload-time = "2025-04-20T18:50:07.196Z" }, +] + +[[package]] +name = "sphinx" +version = "7.4.7" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "alabaster", version = "0.7.16", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "babel", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "colorama", marker = "(python_full_version < '3.10' and sys_platform == 'win32') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "docutils", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "imagesize", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "jinja2", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "packaging", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "pygments", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "requests", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "snowballstemmer", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "tomli", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/be/50e50cb4f2eff47df05673d361095cafd95521d2a22521b920c67a372dcb/sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe", size = 8067911, upload-time = "2024-07-20T14:46:56.059Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/ef/153f6803c5d5f8917dbb7f7fcf6d34a871ede3296fa89c2c703f5f8a6c8e/sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239", size = 3401624, upload-time = "2024-07-20T14:46:52.142Z" }, +] + +[[package]] +name = "sphinx" +version = "8.1.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "babel", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "colorama", marker = "(python_full_version == '3.10.*' and sys_platform == 'win32') or (python_full_version != '3.10.*' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (python_full_version != '3.10.*' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (python_full_version != '3.10.*' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "docutils", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "imagesize", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "jinja2", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "packaging", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "pygments", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "requests", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "snowballstemmer", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "tomli", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125, upload-time = "2024-10-13T20:27:10.448Z" }, +] + +[[package]] +name = "sphinx" +version = "8.2.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", +] +dependencies = [ + { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "babel", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "colorama", marker = "(python_full_version >= '3.11' and sys_platform == 'win32') or (python_full_version < '3.11' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (python_full_version < '3.11' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (python_full_version < '3.11' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "docutils", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "imagesize", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "jinja2", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "packaging", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "pygments", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "requests", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "roman-numerals-py", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "snowballstemmer", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876, upload-time = "2025-03-02T22:31:59.658Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3", size = 3589741, upload-time = "2025-03-02T22:31:56.836Z" }, +] + +[[package]] +name = "sphinx-autoapi" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astroid" }, + { name = "jinja2" }, + { name = "pyyaml" }, + { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "stdlib-list", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7f/a8/22b379a2a75ccb881217d3d4ae56d7d35f2d1bb4c8c0c51d0253676746a1/sphinx_autoapi-3.6.0.tar.gz", hash = "sha256:c685f274e41d0842ae7e199460c322c4bd7fec816ccc2da8d806094b4f64af06", size = 55417, upload-time = "2025-02-18T01:50:55.241Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/17/0eda9dc80fcaf257222b506844207e71b5d59567c41bbdcca2a72da119b9/sphinx_autoapi-3.6.0-py3-none-any.whl", hash = "sha256:f3b66714493cab140b0e896d33ce7137654a16ac1edb6563edcbd47bf975f711", size = 35281, upload-time = "2025-02-18T01:50:52.789Z" }, +] + +[[package]] +name = "sphinx-rtd-theme" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinxcontrib-jquery" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/44/c97faec644d29a5ceddd3020ae2edffa69e7d00054a8c7a6021e82f20335/sphinx_rtd_theme-3.0.2.tar.gz", hash = "sha256:b7457bc25dda723b20b086a670b9953c859eab60a2a03ee8eb2bb23e176e5f85", size = 7620463, upload-time = "2024-11-13T11:06:04.545Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/77/46e3bac77b82b4df5bb5b61f2de98637724f246b4966cfc34bc5895d852a/sphinx_rtd_theme-3.0.2-py2.py3-none-any.whl", hash = "sha256:422ccc750c3a3a311de4ae327e82affdaf59eb695ba4936538552f3b00f4ee13", size = 7655561, upload-time = "2024-11-13T11:06:02.094Z" }, +] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" }, +] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" }, +] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" }, +] + +[[package]] +name = "sphinxcontrib-jquery" +version = "4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", size = 122331, upload-time = "2023-03-14T15:01:01.944Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae", size = 121104, upload-time = "2023-03-14T15:01:00.356Z" }, +] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" }, +] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" }, +] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, +] + +[[package]] +name = "stdlib-list" +version = "0.11.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5d/09/8d5c564931ae23bef17420a6c72618463a59222ca4291a7dd88de8a0d490/stdlib_list-0.11.1.tar.gz", hash = "sha256:95ebd1d73da9333bba03ccc097f5bac05e3aa03e6822a0c0290f87e1047f1857", size = 60442, upload-time = "2025-02-18T15:39:38.769Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/c7/4102536de33c19d090ed2b04e90e7452e2e3dc653cf3323208034eaaca27/stdlib_list-0.11.1-py3-none-any.whl", hash = "sha256:9029ea5e3dfde8cd4294cfd4d1797be56a67fc4693c606181730148c3fd1da29", size = 83620, upload-time = "2025-02-18T15:39:37.02Z" }, +] + +[[package]] +name = "tomli" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, + { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, + { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, + { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, + { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, + { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, + { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, + { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, + { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, + { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, + { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, +] + +[[package]] +name = "tomlkit" +version = "0.13.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/18/0bbf3884e9eaa38819ebe46a7bd25dcd56b67434402b66a58c4b8e552575/tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1", size = 185207, upload-time = "2025-06-05T07:13:44.947Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0", size = 38901, upload-time = "2025-06-05T07:13:43.546Z" }, +] + +[[package]] +name = "trove-classifiers" +version = "2025.5.9.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/04/1cd43f72c241fedcf0d9a18d0783953ee301eac9e5d9db1df0f0f089d9af/trove_classifiers-2025.5.9.12.tar.gz", hash = "sha256:7ca7c8a7a76e2cd314468c677c69d12cc2357711fcab4a60f87994c1589e5cb5", size = 16940, upload-time = "2025-05-09T12:04:48.829Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/ef/c6deb083748be3bcad6f471b6ae983950c161890bf5ae1b2af80cc56c530/trove_classifiers-2025.5.9.12-py3-none-any.whl", hash = "sha256:e381c05537adac78881c8fa345fd0e9970159f4e4a04fcc42cfd3129cca640ce", size = 14119, upload-time = "2025-05-09T12:04:46.38Z" }, +] + +[[package]] +name = "types-click" +version = "7.1.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/ff/0e6a56108d45c80c61cdd4743312d0304d8192482aea4cce96c554aaa90d/types-click-7.1.8.tar.gz", hash = "sha256:b6604968be6401dc516311ca50708a0a28baa7a0cb840efd7412f0dbbff4e092", size = 10015, upload-time = "2021-11-23T12:28:01.701Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/ad/607454a5f991c5b3e14693a7113926758f889138371058a5f72f567fa131/types_click-7.1.8-py3-none-any.whl", hash = "sha256:8cb030a669e2e927461be9827375f83c16b8178c365852c060a34e24871e7e81", size = 12929, upload-time = "2021-11-23T12:27:59.493Z" }, +] + +[[package]] +name = "types-decorator" +version = "5.2.0.20250324" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b4/61/afe7f9505058fc8d0c22deacb379eb299cd7319ee7459ab5c3ec2d435e93/types_decorator-5.2.0.20250324.tar.gz", hash = "sha256:8fbd72b0dadc56176e48e5187de744e76fe45bcc91a25874baa75662412155d3", size = 9063, upload-time = "2025-03-24T02:57:41.52Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/02/49fff752b50ad681003f3adb9573d6a4a928fdaa786eefd8e1d87226c0d6/types_decorator-5.2.0.20250324-py3-none-any.whl", hash = "sha256:0740cee7ce57cf9cf2b306114a1588984255f706efa0f35b54b2cff290a110e2", size = 8175, upload-time = "2025-03-24T02:57:40.306Z" }, +] + +[[package]] +name = "types-flask" +version = "1.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "types-click" }, + { name = "types-jinja2" }, + { name = "types-werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/65/728a104973133a45fba50f3d1e1ee832287666ac74cfd47004cea8402ea3/types-Flask-1.1.6.tar.gz", hash = "sha256:aac777b3abfff9436e6b01f6d08171cf23ea6e5be71cbf773aaabb1c5763e9cf", size = 9829, upload-time = "2021-11-26T06:21:31.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/6c/a98a0c29c39d8a6283ac704f3d36f0d570d8dee931e9d46d6cc60d436bec/types_Flask-1.1.6-py3-none-any.whl", hash = "sha256:6ab8a9a5e258b76539d652f6341408867298550b19b81f0e41e916825fc39087", size = 13733, upload-time = "2021-11-26T06:21:30.365Z" }, +] + +[[package]] +name = "types-flask-cors" +version = "6.0.0.20250520" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flask" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/b0/1457265478222c877ac38a79f24d579955380be2c5fdb4322b613aad059a/types_flask_cors-6.0.0.20250520.tar.gz", hash = "sha256:9357c21be733f65e568ff27e816426832f3e3fd906eedbb896bcc6b1cfa026e6", size = 9960, upload-time = "2025-05-20T03:06:28.545Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/9d/a983479a860d536805f9ea9fc7af147bcab7b73b573178d8bd36d8cf25c4/types_flask_cors-6.0.0.20250520-py3-none-any.whl", hash = "sha256:8898ed43a6b68d0b3b499e1d2f7aa696a99a001610de44e09fc6f404d16eb704", size = 10011, upload-time = "2025-05-20T03:06:27.379Z" }, +] + +[[package]] +name = "types-jinja2" +version = "2.11.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "types-markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/c4/b82309bfed8195de7997672deac301bd6f5bd5cbb6a3e392b7fe780d7852/types-Jinja2-2.11.9.tar.gz", hash = "sha256:dbdc74a40aba7aed520b7e4d89e8f0fe4286518494208b35123bcf084d4b8c81", size = 13302, upload-time = "2021-11-26T06:21:17.496Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b0/e79d84748f1d34304f13191424348a719c3febaa3493835370fe9528e1e6/types_Jinja2-2.11.9-py3-none-any.whl", hash = "sha256:60a1e21e8296979db32f9374d8a239af4cb541ff66447bb915d8ad398f9c63b2", size = 18190, upload-time = "2021-11-26T06:21:16.18Z" }, +] + +[[package]] +name = "types-markdown" +version = "3.8.0.20250415" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/d3/5fa81c99f0d169ade1f96822c5b327821c0357663e3b6d8782c870457a2d/types_markdown-3.8.0.20250415.tar.gz", hash = "sha256:98ab13587d1177769d93e55586d3dc97047df75bc6e37ce4074666f5dd4212ba", size = 18265, upload-time = "2025-04-15T02:59:48.441Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/03/3a6ea34260eb8358aaeaa5c2eac8000aa5d1f71778d706848fc91698d5ee/types_markdown-3.8.0.20250415-py3-none-any.whl", hash = "sha256:b41abed474a303ba300e3a4cf6f27eda339219124a59d529a158203570007776", size = 23695, upload-time = "2025-04-15T02:59:47.206Z" }, +] + +[[package]] +name = "types-markupsafe" +version = "1.1.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/39/31/b5f059142d058aec41e913d8e0eff0a967e7bc46f9a2ba2f31bc11cff059/types-MarkupSafe-1.1.10.tar.gz", hash = "sha256:85b3a872683d02aea3a5ac2a8ef590193c344092032f58457287fbf8e06711b1", size = 2986, upload-time = "2021-11-27T03:18:07.558Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/d6/b8effb1c48539260a5eb4196afc55efac4ea1684a4991977555eb266b2ef/types_MarkupSafe-1.1.10-py3-none-any.whl", hash = "sha256:ca2bee0f4faafc45250602567ef38d533e877d2ddca13003b319c551ff5b3cc5", size = 3998, upload-time = "2021-11-27T03:18:06.398Z" }, +] + +[[package]] +name = "types-orjson" +version = "3.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/97/3f78cfdf663e5668e8b490d8c84d6de089d2d8dbad935f0dc43555d52a90/types-orjson-3.6.2.tar.gz", hash = "sha256:cf9afcc79a86325c7aff251790338109ed6f6b1bab09d2d4262dd18c85a3c638", size = 1999, upload-time = "2022-01-07T11:31:10.518Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/84/b34abd2d08381c5113e475908a1d79d27dc9a15f669213cee4ca03d1a891/types_orjson-3.6.2-py3-none-any.whl", hash = "sha256:22ee9a79236b6b0bfb35a0684eded62ad930a88a56797fa3c449b026cf7dbfe4", size = 2224, upload-time = "2022-01-07T11:31:09.271Z" }, +] + +[[package]] +name = "types-protobuf" +version = "6.30.2.20250516" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/6c/5cf088aaa3927d1cc39910f60f220f5ff573ab1a6485b2836e8b26beb58c/types_protobuf-6.30.2.20250516.tar.gz", hash = "sha256:aecd1881770a9bb225ede66872ef7f0da4505edd0b193108edd9892e48d49a41", size = 62254, upload-time = "2025-05-16T03:06:50.794Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/66/06a9c161f5dd5deb4f5c016ba29106a8f1903eb9a1ba77d407dd6588fecb/types_protobuf-6.30.2.20250516-py3-none-any.whl", hash = "sha256:8c226d05b5e8b2623111765fa32d6e648bbc24832b4c2fddf0fa340ba5d5b722", size = 76480, upload-time = "2025-05-16T03:06:49.444Z" }, +] + +[[package]] +name = "types-pywin32" +version = "310.0.0.20250516" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/bc/c7be2934a37cc8c645c945ca88450b541e482c4df3ac51e5556377d34811/types_pywin32-310.0.0.20250516.tar.gz", hash = "sha256:91e5bfc033f65c9efb443722eff8101e31d690dd9a540fa77525590d3da9cc9d", size = 328459, upload-time = "2025-05-16T03:07:57.411Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/72/469e4cc32399dbe6c843e38fdb6d04fee755e984e137c0da502f74d3ac59/types_pywin32-310.0.0.20250516-py3-none-any.whl", hash = "sha256:f9ef83a1ec3e5aae2b0e24c5f55ab41272b5dfeaabb9a0451d33684c9545e41a", size = 390411, upload-time = "2025-05-16T03:07:56.282Z" }, +] + +[[package]] +name = "types-requests" +version = "2.32.4.20250611" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/7f/73b3a04a53b0fd2a911d4ec517940ecd6600630b559e4505cc7b68beb5a0/types_requests-2.32.4.20250611.tar.gz", hash = "sha256:741c8777ed6425830bf51e54d6abe245f79b4dcb9019f1622b773463946bf826", size = 23118, upload-time = "2025-06-11T03:11:41.272Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/ea/0be9258c5a4fa1ba2300111aa5a0767ee6d18eb3fd20e91616c12082284d/types_requests-2.32.4.20250611-py3-none-any.whl", hash = "sha256:ad2fe5d3b0cb3c2c902c8815a70e7fb2302c4b8c1f77bdcd738192cdb3878072", size = 20643, upload-time = "2025-06-11T03:11:40.186Z" }, +] + +[[package]] +name = "types-waitress" +version = "3.0.1.20241117" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/34/d4/af78f0ae18ca02830e0a45d410b6f4eda0dfa5b82861c9d7900d1baceb31/types-waitress-3.0.1.20241117.tar.gz", hash = "sha256:1df08f8de36cc30ddade179e1cb28c59776e89a1a2874893b59fe2ea00a823a0", size = 9536, upload-time = "2024-11-17T02:55:08.514Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/59/27fe5594ca49ba4f4bdf3cf523cf915d06c4dc5df2f59942b895e2f8774a/types_waitress-3.0.1.20241117-py3-none-any.whl", hash = "sha256:4700d2f22bacab5f817692faf25f9b7d43810b72ab7434a628a5a4cd1f5766fd", size = 12799, upload-time = "2024-11-17T02:55:07.55Z" }, +] + +[[package]] +name = "types-werkzeug" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/43/161261d2ac1fc20e944aa108e48a98ff0d994e19b498d6fb19d6637caf05/types-Werkzeug-1.0.9.tar.gz", hash = "sha256:5cc269604c400133d452a40cee6397655f878fc460e03fde291b9e3a5eaa518c", size = 23909, upload-time = "2021-11-26T06:21:28.33Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/c1/eaf8426126eafa46d649afb2fddede327043fbc2e84021b8b09a7fa15115/types_Werkzeug-1.0.9-py3-none-any.whl", hash = "sha256:194bd5715a13c598f05c63e8a739328657590943bce941e8a3619a6b5d4a54ec", size = 36186, upload-time = "2021-11-26T06:21:27.37Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423, upload-time = "2025-06-02T14:52:11.399Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" }, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, +] + +[[package]] +name = "waitress" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/cb/04ddb054f45faa306a230769e868c28b8065ea196891f09004ebace5b184/waitress-3.0.2.tar.gz", hash = "sha256:682aaaf2af0c44ada4abfb70ded36393f0e307f4ab9456a215ce0020baefc31f", size = 179901, upload-time = "2024-11-16T20:02:35.195Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/57/a27182528c90ef38d82b636a11f606b0cbb0e17588ed205435f8affe3368/waitress-3.0.2-py3-none-any.whl", hash = "sha256:c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e", size = 56232, upload-time = "2024-11-16T20:02:33.858Z" }, +] + +[[package]] +name = "websocket-client" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648, upload-time = "2024-04-23T22:16:16.976Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826, upload-time = "2024-04-23T22:16:14.422Z" }, +] + +[[package]] +name = "werkzeug" +version = "3.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/69/83029f1f6300c5fb2471d621ab06f6ec6b3324685a2ce0f9777fd4a8b71e/werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746", size = 806925, upload-time = "2024-11-08T15:52:18.093Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/24/ab44c871b0f07f491e5d2ad12c9bd7358e527510618cb1b803a88e986db1/werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", size = 224498, upload-time = "2024-11-08T15:52:16.132Z" }, +] + +[[package]] +name = "wheel" +version = "0.45.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/98/2d9906746cdc6a6ef809ae6338005b3f21bb568bea3165cfc6a243fdc25c/wheel-0.45.1.tar.gz", hash = "sha256:661e1abd9198507b1409a20c02106d9670b2576e916d58f520316666abca6729", size = 107545, upload-time = "2024-11-23T00:18:23.513Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/2c/87f3254fd8ffd29e4c02732eee68a83a1d3c346ae39bc6822dcbcb697f2b/wheel-0.45.1-py3-none-any.whl", hash = "sha256:708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248", size = 72494, upload-time = "2024-11-23T00:18:21.207Z" }, +] + +[[package]] +name = "wrapt" +version = "1.17.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531, upload-time = "2025-01-14T10:35:45.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/d1/1daec934997e8b160040c78d7b31789f19b122110a75eca3d4e8da0049e1/wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984", size = 53307, upload-time = "2025-01-14T10:33:13.616Z" }, + { url = "https://files.pythonhosted.org/packages/1b/7b/13369d42651b809389c1a7153baa01d9700430576c81a2f5c5e460df0ed9/wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22", size = 38486, upload-time = "2025-01-14T10:33:15.947Z" }, + { url = "https://files.pythonhosted.org/packages/62/bf/e0105016f907c30b4bd9e377867c48c34dc9c6c0c104556c9c9126bd89ed/wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7", size = 38777, upload-time = "2025-01-14T10:33:17.462Z" }, + { url = "https://files.pythonhosted.org/packages/27/70/0f6e0679845cbf8b165e027d43402a55494779295c4b08414097b258ac87/wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c", size = 83314, upload-time = "2025-01-14T10:33:21.282Z" }, + { url = "https://files.pythonhosted.org/packages/0f/77/0576d841bf84af8579124a93d216f55d6f74374e4445264cb378a6ed33eb/wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72", size = 74947, upload-time = "2025-01-14T10:33:24.414Z" }, + { url = "https://files.pythonhosted.org/packages/90/ec/00759565518f268ed707dcc40f7eeec38637d46b098a1f5143bff488fe97/wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061", size = 82778, upload-time = "2025-01-14T10:33:26.152Z" }, + { url = "https://files.pythonhosted.org/packages/f8/5a/7cffd26b1c607b0b0c8a9ca9d75757ad7620c9c0a9b4a25d3f8a1480fafc/wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2", size = 81716, upload-time = "2025-01-14T10:33:27.372Z" }, + { url = "https://files.pythonhosted.org/packages/7e/09/dccf68fa98e862df7e6a60a61d43d644b7d095a5fc36dbb591bbd4a1c7b2/wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c", size = 74548, upload-time = "2025-01-14T10:33:28.52Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8e/067021fa3c8814952c5e228d916963c1115b983e21393289de15128e867e/wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62", size = 81334, upload-time = "2025-01-14T10:33:29.643Z" }, + { url = "https://files.pythonhosted.org/packages/4b/0d/9d4b5219ae4393f718699ca1c05f5ebc0c40d076f7e65fd48f5f693294fb/wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563", size = 36427, upload-time = "2025-01-14T10:33:30.832Z" }, + { url = "https://files.pythonhosted.org/packages/72/6a/c5a83e8f61aec1e1aeef939807602fb880e5872371e95df2137142f5c58e/wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f", size = 38774, upload-time = "2025-01-14T10:33:32.897Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f7/a2aab2cbc7a665efab072344a8949a71081eed1d2f451f7f7d2b966594a2/wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58", size = 53308, upload-time = "2025-01-14T10:33:33.992Z" }, + { url = "https://files.pythonhosted.org/packages/50/ff/149aba8365fdacef52b31a258c4dc1c57c79759c335eff0b3316a2664a64/wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda", size = 38488, upload-time = "2025-01-14T10:33:35.264Z" }, + { url = "https://files.pythonhosted.org/packages/65/46/5a917ce85b5c3b490d35c02bf71aedaa9f2f63f2d15d9949cc4ba56e8ba9/wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438", size = 38776, upload-time = "2025-01-14T10:33:38.28Z" }, + { url = "https://files.pythonhosted.org/packages/ca/74/336c918d2915a4943501c77566db41d1bd6e9f4dbc317f356b9a244dfe83/wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a", size = 83776, upload-time = "2025-01-14T10:33:40.678Z" }, + { url = "https://files.pythonhosted.org/packages/09/99/c0c844a5ccde0fe5761d4305485297f91d67cf2a1a824c5f282e661ec7ff/wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000", size = 75420, upload-time = "2025-01-14T10:33:41.868Z" }, + { url = "https://files.pythonhosted.org/packages/b4/b0/9fc566b0fe08b282c850063591a756057c3247b2362b9286429ec5bf1721/wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6", size = 83199, upload-time = "2025-01-14T10:33:43.598Z" }, + { url = "https://files.pythonhosted.org/packages/9d/4b/71996e62d543b0a0bd95dda485219856def3347e3e9380cc0d6cf10cfb2f/wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b", size = 82307, upload-time = "2025-01-14T10:33:48.499Z" }, + { url = "https://files.pythonhosted.org/packages/39/35/0282c0d8789c0dc9bcc738911776c762a701f95cfe113fb8f0b40e45c2b9/wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662", size = 75025, upload-time = "2025-01-14T10:33:51.191Z" }, + { url = "https://files.pythonhosted.org/packages/4f/6d/90c9fd2c3c6fee181feecb620d95105370198b6b98a0770cba090441a828/wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72", size = 81879, upload-time = "2025-01-14T10:33:52.328Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fa/9fb6e594f2ce03ef03eddbdb5f4f90acb1452221a5351116c7c4708ac865/wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317", size = 36419, upload-time = "2025-01-14T10:33:53.551Z" }, + { url = "https://files.pythonhosted.org/packages/47/f8/fb1773491a253cbc123c5d5dc15c86041f746ed30416535f2a8df1f4a392/wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3", size = 38773, upload-time = "2025-01-14T10:33:56.323Z" }, + { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799, upload-time = "2025-01-14T10:33:57.4Z" }, + { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821, upload-time = "2025-01-14T10:33:59.334Z" }, + { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919, upload-time = "2025-01-14T10:34:04.093Z" }, + { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721, upload-time = "2025-01-14T10:34:07.163Z" }, + { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899, upload-time = "2025-01-14T10:34:09.82Z" }, + { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222, upload-time = "2025-01-14T10:34:11.258Z" }, + { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707, upload-time = "2025-01-14T10:34:12.49Z" }, + { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685, upload-time = "2025-01-14T10:34:15.043Z" }, + { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567, upload-time = "2025-01-14T10:34:16.563Z" }, + { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672, upload-time = "2025-01-14T10:34:17.727Z" }, + { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865, upload-time = "2025-01-14T10:34:19.577Z" }, + { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800, upload-time = "2025-01-14T10:34:21.571Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824, upload-time = "2025-01-14T10:34:22.999Z" }, + { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920, upload-time = "2025-01-14T10:34:25.386Z" }, + { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690, upload-time = "2025-01-14T10:34:28.058Z" }, + { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861, upload-time = "2025-01-14T10:34:29.167Z" }, + { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174, upload-time = "2025-01-14T10:34:31.702Z" }, + { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721, upload-time = "2025-01-14T10:34:32.91Z" }, + { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763, upload-time = "2025-01-14T10:34:34.903Z" }, + { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585, upload-time = "2025-01-14T10:34:36.13Z" }, + { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676, upload-time = "2025-01-14T10:34:37.962Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871, upload-time = "2025-01-14T10:34:39.13Z" }, + { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312, upload-time = "2025-01-14T10:34:40.604Z" }, + { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062, upload-time = "2025-01-14T10:34:45.011Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155, upload-time = "2025-01-14T10:34:47.25Z" }, + { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471, upload-time = "2025-01-14T10:34:50.934Z" }, + { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208, upload-time = "2025-01-14T10:34:52.297Z" }, + { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339, upload-time = "2025-01-14T10:34:53.489Z" }, + { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232, upload-time = "2025-01-14T10:34:55.327Z" }, + { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476, upload-time = "2025-01-14T10:34:58.055Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377, upload-time = "2025-01-14T10:34:59.3Z" }, + { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986, upload-time = "2025-01-14T10:35:00.498Z" }, + { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750, upload-time = "2025-01-14T10:35:03.378Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f4/6ed2b8f6f1c832933283974839b88ec7c983fd12905e01e97889dadf7559/wrapt-1.17.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99039fa9e6306880572915728d7f6c24a86ec57b0a83f6b2491e1d8ab0235b9a", size = 53308, upload-time = "2025-01-14T10:35:24.413Z" }, + { url = "https://files.pythonhosted.org/packages/a2/a9/712a53f8f4f4545768ac532619f6e56d5d0364a87b2212531685e89aeef8/wrapt-1.17.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2696993ee1eebd20b8e4ee4356483c4cb696066ddc24bd70bcbb80fa56ff9061", size = 38489, upload-time = "2025-01-14T10:35:26.913Z" }, + { url = "https://files.pythonhosted.org/packages/fa/9b/e172c8f28a489a2888df18f953e2f6cb8d33b1a2e78c9dfc52d8bf6a5ead/wrapt-1.17.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:612dff5db80beef9e649c6d803a8d50c409082f1fedc9dbcdfde2983b2025b82", size = 38776, upload-time = "2025-01-14T10:35:28.183Z" }, + { url = "https://files.pythonhosted.org/packages/cf/cb/7a07b51762dcd59bdbe07aa97f87b3169766cadf240f48d1cbe70a1be9db/wrapt-1.17.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c2caa1585c82b3f7a7ab56afef7b3602021d6da34fbc1cf234ff139fed3cd9", size = 83050, upload-time = "2025-01-14T10:35:30.645Z" }, + { url = "https://files.pythonhosted.org/packages/a5/51/a42757dd41032afd6d8037617aa3bc6803ba971850733b24dfb7d5c627c4/wrapt-1.17.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c958bcfd59bacc2d0249dcfe575e71da54f9dcf4a8bdf89c4cb9a68a1170d73f", size = 74718, upload-time = "2025-01-14T10:35:32.047Z" }, + { url = "https://files.pythonhosted.org/packages/bf/bb/d552bfe47db02fcfc950fc563073a33500f8108efa5f7b41db2f83a59028/wrapt-1.17.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc78a84e2dfbc27afe4b2bd7c80c8db9bca75cc5b85df52bfe634596a1da846b", size = 82590, upload-time = "2025-01-14T10:35:33.329Z" }, + { url = "https://files.pythonhosted.org/packages/77/99/77b06b3c3c410dbae411105bf22496facf03a5496bfaca8fbcf9da381889/wrapt-1.17.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba0f0eb61ef00ea10e00eb53a9129501f52385c44853dbd6c4ad3f403603083f", size = 81462, upload-time = "2025-01-14T10:35:34.933Z" }, + { url = "https://files.pythonhosted.org/packages/2d/21/cf0bd85ae66f92600829ea1de8e1da778e5e9f6e574ccbe74b66db0d95db/wrapt-1.17.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1e1fe0e6ab7775fd842bc39e86f6dcfc4507ab0ffe206093e76d61cde37225c8", size = 74309, upload-time = "2025-01-14T10:35:37.542Z" }, + { url = "https://files.pythonhosted.org/packages/6d/16/112d25e9092398a0dd6fec50ab7ac1b775a0c19b428f049785096067ada9/wrapt-1.17.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c86563182421896d73858e08e1db93afdd2b947a70064b813d515d66549e15f9", size = 81081, upload-time = "2025-01-14T10:35:38.9Z" }, + { url = "https://files.pythonhosted.org/packages/2b/49/364a615a0cc0872685646c495c7172e4fc7bf1959e3b12a1807a03014e05/wrapt-1.17.2-cp39-cp39-win32.whl", hash = "sha256:f393cda562f79828f38a819f4788641ac7c4085f30f1ce1a68672baa686482bb", size = 36423, upload-time = "2025-01-14T10:35:40.177Z" }, + { url = "https://files.pythonhosted.org/packages/00/ad/5d2c1b34ba3202cd833d9221833e74d6500ce66730974993a8dc9a94fb8c/wrapt-1.17.2-cp39-cp39-win_amd64.whl", hash = "sha256:36ccae62f64235cf8ddb682073a60519426fdd4725524ae38874adf72b5f2aeb", size = 38772, upload-time = "2025-01-14T10:35:42.763Z" }, + { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594, upload-time = "2025-01-14T10:35:44.018Z" }, +] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, +] From f98f620116709605b59c37df5462ec7a0bee3bbe Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Thu, 19 Jun 2025 14:12:18 +0700 Subject: [PATCH 006/101] Avoid committing release deps These will only get bumped on a new publish, and keeping the file around leads to spurious security alerts. --- qt/release/.gitignore | 2 + qt/release/pyproject.toml | 80 --------------------------------------- qt/release/update.sh | 12 ++++++ 3 files changed, 14 insertions(+), 80 deletions(-) create mode 100644 qt/release/.gitignore delete mode 100644 qt/release/pyproject.toml diff --git a/qt/release/.gitignore b/qt/release/.gitignore new file mode 100644 index 000000000..b09017c0e --- /dev/null +++ b/qt/release/.gitignore @@ -0,0 +1,2 @@ +pyproject.toml +pyproject.toml.old \ No newline at end of file diff --git a/qt/release/pyproject.toml b/qt/release/pyproject.toml deleted file mode 100644 index 8f9a8fe63..000000000 --- a/qt/release/pyproject.toml +++ /dev/null @@ -1,80 +0,0 @@ -[project] -name = "anki-release" -version = "0.1.3" -description = "A package to lock Anki's dependencies" -requires-python = ">=3.9" -dependencies = [ - "anki==0.1.2", - "aqt==0.1.2", - "anki-audio==0.1.0 ; sys_platform == 'darwin' or sys_platform == 'win32'", - "attrs==25.3.0", - "beautifulsoup4==4.12.3", - "blinker==1.9.0", - "certifi==2025.4.26", - "charset-normalizer==3.4.2", - "click==8.1.8 ; python_full_version < '3.10'", - "click==8.2.1 ; python_full_version >= '3.10'", - "colorama==0.4.6 ; sys_platform == 'win32'", - "decorator==5.2.1", - "distro==1.9.0 ; sys_platform != 'darwin' and sys_platform != 'win32'", - "flask==3.1.1", - "flask-cors==6.0.0", - "idna==3.10", - "importlib-metadata==8.7.0 ; python_full_version < '3.10'", - "itsdangerous==2.2.0", - "jinja2==3.1.6", - "jsonschema==4.24.0", - "jsonschema-specifications==2025.4.1", - "markdown==3.8", - "markupsafe==3.0.2", - "mock==5.2.0", - "orjson==3.10.18", - "pip-system-certs==4.0", - "protobuf==6.31.1", - "psutil==7.0.0 ; sys_platform == 'win32'", - "pyqt6==6.8.0", - "pyqt6-qt6==6.8.1", - "pyqt6-sip==13.10.2", - "pyqt6-webengine==6.8.0", - "pyqt6-webengine-qt6==6.8.1", - "pysocks==1.7.1", - "pywin32==310 ; sys_platform == 'win32'", - "referencing==0.36.2", - "requests==2.32.3", - "rpds-py==0.25.1", - "send2trash==1.8.3", - "soupsieve==2.7", - "types-click==7.1.8", - "types-decorator==5.2.0.20250324", - "types-flask==1.1.6", - "types-flask-cors==6.0.0.20250520", - "types-jinja2==2.11.9", - "types-markdown==3.8.0.20250415", - "types-markupsafe==1.1.10", - "types-orjson==3.6.2", - "types-protobuf==6.30.2.20250516", - "types-pywin32==310.0.0.20250516", - "types-requests==2.32.0.20250602", - "types-waitress==3.0.1.20241117", - "types-werkzeug==1.0.9", - "typing-extensions==4.14.0", - "urllib3==2.4.0", - "waitress==3.0.2", - "werkzeug==3.1.3", - "wrapt==1.17.2", - "zipp==3.23.0 ; python_full_version < '3.10'", -] - -[[tool.uv.index]] -name = "testpypi" -url = "https://test.pypi.org/simple/" -publish-url = "https://test.pypi.org/legacy/" -explicit = true - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -# hatch throws an error if nothing is included -[tool.hatch.build.targets.wheel] -include = ["no-such-file"] diff --git a/qt/release/update.sh b/qt/release/update.sh index 126214248..e25eaec49 100755 --- a/qt/release/update.sh +++ b/qt/release/update.sh @@ -16,6 +16,11 @@ UV="$PROJ_ROOT/out/extracted/uv/uv" # Prompt for wheel version read -p "Wheel version: " VERSION +# Copy existing pyproject.toml to .old if it exists +if [ -f pyproject.toml ]; then + cp pyproject.toml pyproject.toml.old +fi + # Export dependencies using uv echo "Exporting dependencies..." rm -f pyproject.toml @@ -60,3 +65,10 @@ include = ["no-such-file"] EOF echo "Generated pyproject.toml with version $VERSION" + +# Show diff if .old file exists +if [ -f pyproject.toml.old ]; then + echo + echo "Differences from previous version:" + diff -u --color=always pyproject.toml.old pyproject.toml || true +fi From 344cac1ef4712822c78d8f96da8bf17144358213 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Thu, 19 Jun 2025 14:42:08 +0700 Subject: [PATCH 007/101] Update translations --- ftl/core-repo | 2 +- ftl/qt-repo | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ftl/core-repo b/ftl/core-repo index 78412ce16..2f8c9d956 160000 --- a/ftl/core-repo +++ b/ftl/core-repo @@ -1 +1 @@ -Subproject commit 78412ce163d4dc50dd82f5b27cde3119086a2eb7 +Subproject commit 2f8c9d9566aef8b86e3326fe9ff007d594b7ec83 diff --git a/ftl/qt-repo b/ftl/qt-repo index fbe9d1c73..69f2dbaeb 160000 --- a/ftl/qt-repo +++ b/ftl/qt-repo @@ -1 +1 @@ -Subproject commit fbe9d1c731f7ad09953e63fdb0c455a6d3a3b6be +Subproject commit 69f2dbaeba6f72ac62da0b35881f320603da5124 From cd411927cc31a8080ab8c132a708594f2893d352 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 20 Jun 2025 16:13:49 +0700 Subject: [PATCH 008/101] Split libankihelper into a separate module It's rarely updated, and the old approach resulted in a 'proper' aqt build only being done on a Mac. --- build/configure/src/aqt.rs | 22 ------------- qt/aqt/_macos_helper.py | 49 +++------------------------- qt/mac/anki_mac_helper/__init__.py | 51 ++++++++++++++++++++++++++++++ qt/mac/anki_mac_helper/py.typed | 0 qt/mac/build.sh | 20 ++++++++++++ qt/mac/helper_build.py | 24 +++++++++++--- qt/mac/pyproject.toml | 17 ++++++++++ qt/pyproject.toml | 1 + uv.lock | 12 ++++++- 9 files changed, 125 insertions(+), 71 deletions(-) create mode 100644 qt/mac/anki_mac_helper/__init__.py create mode 100644 qt/mac/anki_mac_helper/py.typed create mode 100755 qt/mac/build.sh create mode 100644 qt/mac/pyproject.toml diff --git a/build/configure/src/aqt.rs b/build/configure/src/aqt.rs index 60316fcd2..6be5adb07 100644 --- a/build/configure/src/aqt.rs +++ b/build/configure/src/aqt.rs @@ -27,7 +27,6 @@ pub fn build_and_check_aqt(build: &mut Build) -> Result<()> { build_forms(build)?; build_generated_sources(build)?; build_data_folder(build)?; - build_macos_helper(build)?; build_wheel(build)?; check_python(build)?; Ok(()) @@ -337,27 +336,6 @@ impl BuildAction for BuildThemedIcon<'_> { } } -fn build_macos_helper(build: &mut Build) -> Result<()> { - if cfg!(target_os = "macos") { - build.add_action( - "qt:aqt:data:lib:libankihelper", - RunCommand { - command: ":pyenv:bin", - args: "$script $out $in", - inputs: hashmap! { - "script" => inputs!["qt/mac/helper_build.py"], - "in" => inputs![glob!["qt/mac/*.swift"]], - "" => inputs!["out/env"], - }, - outputs: hashmap! { - "out" => vec!["qt/_aqt/data/lib/libankihelper.dylib"], - }, - }, - )?; - } - Ok(()) -} - fn build_wheel(build: &mut Build) -> Result<()> { build.add_action( "wheels:aqt", diff --git a/qt/aqt/_macos_helper.py b/qt/aqt/_macos_helper.py index 9328b1c4a..634d94756 100644 --- a/qt/aqt/_macos_helper.py +++ b/qt/aqt/_macos_helper.py @@ -3,50 +3,11 @@ from __future__ import annotations -import os import sys -from collections.abc import Callable -from ctypes import CDLL, CFUNCTYPE, c_bool, c_char_p -import aqt -import aqt.utils - - -class _MacOSHelper: - def __init__(self) -> None: - path = os.path.join(aqt.utils.aqt_data_folder(), "lib", "libankihelper.dylib") - - self._dll = CDLL(path) - self._dll.system_is_dark.restype = c_bool - - def system_is_dark(self) -> bool: - return self._dll.system_is_dark() - - def set_darkmode_enabled(self, enabled: bool) -> bool: - return self._dll.set_darkmode_enabled(enabled) - - def start_wav_record(self, path: str, on_error: Callable[[str], None]) -> None: - global _on_audio_error - _on_audio_error = on_error - self._dll.start_wav_record(path.encode("utf8"), _audio_error_callback) - - def end_wav_record(self) -> None: - "On completion, file should be saved if no error has arrived." - self._dll.end_wav_record() - - -# this must not be overwritten or deallocated -@CFUNCTYPE(None, c_char_p) # type: ignore -def _audio_error_callback(msg: str) -> None: - if handler := _on_audio_error: - handler(msg) - - -_on_audio_error: Callable[[str], None] | None = None - -macos_helper: _MacOSHelper | None = None if sys.platform == "darwin": - try: - macos_helper = _MacOSHelper() - except Exception as e: - print("macos_helper:", e) + from anki_mac_helper import ( # pylint:disable=unused-import,import-error + macos_helper, + ) +else: + macos_helper = None diff --git a/qt/mac/anki_mac_helper/__init__.py b/qt/mac/anki_mac_helper/__init__.py new file mode 100644 index 000000000..a0adb469e --- /dev/null +++ b/qt/mac/anki_mac_helper/__init__.py @@ -0,0 +1,51 @@ +# Copyright: Ankitects Pty Ltd and contributors +# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +from __future__ import annotations + +import sys +from collections.abc import Callable +from ctypes import CDLL, CFUNCTYPE, c_bool, c_char_p +from pathlib import Path + + +class _MacOSHelper: + def __init__(self) -> None: + # Look for the dylib in the same directory as this module + module_dir = Path(__file__).parent + path = module_dir / "libankihelper.dylib" + + self._dll = CDLL(str(path)) + self._dll.system_is_dark.restype = c_bool + + def system_is_dark(self) -> bool: + return self._dll.system_is_dark() + + def set_darkmode_enabled(self, enabled: bool) -> bool: + return self._dll.set_darkmode_enabled(enabled) + + def start_wav_record(self, path: str, on_error: Callable[[str], None]) -> None: + global _on_audio_error + _on_audio_error = on_error + self._dll.start_wav_record(path.encode("utf8"), _audio_error_callback) + + def end_wav_record(self) -> None: + "On completion, file should be saved if no error has arrived." + self._dll.end_wav_record() + + +# this must not be overwritten or deallocated +@CFUNCTYPE(None, c_char_p) # type: ignore +def _audio_error_callback(msg: str) -> None: + if handler := _on_audio_error: + handler(msg) + + +_on_audio_error: Callable[[str], None] | None = None + +macos_helper: _MacOSHelper | None = None +if sys.platform == "darwin": + try: + macos_helper = _MacOSHelper() + except Exception as e: + print("macos_helper:", e) diff --git a/qt/mac/anki_mac_helper/py.typed b/qt/mac/anki_mac_helper/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/qt/mac/build.sh b/qt/mac/build.sh new file mode 100755 index 000000000..4c14a13f4 --- /dev/null +++ b/qt/mac/build.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Copyright: Ankitects Pty Ltd and contributors +# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +set -e + +# Get the project root directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJ_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +# Build the dylib first +echo "Building macOS helper dylib..." +"$PROJ_ROOT/out/pyenv/bin/python" "$SCRIPT_DIR/helper_build.py" + +# Create the wheel using uv +echo "Creating wheel..." +cd "$SCRIPT_DIR" +"$PROJ_ROOT/out/extracted/uv/uv" build --wheel + +echo "Build complete!" diff --git a/qt/mac/helper_build.py b/qt/mac/helper_build.py index 0638c9249..aaf997669 100644 --- a/qt/mac/helper_build.py +++ b/qt/mac/helper_build.py @@ -7,8 +7,16 @@ import subprocess import sys from pathlib import Path -out_dylib, *src_files = sys.argv[1:] -out_dir = Path(out_dylib).parent.resolve() +# If no arguments provided, build for the anki_mac_helper package +if len(sys.argv) == 1: + script_dir = Path(__file__).parent + out_dylib = script_dir / "anki_mac_helper" / "libankihelper.dylib" + src_files = list(script_dir.glob("*.swift")) +else: + out_dylib, *src_files = sys.argv[1:] + +out_dylib = Path(out_dylib) +out_dir = out_dylib.parent.resolve() src_dir = Path(src_files[0]).parent.resolve() # Build for both architectures @@ -29,12 +37,20 @@ for arch in architectures: "ankihelper", "-O", ] - args.extend(src_dir / Path(file).name for file in src_files) + if isinstance(src_files[0], Path): + args.extend(src_files) + else: + args.extend(src_dir / Path(file).name for file in src_files) args.extend(["-o", str(temp_out)]) subprocess.run(args, check=True, cwd=out_dir) +# Ensure output directory exists +out_dir.mkdir(parents=True, exist_ok=True) + # Create universal binary -lipo_args = ["lipo", "-create", "-output", out_dylib] + [str(f) for f in temp_files] +lipo_args = ["lipo", "-create", "-output", str(out_dylib)] + [ + str(f) for f in temp_files +] subprocess.run(lipo_args, check=True) # Clean up temporary files diff --git a/qt/mac/pyproject.toml b/qt/mac/pyproject.toml new file mode 100644 index 000000000..93f4e939b --- /dev/null +++ b/qt/mac/pyproject.toml @@ -0,0 +1,17 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "anki-mac-helper" +version = "0.1.0" +description = "Small support library for Anki on Macs" +requires-python = ">=3.9" +license = { text = "AGPL-3.0-or-later" } +authors = [ + { name = "Anki Team" }, +] +urls = { Homepage = "https://github.com/ankitects/anki" } + +[tool.hatch.build.targets.wheel] +packages = ["anki_mac_helper"] diff --git a/qt/pyproject.toml b/qt/pyproject.toml index ab16afc3d..d8b4c4306 100644 --- a/qt/pyproject.toml +++ b/qt/pyproject.toml @@ -14,6 +14,7 @@ dependencies = [ "waitress>=2.0.0", "psutil; sys.platform == 'win32'", "pywin32; sys.platform == 'win32'", + "anki-mac-helper; sys.platform == 'darwin'", "pip-system-certs!=5.1", "mock", "types-decorator", diff --git a/uv.lock b/uv.lock index d735e0772..51b16f8ab 100644 --- a/uv.lock +++ b/uv.lock @@ -141,11 +141,20 @@ dev = [ { name = "wheel" }, ] +[[package]] +name = "anki-mac-helper" +version = "0.1.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/9f/c4d3e635ddbd2c6c24ff5454e96900fd2061b9abbb0198b9283446780d08/anki_mac_helper-0.1.0-py3-none-any.whl", hash = "sha256:ed449aba27ea3bc7999054afa10dacf08ef856ed7af46526d9c8599d8179a618", size = 40637, upload-time = "2025-06-19T14:38:07.672Z" }, +] + [[package]] name = "aqt" version = "0.1.2" source = { editable = "qt" } dependencies = [ + { name = "anki-mac-helper", marker = "sys_platform == 'darwin' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, { name = "beautifulsoup4" }, { name = "flask" }, { name = "flask-cors" }, @@ -201,6 +210,7 @@ qt67 = [ [package.metadata] requires-dist = [ { name = "anki-audio", marker = "(sys_platform == 'darwin' and extra == 'audio') or (sys_platform == 'win32' and extra == 'audio')", specifier = "==0.1.0" }, + { name = "anki-mac-helper", marker = "sys_platform == 'darwin'" }, { name = "beautifulsoup4" }, { name = "flask" }, { name = "flask-cors" }, @@ -572,7 +582,7 @@ name = "importlib-metadata" version = "8.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp" }, + { name = "zipp", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } wheels = [ From 8e20973c52e185d6e87e0ee4792c024fcc419b8f Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 20 Jun 2025 16:11:17 +0700 Subject: [PATCH 009/101] Drop remaining qt5 code --- build/configure/src/aqt.rs | 1 - docs/linux.md | 15 +- qt/aqt/__init__.py | 23 +- qt/aqt/browser/table/model.py | 4 +- qt/aqt/browser/table/table.py | 11 +- qt/aqt/forms/about.py | 9 +- qt/aqt/forms/addcards.py | 9 +- qt/aqt/forms/addfield.py | 9 +- qt/aqt/forms/addmodel.py | 9 +- qt/aqt/forms/addonconf.py | 9 +- qt/aqt/forms/addons.py | 9 +- qt/aqt/forms/browser.py | 9 +- qt/aqt/forms/browserdisp.py | 9 +- qt/aqt/forms/browseropts.py | 9 +- qt/aqt/forms/changemap.py | 9 +- qt/aqt/forms/changemodel.py | 9 +- qt/aqt/forms/clayout_top.py | 9 +- qt/aqt/forms/customstudy.py | 9 +- qt/aqt/forms/dconf.py | 9 +- qt/aqt/forms/debug.py | 9 +- qt/aqt/forms/editcurrent.py | 9 +- qt/aqt/forms/edithtml.py | 9 +- qt/aqt/forms/emptycards.py | 9 +- qt/aqt/forms/exporting.py | 9 +- qt/aqt/forms/fields.py | 9 +- qt/aqt/forms/filtered_deck.py | 9 +- qt/aqt/forms/finddupes.py | 9 +- qt/aqt/forms/findreplace.py | 9 +- qt/aqt/forms/forget.py | 9 +- qt/aqt/forms/getaddons.py | 9 +- qt/aqt/forms/importing.py | 9 +- qt/aqt/forms/main.py | 9 +- qt/aqt/forms/modelopts.py | 9 +- qt/aqt/forms/models.py | 9 +- qt/aqt/forms/preferences.py | 9 +- qt/aqt/forms/preview.py | 9 +- qt/aqt/forms/profiles.py | 9 +- qt/aqt/forms/progress.py | 9 +- qt/aqt/forms/reposition.py | 9 +- qt/aqt/forms/setgroup.py | 9 +- qt/aqt/forms/setlang.py | 9 +- qt/aqt/forms/stats.py | 9 +- qt/aqt/forms/studydeck.py | 9 +- qt/aqt/forms/synclog.py | 9 +- qt/aqt/forms/taglimit.py | 9 +- qt/aqt/forms/template.py | 9 +- qt/aqt/forms/widgets.py | 9 +- qt/aqt/profiles.py | 7 +- qt/aqt/progress.py | 3 +- qt/aqt/qt/__init__.py | 25 +-- qt/aqt/qt/qt5.py | 22 -- qt/aqt/qt/qt5_audio.py | 99 -------- qt/aqt/qt/qt5_compat.py | 411 ---------------------------------- qt/aqt/sound.py | 19 +- qt/tools/build_ui.py | 26 +-- 55 files changed, 71 insertions(+), 973 deletions(-) delete mode 100644 qt/aqt/qt/qt5.py delete mode 100644 qt/aqt/qt/qt5_audio.py delete mode 100644 qt/aqt/qt/qt5_compat.py diff --git a/build/configure/src/aqt.rs b/build/configure/src/aqt.rs index 6be5adb07..0a9b32270 100644 --- a/build/configure/src/aqt.rs +++ b/build/configure/src/aqt.rs @@ -38,7 +38,6 @@ fn build_forms(build: &mut Build) -> Result<()> { let mut py_files = vec![]; for path in ui_files.resolve() { let outpath = outdir.join(path.file_name().unwrap()).into_string(); - py_files.push(outpath.replace(".ui", "_qt5.py")); py_files.push(outpath.replace(".ui", "_qt6.py")); } build.add_action( diff --git a/docs/linux.md b/docs/linux.md index 27e3ceeda..55794e074 100644 --- a/docs/linux.md +++ b/docs/linux.md @@ -51,13 +51,8 @@ Anki requires a recent glibc. If you are using a distro that uses musl, Anki will not work. -If your glibc version is 2.35+ on AMD64 or 2.39+ on ARM64, you can skip the rest of this section. - -If your system has an older glibc, you won't be able to use the PyQt wheels that are -available in pip/PyPy, and will need to use your system-installed PyQt instead. -Your distro will also need to have Python 3.9 or later. - -After installing the system libraries (eg: +You can use your system's Qt libraries if they are Qt 6.2 or later, if +you wish. After installing the system libraries (eg: 'sudo apt install python3-pyqt6.qt{quick,webengine} python3-venv pyqt6-dev-tools'), find the place they are installed (eg '/usr/lib/python3/dist-packages'). On modern Ubuntu, you'll also need 'sudo apt remove python3-protobuf'. Then before running any commands like './run', tell Anki where @@ -68,12 +63,6 @@ export PYTHONPATH=/usr/lib/python3/dist-packages export PYTHON_BINARY=/usr/bin/python3 ``` -There are a few things to be aware of: - -- You should use ./run and not tools/run-qt5\*, even if your system libraries are Qt5. -- If your system libraries are Qt5, when creating an aqt wheel, the wheel will not work - on Qt6 environments. - ## Packaging considerations Python, node and protoc are downloaded as part of the build. You can optionally define diff --git a/qt/aqt/__init__.py b/qt/aqt/__init__.py index 6645e3599..740dcbc9f 100644 --- a/qt/aqt/__init__.py +++ b/qt/aqt/__init__.py @@ -284,7 +284,7 @@ def setupLangAndBackend( class NativeEventFilter(QAbstractNativeEventFilter): def nativeEventFilter( self, eventType: Any, message: Any - ) -> tuple[bool, sip.voidptr | None]: + ) -> tuple[bool, Any | None]: if eventType == "windows_generic_MSG": import ctypes.wintypes @@ -376,6 +376,8 @@ class AnkiApp(QApplication): def onRecv(self) -> None: sock = self._srv.nextPendingConnection() + if sock is None: + return if not sock.waitForReadyRead(self.TMOUT): sys.stderr.write(sock.errorString()) return @@ -406,14 +408,12 @@ class AnkiApp(QApplication): QRadioButton, QMenu, QSlider, - # classes with PyQt5 compatibility proxy - without_qt5_compat_wrapper(QToolButton), - without_qt5_compat_wrapper(QTabBar), + QToolButton, + QTabBar, ) if evt.type() in [QEvent.Type.Enter, QEvent.Type.HoverEnter]: if (isinstance(src, pointer_classes) and src.isEnabled()) or ( - isinstance(src, without_qt5_compat_wrapper(QComboBox)) - and not src.isEditable() + isinstance(src, QComboBox) and not src.isEditable() ): self.setOverrideCursor(QCursor(Qt.CursorShape.PointingHandCursor)) else: @@ -525,15 +525,12 @@ def setupGL(pm: aqt.profiles.ProfileManager) -> None: QQuickWindow.setGraphicsApi(QSGRendererInterface.GraphicsApi.OpenGL) elif driver in (VideoDriver.Software, VideoDriver.ANGLE): if is_win: - # on Windows, this appears to be sufficient on Qt5/Qt6. + # on Windows, this appears to be sufficient # On Qt6, ANGLE is excluded by the enum. os.environ["QT_OPENGL"] = driver.value elif is_mac: QCoreApplication.setAttribute(Qt.ApplicationAttribute.AA_UseSoftwareOpenGL) elif is_lin: - # Qt5 only - os.environ["QT_XCB_FORCE_SOFTWARE_OPENGL"] = "1" - # Required on Qt6 if "QTWEBENGINE_CHROMIUM_FLAGS" not in os.environ: os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--disable-gpu" if qtmajor > 5: @@ -663,12 +660,6 @@ def _run(argv: list[str] | None = None, exec: bool = True) -> AnkiApp | None: if is_win and "QT_QPA_PLATFORM" not in os.environ: os.environ["QT_QPA_PLATFORM"] = "windows:altgr" - # Disable sandbox on Qt5 PyPi/packaged builds, as it causes blank screens on modern - # glibc versions. We check for specific patch versions, because distros may have - # fixed the issue in their own Qt builds. - if is_lin and qtfullversion in ([5, 15, 2], [5, 14, 1]): - os.environ["QTWEBENGINE_DISABLE_SANDBOX"] = "1" - # create the app QCoreApplication.setApplicationName("Anki") QGuiApplication.setDesktopFileName("anki") diff --git a/qt/aqt/browser/table/model.py b/qt/aqt/browser/table/model.py index 5b42c0ca3..e8d3bb7b6 100644 --- a/qt/aqt/browser/table/model.py +++ b/qt/aqt/browser/table/model.py @@ -325,15 +325,13 @@ class DataModel(QAbstractTableModel): return 0 return self.len_columns() - _QFont = without_qt5_compat_wrapper(QFont) - def data(self, index: QModelIndex = QModelIndex(), role: int = 0) -> Any: if not index.isValid(): return QVariant() if role == Qt.ItemDataRole.FontRole: if not self.column_at(index).uses_cell_font: return QVariant() - qfont = self._QFont() + qfont = QFont() row = self.get_row(index) qfont.setFamily(row.font_name) qfont.setPixelSize(row.font_size) diff --git a/qt/aqt/browser/table/table.py b/qt/aqt/browser/table/table.py index f3d543d93..fb921822b 100644 --- a/qt/aqt/browser/table/table.py +++ b/qt/aqt/browser/table/table.py @@ -382,10 +382,7 @@ class Table: hh.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self._restore_header() qconnect(hh.customContextMenuRequested, self._on_header_context) - if qtmajor == 5: - qconnect(hh.sortIndicatorChanged, self._on_sort_column_changed_qt5) - else: - qconnect(hh.sortIndicatorChanged, self._on_sort_column_changed) + qconnect(hh.sortIndicatorChanged, self._on_sort_column_changed) qconnect(hh.sectionMoved, self._on_column_moved) # Slots @@ -495,12 +492,6 @@ class Table: if checked: self._scroll_to_column(self._model.len_columns() - 1) - def _on_sort_column_changed_qt5(self, section: int, order: int) -> None: - self._on_sort_column_changed( - section, - Qt.SortOrder.AscendingOrder if not order else Qt.SortOrder.DescendingOrder, - ) - def _on_sort_column_changed(self, section: int, order: Qt.SortOrder) -> None: column = self._model.column_at_section(section) sorting = column.sorting_notes if self.is_notes_mode() else column.sorting_cards diff --git a/qt/aqt/forms/about.py b/qt/aqt/forms/about.py index 4faf97fb0..fe66f7da3 100644 --- a/qt/aqt/forms/about.py +++ b/qt/aqt/forms/about.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.about_qt6 import * -else: - from _aqt.forms.about_qt5 import * # type: ignore +from _aqt.forms.about_qt6 import * diff --git a/qt/aqt/forms/addcards.py b/qt/aqt/forms/addcards.py index ae2debe3e..8c501695e 100644 --- a/qt/aqt/forms/addcards.py +++ b/qt/aqt/forms/addcards.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.addcards_qt6 import * -else: - from _aqt.forms.addcards_qt5 import * # type: ignore +from _aqt.forms.addcards_qt6 import * diff --git a/qt/aqt/forms/addfield.py b/qt/aqt/forms/addfield.py index 57c697b4a..a2f9eed74 100644 --- a/qt/aqt/forms/addfield.py +++ b/qt/aqt/forms/addfield.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.addfield_qt6 import * -else: - from _aqt.forms.addfield_qt5 import * # type: ignore +from _aqt.forms.addfield_qt6 import * diff --git a/qt/aqt/forms/addmodel.py b/qt/aqt/forms/addmodel.py index 9a7d06b7e..0af313a45 100644 --- a/qt/aqt/forms/addmodel.py +++ b/qt/aqt/forms/addmodel.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.addmodel_qt6 import * -else: - from _aqt.forms.addmodel_qt5 import * # type: ignore +from _aqt.forms.addmodel_qt6 import * diff --git a/qt/aqt/forms/addonconf.py b/qt/aqt/forms/addonconf.py index cca92b7b9..d78ebb82a 100644 --- a/qt/aqt/forms/addonconf.py +++ b/qt/aqt/forms/addonconf.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.addonconf_qt6 import * -else: - from _aqt.forms.addonconf_qt5 import * # type: ignore +from _aqt.forms.addonconf_qt6 import * diff --git a/qt/aqt/forms/addons.py b/qt/aqt/forms/addons.py index fa00be08b..46d7532b4 100644 --- a/qt/aqt/forms/addons.py +++ b/qt/aqt/forms/addons.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.addons_qt6 import * -else: - from _aqt.forms.addons_qt5 import * # type: ignore +from _aqt.forms.addons_qt6 import * diff --git a/qt/aqt/forms/browser.py b/qt/aqt/forms/browser.py index 403f780c5..70214ba4c 100644 --- a/qt/aqt/forms/browser.py +++ b/qt/aqt/forms/browser.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.browser_qt6 import * -else: - from _aqt.forms.browser_qt5 import * # type: ignore +from _aqt.forms.browser_qt6 import * diff --git a/qt/aqt/forms/browserdisp.py b/qt/aqt/forms/browserdisp.py index 712e5a400..fc745a703 100644 --- a/qt/aqt/forms/browserdisp.py +++ b/qt/aqt/forms/browserdisp.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.browserdisp_qt6 import * -else: - from _aqt.forms.browserdisp_qt5 import * # type: ignore +from _aqt.forms.browserdisp_qt6 import * diff --git a/qt/aqt/forms/browseropts.py b/qt/aqt/forms/browseropts.py index 68602c85c..1ae696033 100644 --- a/qt/aqt/forms/browseropts.py +++ b/qt/aqt/forms/browseropts.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.browseropts_qt6 import * -else: - from _aqt.forms.browseropts_qt5 import * # type: ignore +from _aqt.forms.browseropts_qt6 import * diff --git a/qt/aqt/forms/changemap.py b/qt/aqt/forms/changemap.py index 6028b0d49..b48b49a83 100644 --- a/qt/aqt/forms/changemap.py +++ b/qt/aqt/forms/changemap.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.changemap_qt6 import * -else: - from _aqt.forms.changemap_qt5 import * # type: ignore +from _aqt.forms.changemap_qt6 import * diff --git a/qt/aqt/forms/changemodel.py b/qt/aqt/forms/changemodel.py index 73f7f6095..cd1931af8 100644 --- a/qt/aqt/forms/changemodel.py +++ b/qt/aqt/forms/changemodel.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.changemodel_qt6 import * -else: - from _aqt.forms.changemodel_qt5 import * # type: ignore +from _aqt.forms.changemodel_qt6 import * diff --git a/qt/aqt/forms/clayout_top.py b/qt/aqt/forms/clayout_top.py index 24f78be11..1a76c882a 100644 --- a/qt/aqt/forms/clayout_top.py +++ b/qt/aqt/forms/clayout_top.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.clayout_top_qt6 import * -else: - from _aqt.forms.clayout_top_qt5 import * # type: ignore +from _aqt.forms.clayout_top_qt6 import * diff --git a/qt/aqt/forms/customstudy.py b/qt/aqt/forms/customstudy.py index 393638b2c..3bfad32ac 100644 --- a/qt/aqt/forms/customstudy.py +++ b/qt/aqt/forms/customstudy.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.customstudy_qt6 import * -else: - from _aqt.forms.customstudy_qt5 import * # type: ignore +from _aqt.forms.customstudy_qt6 import * diff --git a/qt/aqt/forms/dconf.py b/qt/aqt/forms/dconf.py index e28db5c31..f39de7077 100644 --- a/qt/aqt/forms/dconf.py +++ b/qt/aqt/forms/dconf.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.dconf_qt6 import * -else: - from _aqt.forms.dconf_qt5 import * # type: ignore +from _aqt.forms.dconf_qt6 import * diff --git a/qt/aqt/forms/debug.py b/qt/aqt/forms/debug.py index 928ba7795..0880c49fc 100644 --- a/qt/aqt/forms/debug.py +++ b/qt/aqt/forms/debug.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.debug_qt6 import * -else: - from _aqt.forms.debug_qt5 import * # type: ignore +from _aqt.forms.debug_qt6 import * diff --git a/qt/aqt/forms/editcurrent.py b/qt/aqt/forms/editcurrent.py index 1281faafe..cfa9ab1d9 100644 --- a/qt/aqt/forms/editcurrent.py +++ b/qt/aqt/forms/editcurrent.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.editcurrent_qt6 import * -else: - from _aqt.forms.editcurrent_qt5 import * # type: ignore +from _aqt.forms.editcurrent_qt6 import * diff --git a/qt/aqt/forms/edithtml.py b/qt/aqt/forms/edithtml.py index 029977705..61b9e0fd2 100644 --- a/qt/aqt/forms/edithtml.py +++ b/qt/aqt/forms/edithtml.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.edithtml_qt6 import * -else: - from _aqt.forms.edithtml_qt5 import * # type: ignore +from _aqt.forms.edithtml_qt6 import * diff --git a/qt/aqt/forms/emptycards.py b/qt/aqt/forms/emptycards.py index 046c7eb3a..1cae290fd 100644 --- a/qt/aqt/forms/emptycards.py +++ b/qt/aqt/forms/emptycards.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.emptycards_qt6 import * -else: - from _aqt.forms.emptycards_qt5 import * # type: ignore +from _aqt.forms.emptycards_qt6 import * diff --git a/qt/aqt/forms/exporting.py b/qt/aqt/forms/exporting.py index 559e50ecd..d09e9cdd9 100644 --- a/qt/aqt/forms/exporting.py +++ b/qt/aqt/forms/exporting.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.exporting_qt6 import * -else: - from _aqt.forms.exporting_qt5 import * # type: ignore +from _aqt.forms.exporting_qt6 import * diff --git a/qt/aqt/forms/fields.py b/qt/aqt/forms/fields.py index fa379be67..cf7a39f75 100644 --- a/qt/aqt/forms/fields.py +++ b/qt/aqt/forms/fields.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.fields_qt6 import * -else: - from _aqt.forms.fields_qt5 import * # type: ignore +from _aqt.forms.fields_qt6 import * diff --git a/qt/aqt/forms/filtered_deck.py b/qt/aqt/forms/filtered_deck.py index 9b9589046..59870f5a0 100644 --- a/qt/aqt/forms/filtered_deck.py +++ b/qt/aqt/forms/filtered_deck.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.filtered_deck_qt6 import * -else: - from _aqt.forms.filtered_deck_qt5 import * # type: ignore +from _aqt.forms.filtered_deck_qt6 import * diff --git a/qt/aqt/forms/finddupes.py b/qt/aqt/forms/finddupes.py index 7bca9c4cd..43ac30549 100644 --- a/qt/aqt/forms/finddupes.py +++ b/qt/aqt/forms/finddupes.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.finddupes_qt6 import * -else: - from _aqt.forms.finddupes_qt5 import * # type: ignore +from _aqt.forms.finddupes_qt6 import * diff --git a/qt/aqt/forms/findreplace.py b/qt/aqt/forms/findreplace.py index 8f82e58fe..65d1f3555 100644 --- a/qt/aqt/forms/findreplace.py +++ b/qt/aqt/forms/findreplace.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.findreplace_qt6 import * -else: - from _aqt.forms.findreplace_qt5 import * # type: ignore +from _aqt.forms.findreplace_qt6 import * diff --git a/qt/aqt/forms/forget.py b/qt/aqt/forms/forget.py index 97425aed8..0d17803df 100644 --- a/qt/aqt/forms/forget.py +++ b/qt/aqt/forms/forget.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.forget_qt6 import * -else: - from _aqt.forms.forget_qt5 import * # type: ignore +from _aqt.forms.forget_qt6 import * diff --git a/qt/aqt/forms/getaddons.py b/qt/aqt/forms/getaddons.py index c47ed27a8..ecb6c23dd 100644 --- a/qt/aqt/forms/getaddons.py +++ b/qt/aqt/forms/getaddons.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.getaddons_qt6 import * -else: - from _aqt.forms.getaddons_qt5 import * # type: ignore +from _aqt.forms.getaddons_qt6 import * diff --git a/qt/aqt/forms/importing.py b/qt/aqt/forms/importing.py index f60b74a4e..39ade97c2 100644 --- a/qt/aqt/forms/importing.py +++ b/qt/aqt/forms/importing.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.importing_qt6 import * -else: - from _aqt.forms.importing_qt5 import * # type: ignore +from _aqt.forms.importing_qt6 import * diff --git a/qt/aqt/forms/main.py b/qt/aqt/forms/main.py index 068804a2d..7ec7107b3 100644 --- a/qt/aqt/forms/main.py +++ b/qt/aqt/forms/main.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.main_qt6 import * -else: - from _aqt.forms.main_qt5 import * # type: ignore +from _aqt.forms.main_qt6 import * diff --git a/qt/aqt/forms/modelopts.py b/qt/aqt/forms/modelopts.py index 0e4770c92..811b1fb7b 100644 --- a/qt/aqt/forms/modelopts.py +++ b/qt/aqt/forms/modelopts.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.modelopts_qt6 import * -else: - from _aqt.forms.modelopts_qt5 import * # type: ignore +from _aqt.forms.modelopts_qt6 import * diff --git a/qt/aqt/forms/models.py b/qt/aqt/forms/models.py index fb0b64e0a..43c75c62a 100644 --- a/qt/aqt/forms/models.py +++ b/qt/aqt/forms/models.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.models_qt6 import * -else: - from _aqt.forms.models_qt5 import * # type: ignore +from _aqt.forms.models_qt6 import * diff --git a/qt/aqt/forms/preferences.py b/qt/aqt/forms/preferences.py index de9fdc989..6fdb0bfd3 100644 --- a/qt/aqt/forms/preferences.py +++ b/qt/aqt/forms/preferences.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.preferences_qt6 import * -else: - from _aqt.forms.preferences_qt5 import * # type: ignore +from _aqt.forms.preferences_qt6 import * diff --git a/qt/aqt/forms/preview.py b/qt/aqt/forms/preview.py index ca938a396..bf735bd39 100644 --- a/qt/aqt/forms/preview.py +++ b/qt/aqt/forms/preview.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.preview_qt6 import * -else: - from _aqt.forms.preview_qt5 import * # type: ignore +from _aqt.forms.preview_qt6 import * diff --git a/qt/aqt/forms/profiles.py b/qt/aqt/forms/profiles.py index c7bcc10e1..7d5b8d6e0 100644 --- a/qt/aqt/forms/profiles.py +++ b/qt/aqt/forms/profiles.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.profiles_qt6 import * -else: - from _aqt.forms.profiles_qt5 import * # type: ignore +from _aqt.forms.profiles_qt6 import * diff --git a/qt/aqt/forms/progress.py b/qt/aqt/forms/progress.py index 47a57ce49..7a2a332d5 100644 --- a/qt/aqt/forms/progress.py +++ b/qt/aqt/forms/progress.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.progress_qt6 import * -else: - from _aqt.forms.progress_qt5 import * # type: ignore +from _aqt.forms.progress_qt6 import * diff --git a/qt/aqt/forms/reposition.py b/qt/aqt/forms/reposition.py index 646abf7c4..cfad6b55a 100644 --- a/qt/aqt/forms/reposition.py +++ b/qt/aqt/forms/reposition.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.reposition_qt6 import * -else: - from _aqt.forms.reposition_qt5 import * # type: ignore +from _aqt.forms.reposition_qt6 import * diff --git a/qt/aqt/forms/setgroup.py b/qt/aqt/forms/setgroup.py index 649e4f75a..617ef3b96 100644 --- a/qt/aqt/forms/setgroup.py +++ b/qt/aqt/forms/setgroup.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.setgroup_qt6 import * -else: - from _aqt.forms.setgroup_qt5 import * # type: ignore +from _aqt.forms.setgroup_qt6 import * diff --git a/qt/aqt/forms/setlang.py b/qt/aqt/forms/setlang.py index bb715ff92..efe14343b 100644 --- a/qt/aqt/forms/setlang.py +++ b/qt/aqt/forms/setlang.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.setlang_qt6 import * -else: - from _aqt.forms.setlang_qt5 import * # type: ignore +from _aqt.forms.setlang_qt6 import * diff --git a/qt/aqt/forms/stats.py b/qt/aqt/forms/stats.py index 212c03345..12b161f4e 100644 --- a/qt/aqt/forms/stats.py +++ b/qt/aqt/forms/stats.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.stats_qt6 import * -else: - from _aqt.forms.stats_qt5 import * # type: ignore +from _aqt.forms.stats_qt6 import * diff --git a/qt/aqt/forms/studydeck.py b/qt/aqt/forms/studydeck.py index b95bc7e87..497ab01cf 100644 --- a/qt/aqt/forms/studydeck.py +++ b/qt/aqt/forms/studydeck.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.studydeck_qt6 import * -else: - from _aqt.forms.studydeck_qt5 import * # type: ignore +from _aqt.forms.studydeck_qt6 import * diff --git a/qt/aqt/forms/synclog.py b/qt/aqt/forms/synclog.py index 97fefe300..ddd08456b 100644 --- a/qt/aqt/forms/synclog.py +++ b/qt/aqt/forms/synclog.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.synclog_qt6 import * -else: - from _aqt.forms.synclog_qt5 import * # type: ignore +from _aqt.forms.synclog_qt6 import * diff --git a/qt/aqt/forms/taglimit.py b/qt/aqt/forms/taglimit.py index 7a4763016..88262c657 100644 --- a/qt/aqt/forms/taglimit.py +++ b/qt/aqt/forms/taglimit.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.taglimit_qt6 import * -else: - from _aqt.forms.taglimit_qt5 import * # type: ignore +from _aqt.forms.taglimit_qt6 import * diff --git a/qt/aqt/forms/template.py b/qt/aqt/forms/template.py index 84f3d2a05..7540d72e0 100644 --- a/qt/aqt/forms/template.py +++ b/qt/aqt/forms/template.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.template_qt6 import * -else: - from _aqt.forms.template_qt5 import * # type: ignore +from _aqt.forms.template_qt6 import * diff --git a/qt/aqt/forms/widgets.py b/qt/aqt/forms/widgets.py index b91f7ae26..07dc11c6c 100644 --- a/qt/aqt/forms/widgets.py +++ b/qt/aqt/forms/widgets.py @@ -1,8 +1 @@ -from typing import TYPE_CHECKING - -from aqt.qt import qtmajor - -if qtmajor > 5 or TYPE_CHECKING: - from _aqt.forms.widgets_qt6 import * -else: - from _aqt.forms.widgets_qt5 import * # type: ignore +from _aqt.forms.widgets_qt6 import * diff --git a/qt/aqt/profiles.py b/qt/aqt/profiles.py index 273e6df3a..6597f6705 100644 --- a/qt/aqt/profiles.py +++ b/qt/aqt/profiles.py @@ -189,11 +189,8 @@ class ProfileManager: # return the bytes directly return args[0] elif name == "_unpickle_enum": - if qtmajor == 5: - return sip._unpickle_enum(module, klass, args) # type: ignore - else: - # old style enums can't be unpickled - return None + # old style enums can't be unpickled + return None else: return sip._unpickle_type(module, klass, args) # type: ignore diff --git a/qt/aqt/progress.py b/qt/aqt/progress.py index fbb0a7470..8c45c44ee 100644 --- a/qt/aqt/progress.py +++ b/qt/aqt/progress.py @@ -300,8 +300,7 @@ class ProgressManager: def _closeWin(self) -> None: # if the parent window has been deleted, the progress dialog may have # already been dropped; delete it if it hasn't been - if not sip.isdeleted(self._win): - assert self._win is not None + if self._win and not sip.isdeleted(self._win): self._win.cancel() self._win = None self._shown = 0 diff --git a/qt/aqt/qt/__init__.py b/qt/aqt/qt/__init__.py index ea1b4bd46..11670e90c 100644 --- a/qt/aqt/qt/__init__.py +++ b/qt/aqt/qt/__init__.py @@ -11,20 +11,12 @@ import traceback from collections.abc import Callable from typing import TypeVar, Union -try: - import PyQt6 -except Exception: - from .qt5 import * # type: ignore -else: - if os.getenv("ENABLE_QT5_COMPAT"): - print("Running with temporary Qt5 compatibility shims.") - from . import qt5_compat # needs to be imported first - from .qt6 import * +from anki._legacy import deprecated +# legacy code depends on these re-exports from anki.utils import is_mac, is_win -# fix buggy ubuntu12.04 display of language selector -os.environ["LIBOVERLAY_SCROLLBAR"] = "0" +from .qt6 import * def debug() -> None: @@ -52,7 +44,7 @@ qtminor = _version.minorVersion() qtpoint = _version.microVersion() qtfullversion = _version.segments() -if qtmajor < 5 or (qtmajor == 5 and qtminor < 14): +if qtmajor == 6 and qtminor < 2: raise Exception("Anki does not support your Qt version.") @@ -64,11 +56,6 @@ def qconnect(signal: Callable | pyqtSignal | pyqtBoundSignal, func: Callable) -> _T = TypeVar("_T") +@deprecated(info="no longer required, and now a no-op") def without_qt5_compat_wrapper(cls: _T) -> _T: - """Remove Qt5 compat wrapper from Qt class, if active. - - Only needed for a few Qt APIs that deal with QVariants.""" - if fn := getattr(cls, "_without_compat_wrapper", None): - return fn() - else: - return cls + return cls diff --git a/qt/aqt/qt/qt5.py b/qt/aqt/qt/qt5.py deleted file mode 100644 index 0a45dffb9..000000000 --- a/qt/aqt/qt/qt5.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright: Ankitects Pty Ltd and contributors -# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -# make sure not to optimize imports on this file -# pylint: skip-file - -""" -PyQt5 imports -""" - -from PyQt5.QtCore import * # type: ignore -from PyQt5.QtGui import * # type: ignore -from PyQt5.QtNetwork import QLocalServer, QLocalSocket, QNetworkProxy # type: ignore -from PyQt5.QtWebChannel import QWebChannel # type: ignore -from PyQt5.QtWebEngineCore import * # type: ignore -from PyQt5.QtWebEngineWidgets import * # type: ignore -from PyQt5.QtWidgets import * # type: ignore - -try: - from PyQt5 import sip # type: ignore -except ImportError: - import sip # type: ignore diff --git a/qt/aqt/qt/qt5_audio.py b/qt/aqt/qt/qt5_audio.py deleted file mode 100644 index cc8426a6e..000000000 --- a/qt/aqt/qt/qt5_audio.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright: Ankitects Pty Ltd and contributors -# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -# pylint: skip-file - -""" -PyQt5-only audio code -""" - -import wave -from collections.abc import Callable -from concurrent.futures import Future -from typing import cast - -import aqt - -from . import * # isort:skip -from ..sound import Recorder # isort:skip -from ..utils import showWarning # isort:skip - - -class QtAudioInputRecorder(Recorder): - def __init__(self, output_path: str, mw: aqt.AnkiQt, parent: QWidget) -> None: - super().__init__(output_path) - - self.mw = mw - self._parent = parent - - from PyQt5.QtMultimedia import ( # type: ignore - QAudioDeviceInfo, - QAudioFormat, - QAudioInput, - ) - - format = QAudioFormat() - format.setChannelCount(1) - format.setSampleRate(44100) - format.setSampleSize(16) - format.setCodec("audio/pcm") - format.setByteOrder(QAudioFormat.LittleEndian) - format.setSampleType(QAudioFormat.SignedInt) - - device = QAudioDeviceInfo.defaultInputDevice() - if not device.isFormatSupported(format): - format = device.nearestFormat(format) - print("format changed") - print("channels", format.channelCount()) - print("rate", format.sampleRate()) - print("size", format.sampleSize()) - self._format = format - - self._audio_input = QAudioInput(device, format, parent) - - def start(self, on_done: Callable[[], None]) -> None: - self._iodevice = self._audio_input.start() - self._buffer = bytearray() - qconnect(self._iodevice.readyRead, self._on_read_ready) - super().start(on_done) - - def _on_read_ready(self) -> None: - self._buffer.extend(cast(bytes, self._iodevice.readAll())) - - def stop(self, on_done: Callable[[str], None]) -> None: - def on_stop_timer() -> None: - # read anything remaining in buffer & stop - self._on_read_ready() - self._audio_input.stop() - - if err := self._audio_input.error(): - showWarning(f"recording failed: {err}") - return - - def write_file() -> None: - # swallow the first 300ms to allow audio device to quiesce - wait = int(44100 * self.STARTUP_DELAY) - if len(self._buffer) <= wait: - return - self._buffer = self._buffer[wait:] - - # write out the wave file - wf = wave.open(self.output_path, "wb") - wf.setnchannels(self._format.channelCount()) - wf.setsampwidth(self._format.sampleSize() // 8) - wf.setframerate(self._format.sampleRate()) - wf.writeframes(self._buffer) - wf.close() - - def and_then(fut: Future) -> None: - fut.result() - Recorder.stop(self, on_done) - - self.mw.taskman.run_in_background(write_file, and_then) - - # schedule the stop for half a second in the future, - # to avoid truncating the end of the recording - self._stop_timer = t = QTimer(self._parent) - t.timeout.connect(on_stop_timer) # type: ignore - t.setSingleShot(True) - t.start(500) diff --git a/qt/aqt/qt/qt5_compat.py b/qt/aqt/qt/qt5_compat.py deleted file mode 100644 index ef281b87c..000000000 --- a/qt/aqt/qt/qt5_compat.py +++ /dev/null @@ -1,411 +0,0 @@ -# Copyright: Ankitects Pty Ltd and contributors -# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -# type: ignore -# pylint: disable=unused-import - -""" -Patches and aliases that provide a PyQt5 → PyQt6 compatibility shim for add-ons -""" - -import sys -import types -import typing - -import PyQt6.QtCore -import PyQt6.QtDBus -import PyQt6.QtGui -import PyQt6.QtNetwork -import PyQt6.QtPrintSupport -import PyQt6.QtWebChannel -import PyQt6.QtWebEngineCore -import PyQt6.QtWebEngineWidgets -import PyQt6.QtWidgets - -from anki._legacy import print_deprecation_warning - -# Globally alias PyQt5 to PyQt6 -# ######################################################################### - -sys.modules["PyQt5"] = PyQt6 -# Need to alias QtCore explicitly as sip otherwise complains about repeat registration -sys.modules["PyQt5.QtCore"] = PyQt6.QtCore -# Need to alias QtWidgets and QtGui explicitly to facilitate patches -sys.modules["PyQt5.QtGui"] = PyQt6.QtGui -sys.modules["PyQt5.QtWidgets"] = PyQt6.QtWidgets -# Needed to maintain import order between QtWebEngineWidgets and QCoreApplication: -sys.modules["PyQt5.QtWebEngineWidgets"] = PyQt6.QtWebEngineWidgets -# Register other aliased top-level Qt modules just to be safe: -sys.modules["PyQt5.QtWebEngineCore"] = PyQt6.QtWebEngineCore -sys.modules["PyQt5.QtWebChannel"] = PyQt6.QtWebChannel -sys.modules["PyQt5.QtNetwork"] = PyQt6.QtNetwork -# Alias sip -sys.modules["sip"] = PyQt6.sip - -# Restore QWebEnginePage.view() -# ######################################################################## - -from PyQt6.QtWebEngineCore import QWebEnginePage -from PyQt6.QtWebEngineWidgets import QWebEngineView - - -def qwebenginepage_view(page: QWebEnginePage) -> QWebEnginePage: - print_deprecation_warning( - "'QWebEnginePage.view()' is deprecated. " - "Please use 'QWebEngineView.forPage(page)'" - ) - return QWebEngineView.forPage(page) - - -PyQt6.QtWebEngineCore.QWebEnginePage.view = qwebenginepage_view - -# Alias removed exec_ methods to exec -# ######################################################################## - -from PyQt6.QtCore import QCoreApplication, QEventLoop, QThread -from PyQt6.QtGui import QDrag, QGuiApplication -from PyQt6.QtWidgets import QApplication, QDialog, QMenu - - -# This helper function is needed as aliasing exec_ to exec directly will cause -# an unbound method error, even when wrapped with types.MethodType -def qt_exec_(object, *args, **kwargs): - class_name = object.__class__.__name__ - print_deprecation_warning( - f"'{class_name}.exec_()' is deprecated. Please use '{class_name}.exec()'" - ) - return object.exec(*args, **kwargs) - - -QCoreApplication.exec_ = qt_exec_ -QEventLoop.exec_ = qt_exec_ -QThread.exec_ = qt_exec_ -QDrag.exec_ = qt_exec_ -QGuiApplication.exec_ = qt_exec_ -QApplication.exec_ = qt_exec_ -QDialog.exec_ = qt_exec_ -QMenu.exec_ = qt_exec_ - -# Graciously handle removed Qt resource system -# ######################################################################## - -# Given that add-ons mostly use the Qt resource system to equip UI elements with -# icons – which oftentimes are not essential to the core UX –, printing a warning -# instead of preventing the add-on from loading seems appropriate. - - -def qt_resource_system_call(*args, **kwargs): - print_deprecation_warning( - "The Qt resource system no longer works on PyQt6. " - "Use QDir.addSearchPath() or mw.addonManager.setWebExports() instead." - ) - - -PyQt6.QtCore.qRegisterResourceData = qt_resource_system_call -PyQt6.QtCore.qUnregisterResourceData = qt_resource_system_call - -# Patch unscoped enums back in, aliasing them to scoped enums -# ######################################################################## - -PyQt6.QtWidgets.QDockWidget.AllDockWidgetFeatures = ( - PyQt6.QtWidgets.QDockWidget.DockWidgetFeature.DockWidgetClosable - | PyQt6.QtWidgets.QDockWidget.DockWidgetFeature.DockWidgetMovable - | PyQt6.QtWidgets.QDockWidget.DockWidgetFeature.DockWidgetFloatable -) - -# when we subclass QIcon, icons fail to show when returned by getData() -# in a tableview/treeview, so we need to manually alias these -PyQt6.QtGui.QIcon.Active = PyQt6.QtGui.QIcon.Mode.Active -PyQt6.QtGui.QIcon.Disabled = PyQt6.QtGui.QIcon.Mode.Disabled -PyQt6.QtGui.QIcon.Normal = PyQt6.QtGui.QIcon.Mode.Normal -PyQt6.QtGui.QIcon.Selected = PyQt6.QtGui.QIcon.Mode.Selected -PyQt6.QtGui.QIcon.Off = PyQt6.QtGui.QIcon.State.Off -PyQt6.QtGui.QIcon.On = PyQt6.QtGui.QIcon.State.On - -# This is the subset of enums used in all public Anki add-ons as of 2021-10-19. -# Please note that this list is likely to be incomplete as the process used to -# find them probably missed dynamically constructed enums. -# Also, as mostly only public Anki add-ons were taken into consideration, -# some enums in other add-ons might not be included. In those cases please -# consider filing a PR to extend the assignments below. - -# Important: These patches are not meant to provide compatibility for all -# add-ons going forward, but simply to maintain support with already -# existing add-ons. Add-on authors should take heed to use scoped enums -# in any future code changes. - -# (module, [(type_name, enums)]) -_enum_map = ( - ( - PyQt6.QtCore, - [ - ("QEvent", ("Type",)), - ("QEventLoop", ("ProcessEventsFlag",)), - ("QIODevice", ("OpenModeFlag",)), - ("QItemSelectionModel", ("SelectionFlag",)), - ("QLocale", ("Country", "Language")), - ("QMetaType", ("Type",)), - ("QProcess", ("ProcessState", "ProcessChannel")), - ("QStandardPaths", ("StandardLocation",)), - ( - "Qt", - ( - "AlignmentFlag", - "ApplicationAttribute", - "ArrowType", - "AspectRatioMode", - "BrushStyle", - "CaseSensitivity", - "CheckState", - "ConnectionType", - "ContextMenuPolicy", - "CursorShape", - "DateFormat", - "DayOfWeek", - "DockWidgetArea", - "FindChildOption", - "FocusPolicy", - "FocusReason", - "GlobalColor", - "HighDpiScaleFactorRoundingPolicy", - "ImageConversionFlag", - "InputMethodHint", - "ItemDataRole", - "ItemFlag", - "KeyboardModifier", - "LayoutDirection", - "MatchFlag", - "Modifier", - "MouseButton", - "Orientation", - "PenCapStyle", - "PenJoinStyle", - "PenStyle", - "ScrollBarPolicy", - "ShortcutContext", - "SortOrder", - "TextElideMode", - "TextFlag", - "TextFormat", - "TextInteractionFlag", - "ToolBarArea", - "ToolButtonStyle", - "TransformationMode", - "WidgetAttribute", - "WindowModality", - "WindowState", - "WindowType", - "Key", - ), - ), - ("QThread", ("Priority",)), - ], - ), - (PyQt6.QtDBus, [("QDBus", ("CallMode",))]), - ( - PyQt6.QtGui, - [ - ("QAction", ("MenuRole", "ActionEvent")), - ("QClipboard", ("Mode",)), - ("QColor", ("NameFormat",)), - ("QFont", ("Style", "Weight", "StyleHint")), - ("QFontDatabase", ("WritingSystem", "SystemFont")), - ("QImage", ("Format",)), - ("QKeySequence", ("SequenceFormat", "StandardKey")), - ("QMovie", ("CacheMode",)), - ("QPageLayout", ("Orientation",)), - ("QPageSize", ("PageSizeId",)), - ("QPainter", ("RenderHint",)), - ("QPalette", ("ColorRole", "ColorGroup")), - ("QTextCharFormat", ("UnderlineStyle",)), - ("QTextCursor", ("MoveOperation", "MoveMode", "SelectionType")), - ("QTextFormat", ("Property",)), - ("QTextOption", ("WrapMode",)), - ("QValidator", ("State",)), - ], - ), - (PyQt6.QtNetwork, [("QHostAddress", ("SpecialAddress",))]), - (PyQt6.QtPrintSupport, [("QPrinter", ("Unit",))]), - ( - PyQt6.QtWebEngineCore, - [ - ("QWebEnginePage", ("WebWindowType", "FindFlag", "WebAction")), - ("QWebEngineProfile", ("PersistentCookiesPolicy", "HttpCacheType")), - ("QWebEngineScript", ("ScriptWorldId", "InjectionPoint")), - ("QWebEngineSettings", ("FontSize", "WebAttribute")), - ], - ), - ( - PyQt6.QtWidgets, - [ - ( - "QAbstractItemView", - ( - "CursorAction", - "DropIndicatorPosition", - "ScrollMode", - "EditTrigger", - "SelectionMode", - "SelectionBehavior", - "DragDropMode", - "ScrollHint", - ), - ), - ("QAbstractScrollArea", ("SizeAdjustPolicy",)), - ("QAbstractSpinBox", ("ButtonSymbols",)), - ("QBoxLayout", ("Direction",)), - ("QColorDialog", ("ColorDialogOption",)), - ("QComboBox", ("SizeAdjustPolicy", "InsertPolicy")), - ("QCompleter", ("CompletionMode",)), - ("QDateTimeEdit", ("Section",)), - ("QDialog", ("DialogCode",)), - ("QDialogButtonBox", ("StandardButton", "ButtonRole")), - ("QDockWidget", ("DockWidgetFeature",)), - ("QFileDialog", ("Option", "FileMode", "AcceptMode", "DialogLabel")), - ("QFormLayout", ("FieldGrowthPolicy", "ItemRole")), - ("QFrame", ("Shape", "Shadow")), - ("QGraphicsItem", ("GraphicsItemFlag",)), - ("QGraphicsPixmapItem", ("ShapeMode",)), - ("QGraphicsView", ("ViewportAnchor", "DragMode")), - ("QHeaderView", ("ResizeMode",)), - ("QLayout", ("SizeConstraint",)), - ("QLineEdit", ("EchoMode",)), - ( - "QListView", - ("Flow", "BrowserLayout", "ResizeMode", "Movement", "ViewMode"), - ), - ("QListWidgetItem", ("ItemType",)), - ("QMessageBox", ("StandardButton", "Icon", "ButtonRole")), - ("QPlainTextEdit", ("LineWrapMode",)), - ("QProgressBar", ("Direction",)), - ("QRubberBand", ("Shape",)), - ("QSizePolicy", ("ControlType", "Policy")), - ("QSlider", ("TickPosition",)), - ( - "QStyle", - ( - "SubElement", - "ComplexControl", - "StandardPixmap", - "ControlElement", - "PixelMetric", - "StateFlag", - "SubControl", - ), - ), - ("QSystemTrayIcon", ("MessageIcon", "ActivationReason")), - ("QTabBar", ("ButtonPosition",)), - ("QTabWidget", ("TabShape", "TabPosition")), - ("QTextEdit", ("LineWrapMode",)), - ("QToolButton", ("ToolButtonPopupMode",)), - ("QWizard", ("WizardStyle", "WizardOption")), - ], - ), -) - -_renamed_enum_cases = { - "QComboBox": { - "AdjustToMinimumContentsLength": "AdjustToMinimumContentsLengthWithIcon" - }, - "QDialogButtonBox": {"No": "NoButton"}, - "QPainter": {"HighQualityAntialiasing": "Antialiasing"}, - "QPalette": {"Background": "Window", "Foreground": "WindowText"}, - "Qt": {"MatchRegExp": "MatchRegularExpression", "MidButton": "MiddleButton"}, -} - - -# This works by wrapping each enum-containing Qt class (eg QAction) in a proxy. -# When an attribute is missing from the underlying Qt class, __getattr__ is -# called, and we try fetching the attribute from each of the declared enums -# for that module. If a match is found, a deprecation warning is printed. -# -# Looping through enumerations is not particularly efficient on a large type like -# Qt, but we only pay the cost when an attribute is not found. In the worst case, -# it's about 50ms per 1000 failed lookups on the Qt module. - - -def _instrument_type( - module: types.ModuleType, type_name: str, enums: list[str] -) -> None: - type = getattr(module, type_name) - renamed_attrs = _renamed_enum_cases.get(type_name, {}) - - class QtClassProxyType(type.__class__): - def __getattr__(cls, provided_name): # pylint: disable=no-self-argument - # we know this is not an enum - if provided_name == "__pyqtSignature__": - raise AttributeError - - name = renamed_attrs.get(provided_name) or provided_name - - for enum_name in enums: - enum = getattr(type, enum_name) - try: - val = getattr(enum, name) - except AttributeError: - continue - - print_deprecation_warning( - f"'{type_name}.{provided_name}' will stop working. Please use '{type_name}.{enum_name}.{name}' instead." - ) - return val - - return getattr(type, name) - - class QtClassProxy( - type, metaclass=QtClassProxyType - ): # pylint: disable=invalid-metaclass - @staticmethod - def _without_compat_wrapper(): - return type - - setattr(module, type_name, QtClassProxy) - - -for module, type_to_enum_list in _enum_map: - for type_name, enums in type_to_enum_list: - _instrument_type(module, type_name, enums) - -# Alias classes shifted between QtWidgets and QtGui -########################################################################## - -PyQt6.QtWidgets.QAction = PyQt6.QtGui.QAction -PyQt6.QtWidgets.QActionGroup = PyQt6.QtGui.QActionGroup -PyQt6.QtWidgets.QShortcut = PyQt6.QtGui.QShortcut - -# Alias classes shifted between QtWebEngineWidgets and QtWebEngineCore -########################################################################## - -PyQt6.QtWebEngineWidgets.QWebEnginePage = PyQt6.QtWebEngineCore.QWebEnginePage -PyQt6.QtWebEngineWidgets.QWebEngineHistory = PyQt6.QtWebEngineCore.QWebEngineHistory -PyQt6.QtWebEngineWidgets.QWebEngineProfile = PyQt6.QtWebEngineCore.QWebEngineProfile -PyQt6.QtWebEngineWidgets.QWebEngineScript = PyQt6.QtWebEngineCore.QWebEngineScript -PyQt6.QtWebEngineWidgets.QWebEngineScriptCollection = ( - PyQt6.QtWebEngineCore.QWebEngineScriptCollection -) -PyQt6.QtWebEngineWidgets.QWebEngineClientCertificateSelection = ( - PyQt6.QtWebEngineCore.QWebEngineClientCertificateSelection -) -PyQt6.QtWebEngineWidgets.QWebEngineSettings = PyQt6.QtWebEngineCore.QWebEngineSettings -PyQt6.QtWebEngineWidgets.QWebEngineFullScreenRequest = ( - PyQt6.QtWebEngineCore.QWebEngineFullScreenRequest -) -PyQt6.QtWebEngineWidgets.QWebEngineContextMenuData = ( - PyQt6.QtWebEngineCore.QWebEngineContextMenuRequest -) -PyQt6.QtWebEngineWidgets.QWebEngineDownloadItem = ( - PyQt6.QtWebEngineCore.QWebEngineDownloadRequest -) - -# Aliases for other miscellaneous class changes -########################################################################## - -PyQt6.QtCore.QRegExp = PyQt6.QtCore.QRegularExpression - - -# Mock the removed PyQt5.Qt module -########################################################################## - -sys.modules["PyQt5.Qt"] = sys.modules["aqt.qt"] -# support 'from PyQt5 import Qt', as it's an alias to PyQt6 -PyQt6.Qt = sys.modules["aqt.qt"] diff --git a/qt/aqt/sound.py b/qt/aqt/sound.py index 5753ab234..8ff49024f 100644 --- a/qt/aqt/sound.py +++ b/qt/aqt/sound.py @@ -772,19 +772,14 @@ class RecordDialog(QDialog): saveGeom(self, "audioRecorder2") def _start_recording(self) -> None: - if qtmajor > 5: - if macos_helper and platform.machine() == "arm64": - self._recorder = NativeMacRecorder( - namedtmp("rec.wav"), - ) - else: - self._recorder = QtAudioInputRecorder( - namedtmp("rec.wav"), self.mw, self._parent - ) + if macos_helper and platform.machine() == "arm64": + self._recorder = NativeMacRecorder( + namedtmp("rec.wav"), + ) else: - from aqt.qt.qt5_audio import QtAudioInputRecorder as Qt5Recorder - - self._recorder = Qt5Recorder(namedtmp("rec.wav"), self.mw, self._parent) + self._recorder = QtAudioInputRecorder( + namedtmp("rec.wav"), self.mw, self._parent + ) self._recorder.start(self._start_timer) def _start_timer(self) -> None: diff --git a/qt/tools/build_ui.py b/qt/tools/build_ui.py index 776375598..b87031213 100644 --- a/qt/tools/build_ui.py +++ b/qt/tools/build_ui.py @@ -6,16 +6,10 @@ from __future__ import annotations import io import re import sys +from dataclasses import dataclass from pathlib import Path -try: - from PyQt6.uic import compileUi -except ImportError: - # ARM64 Linux builds may not have access to PyQt6, and may have aliased - # it to PyQt5. We allow fallback, but the _qt6.py files will not be valid. - from PyQt5.uic import compileUi # type: ignore - -from dataclasses import dataclass +from PyQt6.uic import compileUi def compile(ui_file: str | Path) -> str: @@ -53,21 +47,9 @@ def with_fixes_for_qt6(code: str) -> str: return "\n".join(outlines) -def with_fixes_for_qt5(code: str) -> str: - code = code.replace( - "from PyQt5 import QtCore, QtGui, QtWidgets", - "from PyQt5 import QtCore, QtGui, QtWidgets\nfrom aqt.utils import tr\n", - ) - code = code.replace("Qt6", "Qt5") - code = code.replace("QtGui.QAction", "QtWidgets.QAction") - code = code.replace("import icons_rc", "") - return code - - @dataclass class UiFileAndOutputs: ui_file: Path - qt5_file: str qt6_file: str @@ -82,7 +64,6 @@ def get_files() -> list[UiFileAndOutputs]: out.append( UiFileAndOutputs( ui_file=path, - qt5_file=outpath.replace(".ui", "_qt5.py"), qt6_file=outpath.replace(".ui", "_qt6.py"), ) ) @@ -93,8 +74,5 @@ if __name__ == "__main__": for entry in get_files(): stock = compile(entry.ui_file) for_qt6 = with_fixes_for_qt6(stock) - for_qt5 = with_fixes_for_qt5(for_qt6) - with open(entry.qt5_file, "w") as file: - file.write(for_qt5) with open(entry.qt6_file, "w") as file: file.write(for_qt6) From a41c60c016065b792a3ea894ad8bef6929af90c4 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Thu, 19 Jun 2025 22:17:11 +0700 Subject: [PATCH 010/101] Trigger uv sync if user approves update --- ftl/qt/qt-misc.ftl | 1 + qt/aqt/update.py | 38 +++++++++++++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/ftl/qt/qt-misc.ftl b/ftl/qt/qt-misc.ftl index 294cd8a83..60c22ef8b 100644 --- a/ftl/qt/qt-misc.ftl +++ b/ftl/qt/qt-misc.ftl @@ -73,6 +73,7 @@ qt-misc-second = qt-misc-layout-auto-enabled = Responsive layout enabled qt-misc-layout-vertical-enabled = Vertical layout enabled qt-misc-layout-horizontal-enabled = Horizontal layout enabled +qt-misc-please-restart-to-update-anki = Please restart Anki to update to the latest version. ## deprecated- these strings will be removed in the future, and do not need ## to be translated diff --git a/qt/aqt/update.py b/qt/aqt/update.py index fd0e4eafd..d8e92426c 100644 --- a/qt/aqt/update.py +++ b/qt/aqt/update.py @@ -1,13 +1,16 @@ # Copyright: Ankitects Pty Ltd and contributors # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +import os +from pathlib import Path + import aqt from anki.buildinfo import buildhash from anki.collection import CheckForUpdateResponse, Collection -from anki.utils import dev_mode, int_time, int_version, plat_desc +from anki.utils import dev_mode, int_time, int_version, is_mac, is_win, plat_desc from aqt.operations import QueryOp from aqt.qt import * -from aqt.utils import openLink, show_warning, showText, tr +from aqt.utils import show_info, show_warning, showText, tr def check_for_update() -> None: @@ -77,4 +80,33 @@ def prompt_to_update(mw: aqt.AnkiQt, ver: str) -> None: # ignore this update mw.pm.meta["suppressUpdate"] = ver elif ret == QMessageBox.StandardButton.Yes: - openLink(aqt.appWebsiteDownloadSection) + update_and_restart() + + +def update_and_restart() -> None: + """Download and install the update, then restart Anki.""" + update_on_next_run() + # todo: do this automatically in the future + show_info(tr.qt_misc_please_restart_to_update_anki()) + + +def update_on_next_run() -> None: + """Bump the mtime on pyproject.toml in the local data directory to trigger an update on next run.""" + try: + # Get the local data directory equivalent to Rust's dirs::data_local_dir() + if is_win: + data_dir = Path(os.environ.get("LOCALAPPDATA", "")) + elif is_mac: + data_dir = Path.home() / "Library" / "Application Support" + else: # Linux + data_dir = Path( + os.environ.get("XDG_DATA_HOME", Path.home() / ".local" / "share") + ) + + pyproject_path = data_dir / "AnkiProgramFiles" / "pyproject.toml" + + if pyproject_path.exists(): + # Touch the file to update its mtime + pyproject_path.touch() + except Exception as e: + print(e) From a60a955c611bcc2d4435c0b9ba5975d2ab2c1c6c Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Thu, 19 Jun 2025 23:33:51 +0700 Subject: [PATCH 011/101] Handle beta/rc tags, bump beta, add exact version pin to aqt --- .version | 2 +- build/configure/src/python.rs | 70 +++++++++++++++++++++++++++++++---- pylib/pyproject.toml | 3 +- qt/hatch_build.py | 26 +++++++++++++ qt/pyproject.toml | 4 +- tools/publish | 6 ++- 6 files changed, 96 insertions(+), 15 deletions(-) diff --git a/.version b/.version index 9bab2a4b4..d1e34a036 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -25.06 +25.06b2 diff --git a/build/configure/src/python.rs b/build/configure/src/python.rs index 7cd66b76e..474a55f31 100644 --- a/build/configure/src/python.rs +++ b/build/configure/src/python.rs @@ -20,6 +20,45 @@ use ninja_gen::python::PythonTypecheck; use ninja_gen::rsync::RsyncFiles; use ninja_gen::Build; +/// Normalize version string by removing leading zeros from numeric parts +/// while preserving pre-release markers (b1, rc2, a3, etc.) +fn normalize_version(version: &str) -> String { + version + .split('.') + .map(|part| { + // Check if the part contains only digits + if part.chars().all(|c| c.is_ascii_digit()) { + // Numeric part: remove leading zeros + part.parse::().unwrap_or(0).to_string() + } else { + // Mixed part (contains both numbers and pre-release markers) + // Split on first non-digit character and normalize the numeric prefix + let chars = part.chars(); + let mut numeric_prefix = String::new(); + let mut rest = String::new(); + let mut found_non_digit = false; + + for ch in chars { + if ch.is_ascii_digit() && !found_non_digit { + numeric_prefix.push(ch); + } else { + found_non_digit = true; + rest.push(ch); + } + } + + if numeric_prefix.is_empty() { + part.to_string() + } else { + let normalized_prefix = numeric_prefix.parse::().unwrap_or(0).to_string(); + format!("{}{}", normalized_prefix, rest) + } + } + }) + .collect::>() + .join(".") +} + pub fn setup_venv(build: &mut Build) -> Result<()> { let extra_binary_exports = &[ "mypy", @@ -131,14 +170,7 @@ impl BuildAction for BuildWheel { let name = self.name; - // Normalize version like hatchling does: remove leading zeros from version - // parts - let normalized_version = self - .version - .split('.') - .map(|part| part.parse::().unwrap_or(0).to_string()) - .collect::>() - .join("."); + let normalized_version = normalize_version(&self.version); let wheel_path = format!("wheels/{name}-{normalized_version}-{tag}.whl"); build.add_outputs("out", vec![wheel_path]); @@ -279,3 +311,25 @@ pub(crate) fn setup_sphinx(build: &mut Build) -> Result<()> { )?; Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_normalize_version_basic() { + assert_eq!(normalize_version("1.2.3"), "1.2.3"); + assert_eq!(normalize_version("01.02.03"), "1.2.3"); + assert_eq!(normalize_version("1.0.0"), "1.0.0"); + } + + #[test] + fn test_normalize_version_with_prerelease() { + assert_eq!(normalize_version("1.2.3b1"), "1.2.3b1"); + assert_eq!(normalize_version("01.02.03b1"), "1.2.3b1"); + assert_eq!(normalize_version("1.0.0rc2"), "1.0.0rc2"); + assert_eq!(normalize_version("2.1.0a3"), "2.1.0a3"); + assert_eq!(normalize_version("1.2.3beta1"), "1.2.3beta1"); + assert_eq!(normalize_version("1.2.3alpha1"), "1.2.3alpha1"); + } +} diff --git a/pylib/pyproject.toml b/pylib/pyproject.toml index a12c6848b..555f30c86 100644 --- a/pylib/pyproject.toml +++ b/pylib/pyproject.toml @@ -1,7 +1,6 @@ [project] name = "anki" -# dynamic = ["version"] -version = "0.1.2" +dynamic = ["version"] requires-python = ">=3.9" license = "AGPL-3.0-or-later" dependencies = [ diff --git a/qt/hatch_build.py b/qt/hatch_build.py index e475b5d84..52ca7a0ec 100644 --- a/qt/hatch_build.py +++ b/qt/hatch_build.py @@ -18,6 +18,9 @@ class CustomBuildHook(BuildHookInterface): """Initialize the build hook.""" force_include = build_data.setdefault("force_include", {}) + # Pin anki== + self._set_anki_dependency(version, build_data) + # Look for generated files in out/qt/_aqt project_root = Path(self.root).parent generated_root = project_root / "out" / "qt" / "_aqt" @@ -30,6 +33,29 @@ class CustomBuildHook(BuildHookInterface): assert generated_root.exists(), "you should build with --wheel" self._add_aqt_files(force_include, generated_root) + def _set_anki_dependency(self, version: str, build_data: Dict[str, Any]) -> None: + # Get current dependencies and replace 'anki' with exact version + dependencies = build_data.setdefault("dependencies", []) + + # Remove any existing anki dependency + dependencies[:] = [dep for dep in dependencies if not dep.startswith("anki")] + + # Handle version detection + actual_version = version + if version == "standard": + # Read actual version from .version file + project_root = Path(self.root).parent + version_file = project_root / ".version" + if version_file.exists(): + actual_version = version_file.read_text().strip() + + # Only add exact version for real releases, not editable installs + if actual_version != "editable": + dependencies.append(f"anki=={actual_version}") + else: + # For editable installs, just add anki without version constraint + dependencies.append("anki") + def _add_aqt_files(self, force_include: Dict[str, str], aqt_root: Path) -> None: """Add _aqt files to the build.""" for path in aqt_root.rglob("*"): diff --git a/qt/pyproject.toml b/qt/pyproject.toml index d8b4c4306..0c1d1b2ae 100644 --- a/qt/pyproject.toml +++ b/qt/pyproject.toml @@ -1,7 +1,6 @@ [project] name = "aqt" -# dynamic = ["version"] -version = "0.1.2" +dynamic = ["version"] requires-python = ">=3.9" license = "AGPL-3.0-or-later" dependencies = [ @@ -25,6 +24,7 @@ dependencies = [ "types-pywin32", "pyqt6>=6.2", "pyqt6-webengine>=6.2", + # anki dependency is added dynamically in hatch_build.py with exact version ] [project.optional-dependencies] diff --git a/tools/publish b/tools/publish index 570f75c85..4c625c36d 100755 --- a/tools/publish +++ b/tools/publish @@ -2,6 +2,8 @@ set -e -export UV_PUBLISH_TOKEN=$(pass show w/pypi-api-test) -out/extracted/uv/uv publish --index testpypi out/wheels/* +#export UV_PUBLISH_TOKEN=$(pass show w/pypi-api-test) +#out/extracted/uv/uv publish --index testpypi out/wheels/* +export UV_PUBLISH_TOKEN=$(pass show w/pypi-api) +out/extracted/uv/uv publish out/wheels/* From 4abc0eb8b843c33b2930d837e11f5d26137cb872 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 20 Jun 2025 00:45:30 +0700 Subject: [PATCH 012/101] Use same version for anki-release; publish to main index --- qt/pyproject.toml | 3 +++ qt/release/publish.sh | 6 +++--- qt/release/update.sh | 12 +++--------- uv.lock | 4 +--- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/qt/pyproject.toml b/qt/pyproject.toml index 0c1d1b2ae..0dce88c99 100644 --- a/qt/pyproject.toml +++ b/qt/pyproject.toml @@ -62,6 +62,9 @@ conflicts = [ ], ] +[tool.uv.sources] +anki = { workspace = true } + [build-system] requires = ["hatchling"] build-backend = "hatchling.build" diff --git a/qt/release/publish.sh b/qt/release/publish.sh index b906637f9..273e34953 100755 --- a/qt/release/publish.sh +++ b/qt/release/publish.sh @@ -1,7 +1,5 @@ #!/bin/bash -export UV_PUBLISH_TOKEN=$(pass show w/pypi-api-test) - # Get the project root (two levels up from qt/release) PROJ_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" @@ -10,4 +8,6 @@ UV="$PROJ_ROOT/out/extracted/uv/uv" rm -rf dist "$UV" build --wheel -"$UV" publish --index testpypi + +#UV_PUBLISH_TOKEN=$(pass show w/pypi-api-test) "$UV" publish --index testpypi +UV_PUBLISH_TOKEN=$(pass show w/pypi-api) "$UV" publish diff --git a/qt/release/update.sh b/qt/release/update.sh index e25eaec49..ddd99c398 100755 --- a/qt/release/update.sh +++ b/qt/release/update.sh @@ -13,8 +13,8 @@ PROJ_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" # Use extracted uv binary UV="$PROJ_ROOT/out/extracted/uv/uv" -# Prompt for wheel version -read -p "Wheel version: " VERSION +# Read version from .version file +VERSION=$(cat "$PROJ_ROOT/.version" | tr -d '[:space:]') # Copy existing pyproject.toml to .old if it exists if [ -f pyproject.toml ]; then @@ -24,7 +24,7 @@ fi # Export dependencies using uv echo "Exporting dependencies..." rm -f pyproject.toml -DEPS=$("$UV" export --no-hashes --no-annotate --no-header --extra audio --extra qt --all-packages --no-dev --no-emit-workspace) +DEPS=$(cd "$PROJ_ROOT" && "$UV" export --no-hashes --no-annotate --no-header --extra audio --extra qt --all-packages --no-dev --no-emit-workspace) # Generate the pyproject.toml file cat > pyproject.toml << EOF @@ -49,12 +49,6 @@ done cat >> pyproject.toml << 'EOF' ] -[[tool.uv.index]] -name = "testpypi" -url = "https://test.pypi.org/simple/" -publish-url = "https://test.pypi.org/legacy/" -explicit = true - [build-system] requires = ["hatchling"] build-backend = "hatchling.build" diff --git a/uv.lock b/uv.lock index 51b16f8ab..9b713648d 100644 --- a/uv.lock +++ b/uv.lock @@ -48,7 +48,6 @@ wheels = [ [[package]] name = "anki" -version = "0.1.2" source = { editable = "pylib" } dependencies = [ { name = "beautifulsoup4" }, @@ -151,7 +150,6 @@ wheels = [ [[package]] name = "aqt" -version = "0.1.2" source = { editable = "qt" } dependencies = [ { name = "anki-mac-helper", marker = "sys_platform == 'darwin' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, @@ -246,7 +244,7 @@ requires-dist = [ { name = "types-waitress" }, { name = "waitress", specifier = ">=2.0.0" }, ] -provides-extras = ["audio", "qt66", "qt67", "qt"] +provides-extras = ["audio", "qt", "qt66", "qt67"] [[package]] name = "astroid" From cd7193150674e636585d81bc5017fc6383539f88 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 20 Jun 2025 01:20:13 +0700 Subject: [PATCH 013/101] Launcher tweaks - Handle beta/rc tags in .version when launching Anki - Update pyproject.toml/.python_version if distributed version newer - Support prerelease marker to opt in to betas - Check for updates when using uv sync - Avoid system Python by default, as it can cause breakages (e.g. ARM Python installed on Windows) --- pylib/anki/utils.py | 9 +++++++-- qt/hatch_build.py | 6 +++--- qt/launcher/mac/build.sh | 1 + qt/launcher/pyproject.toml | 14 -------------- qt/launcher/src/main.rs | 39 +++++++++++++++++++------------------- rslib/io/src/lib.rs | 36 +++++++++++++++++++++++++++++++++++ 6 files changed, 67 insertions(+), 38 deletions(-) diff --git a/pylib/anki/utils.py b/pylib/anki/utils.py index 46daa3b97..c61fd0588 100644 --- a/pylib/anki/utils.py +++ b/pylib/anki/utils.py @@ -309,12 +309,17 @@ def int_version() -> int: """Anki's version as an integer in the form YYMMPP, e.g. 230900. (year, month, patch). In 2.1.x releases, this was just the last number.""" + import re + from anki.buildinfo import version + # Strip non-numeric characters (handles beta/rc suffixes like '25.02b1' or 'rc3') + numeric_version = re.sub(r"[^0-9.]", "", version) + try: - [year, month, patch] = version.split(".") + [year, month, patch] = numeric_version.split(".") except ValueError: - [year, month] = version.split(".") + [year, month] = numeric_version.split(".") patch = "0" year_num = int(year) diff --git a/qt/hatch_build.py b/qt/hatch_build.py index 52ca7a0ec..fc716a57f 100644 --- a/qt/hatch_build.py +++ b/qt/hatch_build.py @@ -36,10 +36,10 @@ class CustomBuildHook(BuildHookInterface): def _set_anki_dependency(self, version: str, build_data: Dict[str, Any]) -> None: # Get current dependencies and replace 'anki' with exact version dependencies = build_data.setdefault("dependencies", []) - + # Remove any existing anki dependency dependencies[:] = [dep for dep in dependencies if not dep.startswith("anki")] - + # Handle version detection actual_version = version if version == "standard": @@ -48,7 +48,7 @@ class CustomBuildHook(BuildHookInterface): version_file = project_root / ".version" if version_file.exists(): actual_version = version_file.read_text().strip() - + # Only add exact version for real releases, not editable installs if actual_version != "editable": dependencies.append(f"anki=={actual_version}") diff --git a/qt/launcher/mac/build.sh b/qt/launcher/mac/build.sh index 8a60d488d..eb4483488 100755 --- a/qt/launcher/mac/build.sh +++ b/qt/launcher/mac/build.sh @@ -8,6 +8,7 @@ APP_LAUNCHER="$OUTPUT_DIR/Anki.app" rm -rf "$APP_LAUNCHER" # Build binaries for both architectures +rustup target add aarch64-apple-darwin x86_64-apple-darwin cargo build -p launcher --release --target aarch64-apple-darwin cargo build -p launcher --release --target x86_64-apple-darwin (cd ../../.. && ./ninja launcher:uv_universal) diff --git a/qt/launcher/pyproject.toml b/qt/launcher/pyproject.toml index 6ba027844..2a45626c7 100644 --- a/qt/launcher/pyproject.toml +++ b/qt/launcher/pyproject.toml @@ -5,18 +5,4 @@ description = "UV-based launcher for Anki." requires-python = ">=3.9" dependencies = [ "anki-release", - # so we can use testpypi - "anki", - "aqt", ] - -[tool.uv.sources] -anki-release = { index = "testpypi" } -anki = { index = "testpypi" } -aqt = { index = "testpypi" } - -[[tool.uv.index]] -name = "testpypi" -url = "https://test.pypi.org/simple/" -publish-url = "https://test.pypi.org/legacy/" -explicit = true diff --git a/qt/launcher/src/main.rs b/qt/launcher/src/main.rs index 77268ce3a..e40253eff 100644 --- a/qt/launcher/src/main.rs +++ b/qt/launcher/src/main.rs @@ -6,9 +6,9 @@ use std::io::stdin; use std::process::Command; -use anki_io::copy_file; +use anki_io::copy_if_newer; use anki_io::create_dir_all; -use anki_io::metadata; +use anki_io::modified_time; use anki_io::remove_file; use anki_io::write_file; use anki_process::CommandExt; @@ -51,6 +51,7 @@ fn run() -> Result<()> { .join("AnkiProgramFiles"); let sync_complete_marker = uv_install_root.join(".sync_complete"); + let prerelease_marker = uv_install_root.join("prerelease"); let (exe_dir, resources_dir) = get_exe_and_resources_dirs()?; let dist_pyproject_path = resources_dir.join("pyproject.toml"); let user_pyproject_path = uv_install_root.join("pyproject.toml"); @@ -59,14 +60,15 @@ fn run() -> Result<()> { let uv_lock_path = uv_install_root.join("uv.lock"); let uv_path: std::path::PathBuf = exe_dir.join(get_uv_binary_name()); + // Create install directory and copy project files in + create_dir_all(&uv_install_root)?; + copy_if_newer(&dist_pyproject_path, &user_pyproject_path)?; + copy_if_newer(&dist_python_version_path, &user_python_version_path)?; + let pyproject_has_changed = !user_pyproject_path.exists() || !sync_complete_marker.exists() || { - let pyproject_toml_time = metadata(&user_pyproject_path)? - .modified() - .context("Failed to get pyproject.toml modified time")?; - let sync_complete_time = metadata(&sync_complete_marker)? - .modified() - .context("Failed to get sync marker modified time")?; + let pyproject_toml_time = modified_time(&user_pyproject_path)?; + let sync_complete_time = modified_time(&sync_complete_marker)?; Ok::(pyproject_toml_time > sync_complete_time) } .unwrap_or(true); @@ -81,22 +83,21 @@ fn run() -> Result<()> { // we'll need to launch uv; reinvoke ourselves in a terminal so the user can see handle_terminal_launch()?; - // Create install directory and copy project files in - create_dir_all(&uv_install_root)?; - if !user_pyproject_path.exists() { - copy_file(&dist_pyproject_path, &user_pyproject_path)?; - copy_file(&dist_python_version_path, &user_python_version_path)?; - } - // Remove sync marker before attempting sync let _ = remove_file(&sync_complete_marker); // Sync the venv - if let Err(e) = Command::new(&uv_path) + let mut command = Command::new(&uv_path); + command .current_dir(&uv_install_root) - .args(["sync", "--refresh"]) - .ensure_success() - { + .args(["sync", "--upgrade", "--managed-python"]); + + // Set UV_PRERELEASE=allow if prerelease file exists + if prerelease_marker.exists() { + command.env("UV_PRERELEASE", "allow"); + } + + if let Err(e) = command.ensure_success() { // If sync fails due to things like a missing wheel on pypi, // we need to remove the lockfile or uv will cache the bad result. let _ = remove_file(&uv_lock_path); diff --git a/rslib/io/src/lib.rs b/rslib/io/src/lib.rs index c1d4c0205..cb44467e6 100644 --- a/rslib/io/src/lib.rs +++ b/rslib/io/src/lib.rs @@ -152,6 +152,34 @@ pub fn copy_file(src: impl AsRef, dst: impl AsRef) -> Result { }) } +/// Copy a file from src to dst if dst doesn't exist or if src is newer than +/// dst. Preserves the modification time from the source file. +pub fn copy_if_newer(src: impl AsRef, dst: impl AsRef) -> Result { + let src = src.as_ref(); + let dst = dst.as_ref(); + + let should_copy = if !dst.exists() { + true + } else { + let src_time = modified_time(src)?; + let dst_time = modified_time(dst)?; + src_time > dst_time + }; + + if should_copy { + copy_file(src, dst)?; + + // Preserve the modification time from the source file + let src_mtime = modified_time(src)?; + let times = FileTimes::new().set_modified(src_mtime); + set_file_times(dst, times)?; + + Ok(true) + } else { + Ok(false) + } +} + /// Like [read_file], but skips the section that is potentially locked by /// SQLite. pub fn read_locked_db_file(path: impl AsRef) -> Result> { @@ -188,6 +216,14 @@ pub fn metadata(path: impl AsRef) -> Result { }) } +/// Get the modification time of a file. +pub fn modified_time(path: impl AsRef) -> Result { + metadata(&path)?.modified().context(FileIoSnafu { + path: path.as_ref(), + op: FileOp::Metadata, + }) +} + pub fn new_tempfile() -> Result { NamedTempFile::new().context(FileIoSnafu { path: std::env::temp_dir(), From d542ae90653d2a3600f34cf7b21c6991883846f8 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 20 Jun 2025 16:37:54 +0700 Subject: [PATCH 014/101] Fix check action on Windows ARM - Update nextest (not required) - Build nextest without self-update, which pulls in ring - Disable running of tests in rsbridge, as it has no tests, and requires host arch's python.lib to execute - A double \ in CARGO_TARGET_DIR was breaking update_* tests --- build/ninja_gen/src/cargo.rs | 2 +- pylib/rsbridge/Cargo.toml | 1 + tools/ninja.bat | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build/ninja_gen/src/cargo.rs b/build/ninja_gen/src/cargo.rs index 645203170..2a3397704 100644 --- a/build/ninja_gen/src/cargo.rs +++ b/build/ninja_gen/src/cargo.rs @@ -162,7 +162,7 @@ impl BuildAction for CargoTest { "cargo-nextest", CargoInstall { binary_name: "cargo-nextest", - args: "cargo-nextest --version 0.9.57 --locked", + args: "cargo-nextest --version 0.9.99 --locked --no-default-features --features default-no-update", }, )?; setup_flags(build) diff --git a/pylib/rsbridge/Cargo.toml b/pylib/rsbridge/Cargo.toml index fbe76c8a5..22dca83fa 100644 --- a/pylib/rsbridge/Cargo.toml +++ b/pylib/rsbridge/Cargo.toml @@ -12,6 +12,7 @@ description = "Anki's Rust library code Python bindings" name = "rsbridge" crate-type = ["cdylib"] path = "lib.rs" +test = false [dependencies] anki.workspace = true diff --git a/tools/ninja.bat b/tools/ninja.bat index 7d939846e..6310103c3 100755 --- a/tools/ninja.bat +++ b/tools/ninja.bat @@ -1,5 +1,5 @@ @echo off -set CARGO_TARGET_DIR=%~dp0\..\out\rust +set CARGO_TARGET_DIR=%~dp0..\out\rust REM separate build+run steps so build env doesn't leak into subprocesses cargo build -p runner --release || exit /b 1 out\rust\release\runner build %* || exit /b 1 From b2dc5a02636333a27dc862e156a598725de42f3a Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 20 Jun 2025 16:08:00 +0700 Subject: [PATCH 015/101] Bump version --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index d1e34a036..9079196d0 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -25.06b2 +25.06b4 From 718f39fdf382968348575f776aed26678f94546c Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 20 Jun 2025 19:05:30 +0700 Subject: [PATCH 016/101] Temporarily force-enable prereleases Some users are struggling to read or understand the steps to enable it. --- qt/launcher/src/main.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qt/launcher/src/main.rs b/qt/launcher/src/main.rs index e40253eff..4f397ad99 100644 --- a/qt/launcher/src/main.rs +++ b/qt/launcher/src/main.rs @@ -97,6 +97,9 @@ fn run() -> Result<()> { command.env("UV_PRERELEASE", "allow"); } + // temporarily force it on during initial beta testing + command.env("UV_PRERELEASE", "allow"); + if let Err(e) = command.ensure_success() { // If sync fails due to things like a missing wheel on pypi, // we need to remove the lockfile or uv will cache the bad result. From b781dfabf58e20b32b6f6ca8dfd1eb1a8ac1d45b Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 20 Jun 2025 21:50:27 +0700 Subject: [PATCH 017/101] Add helpers to run Qt 6.7 and 6.9 Removed the 6.8 one, as that's our default --- qt/pyproject.toml | 8 + tools/{run-qt6.8 => run-qt6.7} | 4 +- tools/run-qt6.9 | 9 + uv.lock | 338 +++++++++++++++++++++------------ 4 files changed, 237 insertions(+), 122 deletions(-) rename tools/{run-qt6.8 => run-qt6.7} (70%) create mode 100755 tools/run-qt6.9 diff --git a/qt/pyproject.toml b/qt/pyproject.toml index 0dce88c99..e6537c76c 100644 --- a/qt/pyproject.toml +++ b/qt/pyproject.toml @@ -45,6 +45,13 @@ qt67 = [ "pyqt6-webengine-qt6==6.7.3", "pyqt6_sip==13.10.2", ] +qt69 = [ + "pyqt6==6.9.1", + "pyqt6-qt6==6.9.1", + "pyqt6-webengine==6.9.0", + "pyqt6-webengine-qt6==6.9.1", + "pyqt6_sip==13.10.2", +] qt = [ "pyqt6==6.8.0", "pyqt6-qt6==6.8.1", @@ -59,6 +66,7 @@ conflicts = [ { extra = "qt" }, { extra = "qt66" }, { extra = "qt67" }, + { extra = "qt69" }, ], ] diff --git a/tools/run-qt6.8 b/tools/run-qt6.7 similarity index 70% rename from tools/run-qt6.8 rename to tools/run-qt6.7 index 1628a1e33..d01d46cea 100755 --- a/tools/run-qt6.8 +++ b/tools/run-qt6.7 @@ -4,6 +4,6 @@ set -e ./ninja extract:uv -export PYENV=./out/pyenv68 -UV_PROJECT_ENVIRONMENT=$PYENV ./out/extracted/uv/uv sync --all-packages --extra qt68 +export PYENV=./out/pyenv67 +UV_PROJECT_ENVIRONMENT=$PYENV ./out/extracted/uv/uv sync --all-packages --extra qt67 ./run $* diff --git a/tools/run-qt6.9 b/tools/run-qt6.9 new file mode 100755 index 000000000..6576b6c81 --- /dev/null +++ b/tools/run-qt6.9 @@ -0,0 +1,9 @@ +#!/bin/bash + +set -e + +./ninja extract:uv + +export PYENV=./out/pyenv69 +UV_PROJECT_ENVIRONMENT=$PYENV ./out/extracted/uv/uv sync --all-packages --extra qt69 +./run $* diff --git a/uv.lock b/uv.lock index 9b713648d..348d14dd0 100644 --- a/uv.lock +++ b/uv.lock @@ -11,6 +11,7 @@ conflicts = [[ { package = "aqt", extra = "qt" }, { package = "aqt", extra = "qt66" }, { package = "aqt", extra = "qt67" }, + { package = "aqt", extra = "qt69" }, ]] [manifest] @@ -52,11 +53,11 @@ source = { editable = "pylib" } dependencies = [ { name = "beautifulsoup4" }, { name = "decorator" }, - { name = "distro", marker = "(sys_platform != 'darwin' and sys_platform != 'win32') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "distro", marker = "(sys_platform != 'darwin' and sys_platform != 'win32') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "markdown" }, { name = "orjson" }, { name = "protobuf" }, - { name = "psutil", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "psutil", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "requests", extra = ["socks"] }, { name = "types-orjson" }, { name = "types-protobuf" }, @@ -97,9 +98,9 @@ source = { virtual = "." } [package.optional-dependencies] sphinx = [ - { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "sphinx-autoapi" }, { name = "sphinx-rtd-theme" }, ] @@ -152,22 +153,24 @@ wheels = [ name = "aqt" source = { editable = "qt" } dependencies = [ - { name = "anki-mac-helper", marker = "sys_platform == 'darwin' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "anki-mac-helper", marker = "sys_platform == 'darwin' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "beautifulsoup4" }, { name = "flask" }, { name = "flask-cors" }, { name = "jsonschema" }, { name = "mock" }, - { name = "pip-system-certs", version = "4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "pip-system-certs", version = "5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "psutil", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "pyqt6", version = "6.6.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, - { name = "pyqt6", version = "6.7.1", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra != 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, - { name = "pyqt6", version = "6.8.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt' or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt66' and extra != 'extra-3-aqt-qt67')" }, - { name = "pyqt6-webengine", version = "6.6.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, - { name = "pyqt6-webengine", version = "6.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra != 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, - { name = "pyqt6-webengine", version = "6.8.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt' or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt66' and extra != 'extra-3-aqt-qt67')" }, - { name = "pywin32", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "pip-system-certs", version = "4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pip-system-certs", version = "5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "psutil", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pyqt6", version = "6.6.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pyqt6", version = "6.7.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt67' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt' and extra != 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pyqt6", version = "6.8.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt' or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pyqt6", version = "6.9.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt69' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra != 'extra-3-aqt-qt66' and extra != 'extra-3-aqt-qt67')" }, + { name = "pyqt6-webengine", version = "6.6.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pyqt6-webengine", version = "6.7.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt67' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt' and extra != 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pyqt6-webengine", version = "6.8.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt' or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pyqt6-webengine", version = "6.9.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt69' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra != 'extra-3-aqt-qt66' and extra != 'extra-3-aqt-qt67')" }, + { name = "pywin32", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "requests" }, { name = "send2trash" }, { name = "types-decorator" }, @@ -181,7 +184,7 @@ dependencies = [ [package.optional-dependencies] audio = [ - { name = "anki-audio", marker = "sys_platform == 'darwin' or sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "anki-audio", marker = "sys_platform == 'darwin' or sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] qt = [ { name = "pyqt6", version = "6.8.0", source = { registry = "https://pypi.org/simple" } }, @@ -204,6 +207,13 @@ qt67 = [ { name = "pyqt6-webengine", version = "6.7.0", source = { registry = "https://pypi.org/simple" } }, { name = "pyqt6-webengine-qt6", version = "6.7.3", source = { registry = "https://pypi.org/simple" } }, ] +qt69 = [ + { name = "pyqt6", version = "6.9.1", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-qt6", version = "6.9.1", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-sip", version = "13.10.2", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-webengine", version = "6.9.0", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-webengine-qt6", version = "6.9.1", source = { registry = "https://pypi.org/simple" } }, +] [package.metadata] requires-dist = [ @@ -220,19 +230,24 @@ requires-dist = [ { name = "pyqt6", marker = "extra == 'qt'", specifier = "==6.8.0" }, { name = "pyqt6", marker = "extra == 'qt66'", specifier = "==6.6.1" }, { name = "pyqt6", marker = "extra == 'qt67'", specifier = "==6.7.1" }, + { name = "pyqt6", marker = "extra == 'qt69'", specifier = "==6.9.1" }, { name = "pyqt6-qt6", marker = "extra == 'qt'", specifier = "==6.8.1" }, { name = "pyqt6-qt6", marker = "extra == 'qt66'", specifier = "==6.6.2" }, { name = "pyqt6-qt6", marker = "extra == 'qt67'", specifier = "==6.7.3" }, + { name = "pyqt6-qt6", marker = "extra == 'qt69'", specifier = "==6.9.1" }, { name = "pyqt6-sip", marker = "extra == 'qt'", specifier = "==13.10.2" }, { name = "pyqt6-sip", marker = "extra == 'qt66'", specifier = "==13.6.0" }, { name = "pyqt6-sip", marker = "extra == 'qt67'", specifier = "==13.10.2" }, + { name = "pyqt6-sip", marker = "extra == 'qt69'", specifier = "==13.10.2" }, { name = "pyqt6-webengine", specifier = ">=6.2" }, { name = "pyqt6-webengine", marker = "extra == 'qt'", specifier = "==6.8.0" }, { name = "pyqt6-webengine", marker = "extra == 'qt66'", specifier = "==6.6.0" }, { name = "pyqt6-webengine", marker = "extra == 'qt67'", specifier = "==6.7.0" }, + { name = "pyqt6-webengine", marker = "extra == 'qt69'", specifier = "==6.9.0" }, { name = "pyqt6-webengine-qt6", marker = "extra == 'qt'", specifier = "==6.8.1" }, { name = "pyqt6-webengine-qt6", marker = "extra == 'qt66'", specifier = "==6.6.2" }, { name = "pyqt6-webengine-qt6", marker = "extra == 'qt67'", specifier = "==6.7.3" }, + { name = "pyqt6-webengine-qt6", marker = "extra == 'qt69'", specifier = "==6.9.1" }, { name = "pywin32", marker = "sys_platform == 'win32'" }, { name = "requests" }, { name = "send2trash" }, @@ -244,14 +259,14 @@ requires-dist = [ { name = "types-waitress" }, { name = "waitress", specifier = ">=2.0.0" }, ] -provides-extras = ["audio", "qt", "qt66", "qt67"] +provides-extras = ["audio", "qt", "qt66", "qt67", "qt69"] [[package]] name = "astroid" version = "3.3.10" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/00/c2/9b2de9ed027f9fe5734a6c0c0a601289d796b3caaf1e372e23fa88a73047/astroid-3.3.10.tar.gz", hash = "sha256:c332157953060c6deb9caa57303ae0d20b0fbdb2e59b4a4f2a6ba49d0a7961ce", size = 398941, upload-time = "2025-05-10T13:33:10.405Z" } wheels = [ @@ -294,14 +309,14 @@ name = "black" version = "25.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "mypy-extensions" }, { name = "packaging" }, { name = "pathspec" }, { name = "platformdirs" }, - { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449, upload-time = "2025-01-29T04:15:40.373Z" } wheels = [ @@ -428,7 +443,7 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "colorama", marker = "(python_full_version < '3.10' and sys_platform == 'win32') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "colorama", marker = "(python_full_version < '3.10' and sys_platform == 'win32') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } wheels = [ @@ -445,7 +460,7 @@ resolution-markers = [ "python_full_version == '3.10.*'", ] dependencies = [ - { name = "colorama", marker = "(python_full_version >= '3.10' and sys_platform == 'win32') or (python_full_version < '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (python_full_version < '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (python_full_version < '3.10' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "colorama", marker = "(python_full_version >= '3.10' and sys_platform == 'win32') or (python_full_version < '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (python_full_version < '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (python_full_version < '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (python_full_version < '3.10' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (python_full_version < '3.10' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (python_full_version < '3.10' and extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } wheels = [ @@ -502,7 +517,7 @@ name = "exceptiongroup" version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } wheels = [ @@ -515,9 +530,9 @@ version = "3.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "blinker" }, - { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "importlib-metadata", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "itsdangerous" }, { name = "jinja2" }, { name = "markupsafe" }, @@ -549,7 +564,7 @@ dependencies = [ { name = "packaging" }, { name = "pathspec" }, { name = "pluggy" }, - { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "trove-classifiers" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8f/8a/cc1debe3514da292094f1c3a700e4ca25442489731ef7c0814358816bb03/hatchling-1.27.0.tar.gz", hash = "sha256:971c296d9819abb3811112fc52c7a9751c8d381898f36533bb16f9791e941fd6", size = 54983, upload-time = "2024-12-15T17:08:11.894Z" } @@ -580,7 +595,7 @@ name = "importlib-metadata" version = "8.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "zipp" }, ] sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } wheels = [ @@ -658,7 +673,7 @@ name = "markdown" version = "3.8.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "importlib-metadata", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/db/7c/0738e5ff0adccd0b4e02c66d0446c03a3c557e02bb49b7c263d7ab56c57d/markdown-3.8.1.tar.gz", hash = "sha256:a2e2f01cead4828ee74ecca9623045f62216aef2212a7685d6eb9163f590b8c1", size = 361280, upload-time = "2025-06-18T14:50:49.618Z" } wheels = [ @@ -758,7 +773,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, { name = "pathspec" }, - { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/81/69/92c7fa98112e4d9eb075a239caa4ef4649ad7d441545ccffbd5e34607cbb/mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab", size = 3324747, upload-time = "2025-06-16T16:51:35.145Z" } @@ -932,7 +947,7 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "wrapt", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "wrapt", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/27/9a/4e949d0a281c5dd45c8d5b02b03fe32044936234675e967de49317a1daee/pip_system_certs-4.0.tar.gz", hash = "sha256:db8e6a31388d9795ec9139957df1a89fa5274fb66164456fd091a5d3e94c350c", size = 5622, upload-time = "2022-11-04T11:01:12.537Z" } wheels = [ @@ -949,7 +964,7 @@ resolution-markers = [ "python_full_version == '3.10.*'", ] dependencies = [ - { name = "pip", marker = "python_full_version >= '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "pip", marker = "python_full_version >= '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3d/0c/a338ae5d49192861cf54da4d5c2af0efe47edbaa0827995b284005366ca5/pip_system_certs-5.2.tar.gz", hash = "sha256:80b776b5cf17191bf99d313699b7fce2fdb84eb7bbb225fd134109a82706406f", size = 5408, upload-time = "2025-06-17T23:33:15.322Z" } wheels = [ @@ -1030,14 +1045,14 @@ version = "3.3.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "astroid" }, - { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "dill" }, { name = "isort" }, { name = "mccabe" }, { name = "platformdirs" }, - { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "tomlkit" }, - { name = "typing-extensions", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "typing-extensions", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1c/e4/83e487d3ddd64ab27749b66137b26dc0c5b5c161be680e6beffdc99070b3/pylint-3.3.7.tar.gz", hash = "sha256:2b11de8bde49f9c5059452e0c310c079c746a0a8eeaa789e5aa966ecc23e4559", size = 1520709, upload-time = "2025-05-04T17:07:51.089Z" } wheels = [ @@ -1055,8 +1070,8 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "pyqt6-qt6", version = "6.6.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, - { name = "pyqt6-sip", version = "13.6.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, + { name = "pyqt6-qt6", version = "6.6.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pyqt6-sip", version = "13.6.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8c/2b/6fe0409501798abc780a70cab48c39599742ab5a8168e682107eaab78fca/PyQt6-6.6.1.tar.gz", hash = "sha256:9f158aa29d205142c56f0f35d07784b8df0be28378d20a97bcda8bd64ffd0379", size = 1043203, upload-time = "2023-12-04T10:37:27.406Z" } wheels = [ @@ -1076,8 +1091,8 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "pyqt6-qt6", version = "6.7.3", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra != 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, - { name = "pyqt6-sip", version = "13.10.2", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra != 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, + { name = "pyqt6-qt6", version = "6.7.3", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt67' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt' and extra != 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pyqt6-sip", version = "13.10.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt67' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt' and extra != 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d1/f9/b0c2ba758b14a7219e076138ea1e738c068bf388e64eee68f3df4fc96f5a/PyQt6-6.7.1.tar.gz", hash = "sha256:3672a82ccd3a62e99ab200a13903421e2928e399fda25ced98d140313ad59cb9", size = 1051212, upload-time = "2024-07-19T08:49:58.247Z" } wheels = [ @@ -1100,8 +1115,8 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "pyqt6-qt6", version = "6.8.1", source = { registry = "https://pypi.org/simple" } }, - { name = "pyqt6-sip", version = "13.10.2", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-qt6", version = "6.8.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt' or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pyqt6-sip", version = "13.10.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt' or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e9/0a/accbebed526158ab2aedd5c84d238159754bd99f481082b3fe7f374c6a3b/PyQt6-6.8.0.tar.gz", hash = "sha256:6d8628de4c2a050f0b74462e4c9cb97f839bf6ffabbca91711722ffb281570d9", size = 1061357, upload-time = "2024-12-12T15:30:42.021Z" } wheels = [ @@ -1113,6 +1128,29 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/23/a0/f564279285ed92b4fe38ed7b2a8fcddab414512391088b6a0f67a1107f24/PyQt6-6.8.0-cp39-abi3-win_arm64.whl", hash = "sha256:48bace7b87676bba5e6114482f3a20ca20be90c7f261b5d340464313f5f2bf5e", size = 5409032, upload-time = "2024-12-12T15:30:38.859Z" }, ] +[[package]] +name = "pyqt6" +version = "6.9.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +dependencies = [ + { name = "pyqt6-qt6", version = "6.9.1", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-sip", version = "13.10.2", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/32/1b/567f46eb43ca961efd38d7a0b73efb70d7342854f075fd919179fdb2a571/pyqt6-6.9.1.tar.gz", hash = "sha256:50642be03fb40f1c2111a09a1f5a0f79813e039c15e78267e6faaf8a96c1c3a6", size = 1067230, upload-time = "2025-06-06T08:49:30.307Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/c4/fc2a69cf3df09b213185ef5a677c3940cd20e7855d29e40061a685b9c6ee/pyqt6-6.9.1-cp39-abi3-macosx_10_14_universal2.whl", hash = "sha256:33c23d28f6608747ecc8bfd04c8795f61631af9db4fb1e6c2a7523ec4cc916d9", size = 59770566, upload-time = "2025-06-06T08:48:20.331Z" }, + { url = "https://files.pythonhosted.org/packages/d5/78/92f3c46440a83ebe22ae614bd6792e7b052bcb58ff128f677f5662015184/pyqt6-6.9.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:37884df27f774e2e1c0c96fa41e817a222329b80ffc6241725b0dc8c110acb35", size = 37804959, upload-time = "2025-06-06T08:48:39.587Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5e/e77fa2761d809cd08d724f44af01a4b6ceb0ff9648e43173187b0e4fac4e/pyqt6-6.9.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:055870b703c1a49ca621f8a89e2ec4d848e6c739d39367eb9687af3b056d9aa3", size = 40414608, upload-time = "2025-06-06T08:49:00.26Z" }, + { url = "https://files.pythonhosted.org/packages/c4/09/69cf80456b6a985e06dd24ed0c2d3451e43567bf2807a5f3a86ef7a74a2e/pyqt6-6.9.1-cp39-abi3-win_amd64.whl", hash = "sha256:15b95bd273bb6288b070ed7a9503d5ff377aa4882dd6d175f07cad28cdb21da0", size = 25717996, upload-time = "2025-06-06T08:49:13.208Z" }, + { url = "https://files.pythonhosted.org/packages/52/b3/0839d8fd18b86362a4de384740f2f6b6885b5d06fda7720f8a335425e316/pyqt6-6.9.1-cp39-abi3-win_arm64.whl", hash = "sha256:08792c72d130a02e3248a120f0b9bbb4bf4319095f92865bc5b365b00518f53d", size = 25212132, upload-time = "2025-06-06T08:49:27.41Z" }, +] + [[package]] name = "pyqt6-qt6" version = "6.6.2" @@ -1170,6 +1208,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c9/0d/b86b64f0ac5e08b1bd18edac9e949897a4626196725e9a86af4e9f628c80/PyQt6_Qt6-6.8.1-py3-none-win_arm64.whl", hash = "sha256:a8bc2ed4ee5e7c6ff4dd1c7db0b27705d151fee5dc232bbd1bf17618f937f515", size = 47836492, upload-time = "2024-12-06T13:52:29.511Z" }, ] +[[package]] +name = "pyqt6-qt6" +version = "6.9.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/40/04f652e714f85ba6b0c24f4ead860f2c5769f9e64737f415524d792d5914/pyqt6_qt6-6.9.1-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:3854c7f83ee4e8c2d91e23ab88b77f90e2ca7ace34fe72f634a446959f2b4d4a", size = 66236777, upload-time = "2025-06-03T14:53:17.684Z" }, + { url = "https://files.pythonhosted.org/packages/57/31/e4fa40568a59953ce5cf9a5adfbd1be4a806dafd94e39072d3cc0bed5468/pyqt6_qt6-6.9.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:123e4aeb037c099bb4696a3ea8edcb1d9d62cedd0b2b950556b26024c97f3293", size = 60551574, upload-time = "2025-06-03T14:53:48.42Z" }, + { url = "https://files.pythonhosted.org/packages/aa/8d/7c8073cbbefe9c103ec8add70f29ffee1db95a3755b429b9f47cd6afa41b/pyqt6_qt6-6.9.1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:cc5bd193ebd2d1a3ec66e1eee65bf532d762c239459bce1ecebf56177243e89b", size = 82000130, upload-time = "2025-06-03T14:54:26.585Z" }, + { url = "https://files.pythonhosted.org/packages/1e/60/a4ab932028b0c15c0501cb52eb1e7f24f4ce2e4c78d46c7cce58a375a88c/pyqt6_qt6-6.9.1-py3-none-manylinux_2_39_aarch64.whl", hash = "sha256:b065af7243d1d450a49470a8185301196a18b1d41085d3ef476eb55bbb225083", size = 80463127, upload-time = "2025-06-03T14:55:03.272Z" }, + { url = "https://files.pythonhosted.org/packages/e7/85/552710819019a96d39d924071324a474aec54b31c410d7de8ebb398adcc1/pyqt6_qt6-6.9.1-py3-none-win_amd64.whl", hash = "sha256:f9e54c424bc921ecb76792a75d123e4ecfc26b00b0c57dae526f41f1d57951d3", size = 73778423, upload-time = "2025-06-03T14:55:39.756Z" }, + { url = "https://files.pythonhosted.org/packages/16/b4/70f6b18a4913f2326dcf7acb15c12cc0b91cb3932c2ba3b5728811f22acd/pyqt6_qt6-6.9.1-py3-none-win_arm64.whl", hash = "sha256:432caaedf5570bc8a9b7c75bc6af6a26bf88589536472eca73417ac019f59d41", size = 49617924, upload-time = "2025-06-03T14:57:13.038Z" }, +] + [[package]] name = "pyqt6-sip" version = "13.6.0" @@ -1248,9 +1305,9 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "pyqt6", version = "6.6.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, - { name = "pyqt6-sip", version = "13.6.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, - { name = "pyqt6-webengine-qt6", version = "6.6.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, + { name = "pyqt6", version = "6.6.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pyqt6-sip", version = "13.6.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pyqt6-webengine-qt6", version = "6.6.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/49/9a/69db3a2ab1ba43f762144a66f0375540e195e107a1049d7263ab48ebc9cc/PyQt6_WebEngine-6.6.0.tar.gz", hash = "sha256:d50b984c3f85e409e692b156132721522d4e8cf9b6c25e0cf927eea2dfb39487", size = 31817, upload-time = "2023-10-30T10:57:13.211Z" } wheels = [ @@ -1270,9 +1327,9 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "pyqt6", version = "6.7.1", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra != 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, - { name = "pyqt6-sip", version = "13.10.2", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra != 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, - { name = "pyqt6-webengine-qt6", version = "6.7.3", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra != 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, + { name = "pyqt6", version = "6.7.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt67' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt' and extra != 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pyqt6-sip", version = "13.10.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt67' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt' and extra != 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pyqt6-webengine-qt6", version = "6.7.3", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt67' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt' and extra != 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/87/88/230ec599944edf941f4cca8d1439e3a9c8c546715434eee05dce7ff032ed/PyQt6_WebEngine-6.7.0.tar.gz", hash = "sha256:68edc7adb6d9e275f5de956881e79cca0d71fad439abeaa10d823bff5ac55001", size = 32593, upload-time = "2024-04-26T08:37:08.355Z" } wheels = [ @@ -1293,9 +1350,9 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "pyqt6", version = "6.8.0", source = { registry = "https://pypi.org/simple" } }, - { name = "pyqt6-sip", version = "13.10.2", source = { registry = "https://pypi.org/simple" } }, - { name = "pyqt6-webengine-qt6", version = "6.8.1", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6", version = "6.8.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt' or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pyqt6-sip", version = "13.10.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt' or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pyqt6-webengine-qt6", version = "6.8.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt' or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cd/c8/cadaa950eaf97f29e48c435e274ea5a81c051e745a3e2f5d9d994b7a6cda/PyQt6_WebEngine-6.8.0.tar.gz", hash = "sha256:64045ea622b6a41882c2b18f55ae9714b8660acff06a54e910eb72822c2f3ff2", size = 34203, upload-time = "2024-12-12T15:34:35.573Z" } wheels = [ @@ -1306,6 +1363,29 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1e/74/9b20e505737ceefe2ffb47355633c84b7d5d7d592f32165425b3e0ce7dd9/PyQt6_WebEngine-6.8.0-cp39-abi3-win_amd64.whl", hash = "sha256:d7366809d681bcc096fa565f2a81d0ab040f7da5bb4f12f78e834a2b173c87d1", size = 234566, upload-time = "2024-12-12T15:34:33.59Z" }, ] +[[package]] +name = "pyqt6-webengine" +version = "6.9.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +dependencies = [ + { name = "pyqt6", version = "6.9.1", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-sip", version = "13.10.2", source = { registry = "https://pypi.org/simple" } }, + { name = "pyqt6-webengine-qt6", version = "6.9.1", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/1a/9971af004a7e859347702f816fb71ecd67c3e32b2f0ae8daf1c1ded99f62/pyqt6_webengine-6.9.0.tar.gz", hash = "sha256:6ae537e3bbda06b8e06535e4852297e0bc3b00543c47929541fcc9b11981aa25", size = 34616, upload-time = "2025-04-08T08:57:35.402Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/e1/964ee1c464a0e1f07f8be54ce9316dc76e431d1bc99c9e5c1437bf548d92/PyQt6_WebEngine-6.9.0-cp39-abi3-macosx_10_14_universal2.whl", hash = "sha256:3ea5bdd48d109f35bf726f59d85b250e430ddd50175fe79a386b7f14d3e34d2d", size = 438004, upload-time = "2025-04-08T08:57:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/cc/9d/8674bb27e2497fdad7ae5bc000831b42dbfb546aacd11ae7a8cca4493190/PyQt6_WebEngine-6.9.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c15012245036604c82abcd865e0b808e75bcfd0b477454298b7a70d9e6c4958b", size = 299003, upload-time = "2025-04-08T08:57:31.334Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9c/b6a1ce7026260d518a103c467a4795c719dd1e4d7f8dc00416d3ec292d3a/PyQt6_WebEngine-6.9.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:e4404899290f86d4652a07471262a2f41397c64ecb091229b5bbbd8b82af35ce", size = 297424, upload-time = "2025-04-08T08:57:32.664Z" }, + { url = "https://files.pythonhosted.org/packages/0e/e8/444487c86472c522d6ab28686b9f3c4d6fe2febde81b40561d42c11b5cd7/PyQt6_WebEngine-6.9.0-cp39-abi3-win_amd64.whl", hash = "sha256:541cf838facadfc38243baaecfeeaf07c8eff030cf27341c85c245d00e571489", size = 237847, upload-time = "2025-04-08T08:57:34.174Z" }, +] + [[package]] name = "pyqt6-webengine-qt6" version = "6.6.2" @@ -1334,7 +1414,7 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "pyqt6-webenginesubwheel-qt6", marker = "(extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra != 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra != 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67')" }, + { name = "pyqt6-webenginesubwheel-qt6", marker = "extra == 'extra-3-aqt-qt67' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt' and extra != 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/06/35/570d072bec7c114b5d155d990e2b8339223e230e9276bdf806a20f71e50d/PyQt6_WebEngine_Qt6-6.7.3-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:68812b2a5d0d417ce32dc4d11a304e7838e02c51013712e7533faf03448672d9", size = 26214456, upload-time = "2024-09-29T16:27:20.257Z" }, @@ -1363,6 +1443,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b0/b5/a641ebe3e5113bee23d911c58fdd2e65061a6e3786a26b068468b988e5d2/PyQt6_WebEngine_Qt6-6.8.1-py3-none-win_amd64.whl", hash = "sha256:0ced2a10433da2571cfa29ed882698e0e164184d54068d17ba73799c45af5f0f", size = 95657750, upload-time = "2024-12-06T13:47:43.048Z" }, ] +[[package]] +name = "pyqt6-webengine-qt6" +version = "6.9.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/68/8eccda2a78d100a95db2131fac82b4ad842bdf0255019dcf86d5db0db3fa/pyqt6_webengine_qt6-6.9.1-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:38a461e21df3e09829ce18cd0ecd052ecff0f9a4001ae000ea6ddac10fae6b0f", size = 120033076, upload-time = "2025-06-03T14:59:49.52Z" }, + { url = "https://files.pythonhosted.org/packages/d4/8a/a2cf83dd9cb8e944507e7b4070ac537a30d6caef7e498c87f1612507431d/pyqt6_webengine_qt6-6.9.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9351ac6cccfdbf414a2514b58d49765d3f48fb3b690ceeaa8ca804340eb460b4", size = 108530410, upload-time = "2025-06-03T15:00:40.813Z" }, + { url = "https://files.pythonhosted.org/packages/fb/82/99b189e30641469dda6eb09a5f3cf0c5e12e2b39967e1df5d156f8499c4f/pyqt6_webengine_qt6-6.9.1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:37f9d33e3f68681687a7d28b80058c6473813c6c2f9706a44dc7bc07486b9e9a", size = 110750602, upload-time = "2025-06-03T15:01:32.055Z" }, + { url = "https://files.pythonhosted.org/packages/68/fc/958c7f5e0f9cfe7903a90ed41b435b17de6ca7cf5d1f73e97ad07fe8107c/pyqt6_webengine_qt6-6.9.1-py3-none-manylinux_2_39_aarch64.whl", hash = "sha256:e8440e65b79df167b49bd3c26b8bf7179306df010e8c319a7ad6b8bc2a8ae8d4", size = 106652228, upload-time = "2025-06-03T15:02:18.985Z" }, + { url = "https://files.pythonhosted.org/packages/4c/82/4a3e2233ca3aa9c3ec77390e570fbcffcc511bc8513461daa1d38cda652a/pyqt6_webengine_qt6-6.9.1-py3-none-win_amd64.whl", hash = "sha256:c7f460226c054a52b7868d3befeed4fe2af03f4f80d2e53ab49fcb238bac2bc7", size = 110244976, upload-time = "2025-06-03T15:03:09.743Z" }, +] + [[package]] name = "pyqt6-webenginesubwheel-qt6" version = "6.7.3" @@ -1389,13 +1487,13 @@ name = "pytest" version = "8.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, { name = "pygments" }, - { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } wheels = [ @@ -1483,7 +1581,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "rpds-py" }, - { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" } wheels = [ @@ -1678,24 +1776,24 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "alabaster", version = "0.7.16", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "babel", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "colorama", marker = "(python_full_version < '3.10' and sys_platform == 'win32') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "docutils", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "imagesize", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "importlib-metadata", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "jinja2", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "packaging", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "pygments", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "requests", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "snowballstemmer", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "tomli", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "alabaster", version = "0.7.16", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "babel", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "colorama", marker = "(python_full_version < '3.10' and sys_platform == 'win32') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (python_full_version >= '3.10' and extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "docutils", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "imagesize", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "jinja2", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "packaging", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pygments", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "requests", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "snowballstemmer", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "tomli", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5b/be/50e50cb4f2eff47df05673d361095cafd95521d2a22521b920c67a372dcb/sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe", size = 8067911, upload-time = "2024-07-20T14:46:56.059Z" } wheels = [ @@ -1710,23 +1808,23 @@ resolution-markers = [ "python_full_version == '3.10.*'", ] dependencies = [ - { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "babel", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "colorama", marker = "(python_full_version == '3.10.*' and sys_platform == 'win32') or (python_full_version != '3.10.*' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (python_full_version != '3.10.*' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (python_full_version != '3.10.*' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "docutils", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "imagesize", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "jinja2", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "packaging", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "pygments", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "requests", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "snowballstemmer", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "tomli", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "babel", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "colorama", marker = "(python_full_version == '3.10.*' and sys_platform == 'win32') or (python_full_version != '3.10.*' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (python_full_version != '3.10.*' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (python_full_version != '3.10.*' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (python_full_version != '3.10.*' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (python_full_version != '3.10.*' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (python_full_version != '3.10.*' and extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "docutils", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "imagesize", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "jinja2", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "packaging", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pygments", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "requests", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "snowballstemmer", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "tomli", marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" } wheels = [ @@ -1742,23 +1840,23 @@ resolution-markers = [ "python_full_version == '3.11.*'", ] dependencies = [ - { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "babel", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "colorama", marker = "(python_full_version >= '3.11' and sys_platform == 'win32') or (python_full_version < '3.11' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (python_full_version < '3.11' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (python_full_version < '3.11' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "docutils", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "imagesize", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "jinja2", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "packaging", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "pygments", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "requests", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "roman-numerals-py", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "snowballstemmer", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "babel", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "colorama", marker = "(python_full_version >= '3.11' and sys_platform == 'win32') or (python_full_version < '3.11' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (python_full_version < '3.11' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (python_full_version < '3.11' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (python_full_version < '3.11' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (python_full_version < '3.11' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (python_full_version < '3.11' and extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (sys_platform != 'win32' and extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "docutils", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "imagesize", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "jinja2", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "packaging", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "pygments", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "requests", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "roman-numerals-py", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "snowballstemmer", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876, upload-time = "2025-03-02T22:31:59.658Z" } wheels = [ @@ -1773,10 +1871,10 @@ dependencies = [ { name = "astroid" }, { name = "jinja2" }, { name = "pyyaml" }, - { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "stdlib-list", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "stdlib-list", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7f/a8/22b379a2a75ccb881217d3d4ae56d7d35f2d1bb4c8c0c51d0253676746a1/sphinx_autoapi-3.6.0.tar.gz", hash = "sha256:c685f274e41d0842ae7e199460c322c4bd7fec816ccc2da8d806094b4f64af06", size = 55417, upload-time = "2025-02-18T01:50:55.241Z" } wheels = [ @@ -1789,9 +1887,9 @@ version = "3.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docutils" }, - { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "sphinxcontrib-jquery" }, ] sdist = { url = "https://files.pythonhosted.org/packages/91/44/c97faec644d29a5ceddd3020ae2edffa69e7d00054a8c7a6021e82f20335/sphinx_rtd_theme-3.0.2.tar.gz", hash = "sha256:b7457bc25dda723b20b086a670b9953c859eab60a2a03ee8eb2bb23e176e5f85", size = 7620463, upload-time = "2024-11-13T11:06:04.545Z" } @@ -1831,9 +1929,9 @@ name = "sphinxcontrib-jquery" version = "4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" }, + { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, + { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", size = 122331, upload-time = "2023-03-14T15:01:01.944Z" } wheels = [ From a4c95f5fbdb22f165c41d31a16c365b3edbeff97 Mon Sep 17 00:00:00 2001 From: Jarrett Ye Date: Sat, 21 Jun 2025 01:59:35 +0800 Subject: [PATCH 018/101] include decay in ComputeMemoryStateResponse (#4102) * include decay in ComputeMemoryStateResponse * Add decay attribute to ComputedMemoryState and update Collection methods * Refactor decay calculation into a helper function for improved readability and maintainability in memory state management * format & clippy --- proto/anki/scheduler.proto | 1 + pylib/anki/collection.py | 7 +++++- rslib/src/scheduler/fsrs/memory_state.rs | 28 +++++++++++++++--------- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/proto/anki/scheduler.proto b/proto/anki/scheduler.proto index ea483d3db..1b7d44a83 100644 --- a/proto/anki/scheduler.proto +++ b/proto/anki/scheduler.proto @@ -450,6 +450,7 @@ message EvaluateParamsResponse { message ComputeMemoryStateResponse { optional cards.FsrsMemoryState state = 1; float desired_retention = 2; + float decay = 3; } message FuzzDeltaRequest { diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index 17ee08e2f..6cf38174c 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -122,6 +122,7 @@ class ComputedMemoryState: desired_retention: float stability: float | None = None difficulty: float | None = None + decay: float | None = None @dataclass @@ -1189,9 +1190,13 @@ class Collection(DeprecatedNamesMixin): desired_retention=resp.desired_retention, stability=resp.state.stability, difficulty=resp.state.difficulty, + decay=resp.decay, ) else: - return ComputedMemoryState(desired_retention=resp.desired_retention) + return ComputedMemoryState( + desired_retention=resp.desired_retention, + decay=resp.decay, + ) def fuzz_delta(self, card_id: CardId, interval: int) -> int: "The delta days of fuzz applied if reviewing the card in v3." diff --git a/rslib/src/scheduler/fsrs/memory_state.rs b/rslib/src/scheduler/fsrs/memory_state.rs index 787fa212d..f5af97674 100644 --- a/rslib/src/scheduler/fsrs/memory_state.rs +++ b/rslib/src/scheduler/fsrs/memory_state.rs @@ -30,6 +30,18 @@ pub struct ComputeMemoryProgress { pub total_cards: u32, } +/// Helper function to determine the appropriate decay value based on FSRS +/// parameters +fn get_decay_from_params(params: &[f32]) -> f32 { + if params.is_empty() { + FSRS6_DEFAULT_DECAY // default decay for FSRS-6 + } else if params.len() < 21 { + FSRS5_DEFAULT_DECAY // default decay for FSRS-4.5 and FSRS-5 + } else { + params[20] + } +} + #[derive(Debug)] pub(crate) struct UpdateMemoryStateRequest { pub params: Params, @@ -77,15 +89,7 @@ impl Collection { .then(|| Rescheduler::new(self)) .transpose()?; let fsrs = FSRS::new(req.as_ref().map(|w| &w.params[..]).or(Some([].as_slice())))?; - let decay = req.as_ref().map(|w| { - if w.params.is_empty() { - FSRS6_DEFAULT_DECAY // default decay for FSRS-6 - } else if w.params.len() < 21 { - FSRS5_DEFAULT_DECAY // default decay for FSRS-4.5 and FSRS-5 - } else { - w.params[20] - } - }); + let decay = req.as_ref().map(|w| get_decay_from_params(&w.params)); let historical_retention = req.as_ref().map(|w| w.historical_retention); let items = fsrs_items_for_memory_states( &fsrs, @@ -190,7 +194,9 @@ impl Collection { .or_not_found(conf_id)?; let desired_retention = config.inner.desired_retention; let historical_retention = config.inner.historical_retention; - let fsrs = FSRS::new(Some(config.fsrs_params()))?; + let params = config.fsrs_params(); + let decay = get_decay_from_params(params); + let fsrs = FSRS::new(Some(params))?; let revlog = self.revlog_for_srs(SearchNode::CardIds(card.id.to_string()))?; let item = fsrs_item_for_memory_state( &fsrs, @@ -204,6 +210,7 @@ impl Collection { Ok(ComputeMemoryStateResponse { state: card.memory_state.map(Into::into), desired_retention, + decay, }) } else { card.memory_state = None; @@ -211,6 +218,7 @@ impl Collection { Ok(ComputeMemoryStateResponse { state: None, desired_retention, + decay, }) } } From cc395f7c448ee0ba268077a1e21fb8e0b80e99f4 Mon Sep 17 00:00:00 2001 From: llama Date: Sat, 21 Jun 2025 20:15:19 +0800 Subject: [PATCH 019/101] Upgrade to nom 8.0.0 (#4105) * bump nom to 8.0.0 * update cloze.rs * update template.rs * update imageocclusion.rs * update search/parser.rs * update card_rendering/parser.rs * replace use of fold_many0 with many0 in nom 8, `many0` doesn't accumulate when used within `recognize` --- Cargo.lock | 13 ++++- Cargo.toml | 2 +- cargo/licenses.json | 9 +++ rslib/src/card_rendering/parser.rs | 61 ++++++++++++--------- rslib/src/cloze.rs | 10 ++-- rslib/src/image_occlusion/imageocclusion.rs | 6 +- rslib/src/search/parser.rs | 32 ++++++----- rslib/src/template.rs | 7 ++- 8 files changed, 90 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66173027b..03f9e63c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,7 +117,7 @@ dependencies = [ "id_tree", "inflections", "itertools 0.14.0", - "nom", + "nom 8.0.0", "num_cpus", "num_enum", "once_cell", @@ -4117,6 +4117,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + [[package]] name = "normpath" version = "1.3.0" @@ -6258,7 +6267,7 @@ dependencies = [ "bytesize", "lazy_static", "libc", - "nom", + "nom 7.1.3", "time", "winapi", ] diff --git a/Cargo.toml b/Cargo.toml index 980956b05..61cca8649 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,7 +93,7 @@ junction = "1.2.0" libc = "0.2" libc-stdhandle = "0.1" maplit = "1.0.2" -nom = "7.1.3" +nom = "8.0.0" num-format = "0.4.4" num_cpus = "1.17.0" num_enum = "0.7.3" diff --git a/cargo/licenses.json b/cargo/licenses.json index c16b20aa7..f2695ac76 100644 --- a/cargo/licenses.json +++ b/cargo/licenses.json @@ -2645,6 +2645,15 @@ "license_file": null, "description": "A byte-oriented, zero-copy, parser combinators library" }, + { + "name": "nom", + "version": "8.0.0", + "authors": "contact@geoffroycouprie.com", + "repository": "https://github.com/rust-bakery/nom", + "license": "MIT", + "license_file": null, + "description": "A byte-oriented, zero-copy, parser combinators library" + }, { "name": "ntapi", "version": "0.4.1", diff --git a/rslib/src/card_rendering/parser.rs b/rslib/src/card_rendering/parser.rs index 6f1cc662e..b124c069d 100644 --- a/rslib/src/card_rendering/parser.rs +++ b/rslib/src/card_rendering/parser.rs @@ -14,14 +14,14 @@ use nom::combinator::recognize; use nom::combinator::rest; use nom::combinator::success; use nom::combinator::value; -use nom::multi::fold_many0; use nom::multi::many0; use nom::sequence::delimited; use nom::sequence::pair; use nom::sequence::preceded; use nom::sequence::separated_pair; use nom::sequence::terminated; -use nom::sequence::tuple; +use nom::Input; +use nom::Parser; use super::CardNodes; use super::Directive; @@ -86,9 +86,12 @@ impl<'a> Directive<'a> { } /// Consume 0 or more of anything in " \t\r\n" after `parser`. -fn trailing_whitespace0<'parser, 's, P, O>(parser: P) -> impl FnMut(&'s str) -> IResult<'s, O> +fn trailing_whitespace0(parser: P) -> impl Parser where - P: FnMut(&'s str) -> IResult<'s, O> + 'parser, + I: Input, + ::Item: nom::AsChar, + E: nom::error::ParseError, + P: Parser, { terminated(parser, multispace0) } @@ -97,11 +100,11 @@ where fn is_not0<'parser, 'arr: 'parser, 's: 'parser>( arr: &'arr str, ) -> impl FnMut(&'s str) -> IResult<'s, &'s str> + 'parser { - alt((is_not(arr), success(""))) + move |s| alt((is_not(arr), success(""))).parse(s) } fn node(s: &str) -> IResult { - alt((sound_node, tag_node, text_node))(s) + alt((sound_node, tag_node, text_node)).parse(s) } /// A sound tag `[sound:resource]`, where `resource` is pointing to a sound or @@ -110,11 +113,11 @@ fn sound_node(s: &str) -> IResult { map( delimited(tag("[sound:"), is_not("]"), tag("]")), Node::SoundOrVideo, - )(s) + ) + .parse(s) } fn take_till_potential_tag_start(s: &str) -> IResult<&str> { - use nom::InputTake; // first char could be '[', but wasn't part of a node, so skip (eof ends parse) let (after, offset) = anychar(s).map(|(s, c)| (s, c.len_utf8()))?; Ok(match after.find('[') { @@ -127,7 +130,7 @@ fn take_till_potential_tag_start(s: &str) -> IResult<&str> { fn tag_node(s: &str) -> IResult { /// Match the start of an opening tag and return its name. fn name(s: &str) -> IResult<&str> { - preceded(tag("[anki:"), is_not("] \t\r\n"))(s) + preceded(tag("[anki:"), is_not("] \t\r\n")).parse(s) } /// Return a parser to match an opening `name` tag and return its options. @@ -138,31 +141,35 @@ fn tag_node(s: &str) -> IResult { /// empty. fn options(s: &str) -> IResult> { fn key(s: &str) -> IResult<&str> { - is_not("] \t\r\n=")(s) + is_not("] \t\r\n=").parse(s) } fn val(s: &str) -> IResult<&str> { alt(( delimited(tag("\""), is_not0("\""), tag("\"")), is_not0("] \t\r\n\""), - ))(s) + )) + .parse(s) } - many0(trailing_whitespace0(separated_pair(key, tag("="), val)))(s) + many0(trailing_whitespace0(separated_pair(key, tag("="), val))).parse(s) } - delimited( - pair(tag("[anki:"), trailing_whitespace0(tag(name))), - options, - tag("]"), - ) + move |s| { + delimited( + pair(tag("[anki:"), trailing_whitespace0(tag(name))), + options, + tag("]"), + ) + .parse(s) + } } /// Return a parser to match a closing `name` tag. fn closing_parser<'parser, 'name: 'parser, 's: 'parser>( name: &'name str, ) -> impl FnMut(&'s str) -> IResult<'s, ()> + 'parser { - value((), tuple((tag("[/anki:"), tag(name), tag("]")))) + move |s| value((), (tag("[/anki:"), tag(name), tag("]"))).parse(s) } /// Return a parser to match and return anything until a closing `name` tag @@ -170,12 +177,13 @@ fn tag_node(s: &str) -> IResult { fn content_parser<'parser, 'name: 'parser, 's: 'parser>( name: &'name str, ) -> impl FnMut(&'s str) -> IResult<'s, &'s str> + 'parser { - recognize(fold_many0( - pair(not(closing_parser(name)), take_till_potential_tag_start), - // we don't need to accumulate anything - || (), - |_, _| (), - )) + move |s| { + recognize(many0(pair( + not(closing_parser(name)), + take_till_potential_tag_start, + ))) + .parse(s) + } } let (_, tag_name) = name(s)?; @@ -185,11 +193,12 @@ fn tag_node(s: &str) -> IResult { closing_parser(tag_name), ), |(options, content)| Node::Directive(Directive::new(tag_name, options, content)), - )(s) + ) + .parse(s) } fn text_node(s: &str) -> IResult { - map(take_till_potential_tag_start, Node::Text)(s) + map(take_till_potential_tag_start, Node::Text).parse(s) } #[cfg(test)] diff --git a/rslib/src/cloze.rs b/rslib/src/cloze.rs index f57d07ab0..208a2f4ed 100644 --- a/rslib/src/cloze.rs +++ b/rslib/src/cloze.rs @@ -15,6 +15,7 @@ use nom::bytes::complete::tag; use nom::bytes::complete::take_while; use nom::combinator::map; use nom::IResult; +use nom::Parser; use regex::Captures; use regex::Regex; @@ -72,7 +73,7 @@ fn tokenize(mut text: &str) -> impl Iterator { } fn close_cloze(text: &str) -> IResult<&str, Token> { - map(tag("}}"), |_| Token::CloseCloze)(text) + map(tag("}}"), |_| Token::CloseCloze).parse(text) } /// Match a run of text until an open/close marker is encountered. @@ -87,7 +88,7 @@ fn tokenize(mut text: &str) -> impl Iterator { // start with the no-match case let mut index = text.len(); for (idx, _) in text.char_indices() { - if other_token(&text[idx..]).is_ok() { + if other_token.parse(&text[idx..]).is_ok() { index = idx; break; } @@ -99,8 +100,9 @@ fn tokenize(mut text: &str) -> impl Iterator { if text.is_empty() { None } else { - let (remaining_text, token) = - alt((open_cloze, close_cloze, normal_text))(text).unwrap(); + let (remaining_text, token) = alt((open_cloze, close_cloze, normal_text)) + .parse(text) + .unwrap(); text = remaining_text; Some(token) } diff --git a/rslib/src/image_occlusion/imageocclusion.rs b/rslib/src/image_occlusion/imageocclusion.rs index 2ba83374f..e2eea9a39 100644 --- a/rslib/src/image_occlusion/imageocclusion.rs +++ b/rslib/src/image_occlusion/imageocclusion.rs @@ -13,6 +13,7 @@ use nom::character::complete::char; use nom::error::ErrorKind; use nom::sequence::preceded; use nom::sequence::separated_pair; +use nom::Parser; fn unescape(text: &str) -> String { text.replace("\\:", ":") @@ -22,11 +23,12 @@ pub fn parse_image_cloze(text: &str) -> Option { if let Some((shape, _)) = text.split_once(':') { let mut properties = vec![]; let mut remaining = &text[shape.len()..]; - while let Ok((rem, (name, value))) = separated_pair::<_, _, _, _, (_, ErrorKind), _, _, _>( + while let Ok((rem, (name, value))) = separated_pair::<_, _, _, (_, ErrorKind), _, _, _>( preceded(tag(":"), is_not("=")), tag("="), escaped(is_not("\\:"), '\\', char(':')), - )(remaining) + ) + .parse(remaining) { remaining = rem; let value = unescape(value); diff --git a/rslib/src/search/parser.rs b/rslib/src/search/parser.rs index 93df4ea08..041ec4948 100644 --- a/rslib/src/search/parser.rs +++ b/rslib/src/search/parser.rs @@ -19,6 +19,7 @@ use nom::error::ErrorKind as NomErrorKind; use nom::multi::many0; use nom::sequence::preceded; use nom::sequence::separated_pair; +use nom::Parser; use regex::Captures; use regex::Regex; @@ -202,18 +203,19 @@ fn group_inner(input: &str) -> IResult> { } fn whitespace0(s: &str) -> IResult> { - many0(one_of(" \u{3000}"))(s) + many0(one_of(" \u{3000}")).parse(s) } /// Optional leading space, then a (negated) group or text fn node(s: &str) -> IResult { - preceded(whitespace0, alt((negated_node, group, text)))(s) + preceded(whitespace0, alt((negated_node, group, text))).parse(s) } fn negated_node(s: &str) -> IResult { map(preceded(char('-'), alt((group, text))), |node| { Node::Not(Box::new(node)) - })(s) + }) + .parse(s) } /// One or more nodes surrounded by brackets, eg (one OR two) @@ -233,7 +235,7 @@ fn group(s: &str) -> IResult { /// Either quoted or unquoted text fn text(s: &str) -> IResult { - alt((quoted_term, partially_quoted_term, unquoted_term))(s) + alt((quoted_term, partially_quoted_term, unquoted_term)).parse(s) } /// Quoted text, including the outer double quotes. @@ -248,7 +250,8 @@ fn partially_quoted_term(s: &str) -> IResult { escaped(is_not("\"(): \u{3000}\\"), '\\', none_of(" \u{3000}")), char(':'), quoted_term_str, - )(s)?; + ) + .parse(s)?; Ok(( remaining, Node::Search(search_node_for_text_with_argument(key, val)?), @@ -296,7 +299,7 @@ fn unquoted_term(s: &str) -> IResult { fn quoted_term_str(s: &str) -> IResult<&str> { let (opened, _) = char('"')(s)?; if let Ok((tail, inner)) = - escaped::<_, ParseError, _, _, _, _>(is_not(r#""\"#), '\\', anychar)(opened) + escaped::<_, ParseError, _, _>(is_not(r#""\"#), '\\', anychar).parse(opened) { if let Ok((remaining, _)) = char::<_, ParseError>('"')(tail) { Ok((remaining, inner)) @@ -321,7 +324,8 @@ fn search_node_for_text(s: &str) -> ParseResult { // leading : is only possible error for well-formed input let (tail, head) = verify(escaped(is_not(r":\"), '\\', anychar), |t: &str| { !t.is_empty() - })(s) + }) + .parse(s) .map_err(|_: nom::Err| parse_failure(s, FailKind::MissingKey))?; if tail.is_empty() { Ok(SearchNode::UnqualifiedText(unescape(head)?)) @@ -407,7 +411,7 @@ fn parse_resched(s: &str) -> ParseResult { /// eg prop:ivl>3, prop:ease!=2.5 fn parse_prop(prop_clause: &str) -> ParseResult { - let (tail, prop) = alt::<_, _, ParseError, _>(( + let (tail, prop) = alt(( tag("ivl"), tag("due"), tag("reps"), @@ -421,8 +425,9 @@ fn parse_prop(prop_clause: &str) -> ParseResult { tag("r"), recognize(preceded(tag("cdn:"), alphanumeric1)), recognize(preceded(tag("cds:"), alphanumeric1)), - ))(prop_clause) - .map_err(|_| { + )) + .parse(prop_clause) + .map_err(|_: nom::Err| { parse_failure( prop_clause, FailKind::InvalidPropProperty { @@ -431,15 +436,16 @@ fn parse_prop(prop_clause: &str) -> ParseResult { ) })?; - let (num, operator) = alt::<_, _, ParseError, _>(( + let (num, operator) = alt(( tag("<="), tag(">="), tag("!="), tag("="), tag("<"), tag(">"), - ))(tail) - .map_err(|_| { + )) + .parse(tail) + .map_err(|_: nom::Err| { parse_failure( prop_clause, FailKind::InvalidPropOperator { diff --git a/rslib/src/template.rs b/rslib/src/template.rs index d09ade580..e3a900a2b 100644 --- a/rslib/src/template.rs +++ b/rslib/src/template.rs @@ -13,6 +13,7 @@ use nom::bytes::complete::tag; use nom::bytes::complete::take_until; use nom::combinator::map; use nom::sequence::delimited; +use nom::Parser; use regex::Regex; use crate::cloze::cloze_number_in_fields; @@ -67,7 +68,8 @@ impl TemplateMode { tag(self.end_tag()), ), |out| classify_handle(out), - )(s) + ) + .parse(s) } /// Return the next handlebar, comment or text token. @@ -127,7 +129,8 @@ fn comment_token(s: &str) -> nom::IResult<&str, Token> { tag(COMMENT_END), ), Token::Comment, - )(s) + ) + .parse(s) } fn tokens(mut template: &str) -> impl Iterator>> { From 88538d8badeb11e76d534077caa05f93d5d07267 Mon Sep 17 00:00:00 2001 From: Jarrett Ye Date: Sat, 21 Jun 2025 20:15:30 +0800 Subject: [PATCH 020/101] Fix/set due date on intraday learning card (#4101) - Introduced `next_day_start` parameter to `set_due_date` for improved due date handling. - Updated logic to account for Unix epoch timestamps when calculating due dates. --- rslib/src/scheduler/reviews.rs | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/rslib/src/scheduler/reviews.rs b/rslib/src/scheduler/reviews.rs index 7e61447da..06390e57d 100644 --- a/rslib/src/scheduler/reviews.rs +++ b/rslib/src/scheduler/reviews.rs @@ -17,6 +17,7 @@ use crate::collection::Collection; use crate::config::StringKey; use crate::error::Result; use crate::prelude::*; +use crate::scheduler::timing::is_unix_epoch_timestamp; impl Card { /// Make card due in `days_from_today`. @@ -27,6 +28,7 @@ impl Card { fn set_due_date( &mut self, today: u32, + next_day_start: i64, days_from_today: u32, ease_factor: f32, force_reset: bool, @@ -34,8 +36,15 @@ impl Card { let new_due = (today + days_from_today) as i32; let fsrs_enabled = self.memory_state.is_some(); let new_interval = if fsrs_enabled { - self.interval - .saturating_add_signed(new_due - self.original_or_current_due()) + let due = self.original_or_current_due(); + let due_diff = if is_unix_epoch_timestamp(due) { + let offset = (due as i64 - next_day_start) / 86_400; + let due = (today as i64 + offset) as i32; + new_due - due + } else { + new_due - due + }; + self.interval.saturating_add_signed(due_diff) } else if force_reset || !matches!(self.ctype, CardType::Review | CardType::Relearn) { days_from_today.max(1) } else { @@ -114,6 +123,7 @@ impl Collection { let spec = parse_due_date_str(days)?; let usn = self.usn()?; let today = self.timing_today()?.days_elapsed; + let next_day_start = self.timing_today()?.next_day_at.0; let mut rng = rand::rng(); let distribution = Uniform::new_inclusive(spec.min, spec.max).unwrap(); let mut decks_initial_ease: HashMap = HashMap::new(); @@ -137,7 +147,13 @@ impl Collection { }; let original = card.clone(); let days_from_today = distribution.sample(&mut rng); - card.set_due_date(today, days_from_today, ease_factor, spec.force_reset); + card.set_due_date( + today, + next_day_start, + days_from_today, + ease_factor, + spec.force_reset, + ); col.log_manually_scheduled_review(&card, original.interval, usn)?; col.update_card_inner(&mut card, original, usn)?; } @@ -228,26 +244,26 @@ mod test { let mut c = Card::new(NoteId(0), 0, DeckId(0), 0); // setting the due date of a new card will convert it - c.set_due_date(5, 2, 1.8, false); + c.set_due_date(5, 0, 2, 1.8, false); assert_eq!(c.ctype, CardType::Review); assert_eq!(c.due, 7); assert_eq!(c.interval, 2); assert_eq!(c.ease_factor, 1800); // reschedule it again the next day, shifting it from day 7 to day 9 - c.set_due_date(6, 3, 2.5, false); + c.set_due_date(6, 0, 3, 2.5, false); assert_eq!(c.due, 9); assert_eq!(c.interval, 2); assert_eq!(c.ease_factor, 1800); // interval doesn't change // we can bring cards forward too - return it to its original due date - c.set_due_date(6, 1, 2.4, false); + c.set_due_date(6, 0, 1, 2.4, false); assert_eq!(c.due, 7); assert_eq!(c.interval, 2); assert_eq!(c.ease_factor, 1800); // interval doesn't change // we can force the interval to be reset instead of shifted - c.set_due_date(6, 3, 2.3, true); + c.set_due_date(6, 0, 3, 2.3, true); assert_eq!(c.due, 9); assert_eq!(c.interval, 3); assert_eq!(c.ease_factor, 1800); // interval doesn't change @@ -259,7 +275,7 @@ mod test { c.original_deck_id = DeckId(1); c.due = -10000; c.queue = CardQueue::New; - c.set_due_date(6, 1, 2.2, false); + c.set_due_date(6, 0, 1, 2.2, false); assert_eq!(c.due, 7); assert_eq!(c.interval, 2); assert_eq!(c.ease_factor, 2200); @@ -271,7 +287,7 @@ mod test { c.ctype = CardType::Relearn; c.original_due = c.due; c.due = 12345678; - c.set_due_date(6, 10, 2.1, false); + c.set_due_date(6, 0, 10, 2.1, false); assert_eq!(c.due, 16); assert_eq!(c.interval, 2); assert_eq!(c.ease_factor, 2200); // interval doesn't change From c28306eb94f23c45f6695ac286cd722ff149c0d0 Mon Sep 17 00:00:00 2001 From: user1823 <92206575+user1823@users.noreply.github.com> Date: Sat, 21 Jun 2025 17:46:54 +0530 Subject: [PATCH 021/101] Save dr and decay in card even if item is None (#4106) * Document the purpose of storing dr and decay in card * Format * Fix type mismatch errors * Update memory_state.rs * Save dr and decay in card even if item is None * Format * Fix mismatched types * Update memory_state.rs --- rslib/src/scheduler/fsrs/memory_state.rs | 135 +++++++++++++---------- 1 file changed, 74 insertions(+), 61 deletions(-) diff --git a/rslib/src/scheduler/fsrs/memory_state.rs b/rslib/src/scheduler/fsrs/memory_state.rs index f5af97674..d01cde767 100644 --- a/rslib/src/scheduler/fsrs/memory_state.rs +++ b/rslib/src/scheduler/fsrs/memory_state.rs @@ -105,77 +105,92 @@ impl Collection { progress.update(true, |state| state.current_cards = idx as u32 + 1)?; let mut card = self.storage.get_card(card_id)?.or_not_found(card_id)?; let original = card.clone(); - if let (Some(req), Some(item)) = (&req, item) { - card.set_memory_state(&fsrs, Some(item), historical_retention.unwrap())?; + if let Some(req) = &req { + // Store decay and desired retention in the card so that add-ons, card info, + // stats and browser search/sorts don't need to access the deck config. + // Unlike memory states, scheduler doesn't use decay and dr stored in the card. card.desired_retention = desired_retention; card.decay = decay; - // if rescheduling - if let Some(reviews) = &last_revlog_info { - // and we have a last review time for the card - if let Some(last_info) = reviews.get(&card.id) { - if let Some(last_review) = &last_info.last_reviewed_at { - let days_elapsed = - timing.next_day_at.elapsed_days_since(*last_review) as i32; - // and the card's not new - if let Some(state) = &card.memory_state { - // or in (re)learning - if card.ctype == CardType::Review { - let deck = self - .get_deck(card.original_or_current_deck_id())? - .or_not_found(card.original_or_current_deck_id())?; - let deckconfig_id = deck.config_id().unwrap(); - // reschedule it - let original_interval = card.interval; - let interval = fsrs.next_interval( - Some(state.stability), - card.desired_retention.unwrap(), - 0, - ); - card.interval = rescheduler - .as_mut() - .and_then(|r| { - r.find_interval( - interval, - 1, - req.max_interval, - days_elapsed as u32, - deckconfig_id, - get_fuzz_seed(&card, true), - ) - }) - .unwrap_or_else(|| { - with_review_fuzz( - card.get_fuzz_factor(true), - interval, - 1, - req.max_interval, - ) - }); - let due = if card.original_due != 0 { - &mut card.original_due - } else { - &mut card.due - }; - let new_due = (timing.days_elapsed as i32) - days_elapsed - + card.interval as i32; - if let Some(rescheduler) = &mut rescheduler { - rescheduler.update_due_cnt_per_day( - *due, - new_due, - deckconfig_id, + if let Some(item) = item { + card.set_memory_state(&fsrs, Some(item), historical_retention.unwrap())?; + // if rescheduling + if let Some(reviews) = &last_revlog_info { + // and we have a last review time for the card + if let Some(last_info) = reviews.get(&card.id) { + if let Some(last_review) = &last_info.last_reviewed_at { + let days_elapsed = + timing.next_day_at.elapsed_days_since(*last_review) as i32; + // and the card's not new + if let Some(state) = &card.memory_state { + // or in (re)learning + if card.ctype == CardType::Review { + let deck = self + .get_deck(card.original_or_current_deck_id())? + .or_not_found(card.original_or_current_deck_id())?; + let deckconfig_id = deck.config_id().unwrap(); + // reschedule it + let original_interval = card.interval; + let interval = fsrs.next_interval( + Some(state.stability), + desired_retention.unwrap(), + 0, ); + card.interval = rescheduler + .as_mut() + .and_then(|r| { + r.find_interval( + interval, + 1, + req.max_interval, + days_elapsed as u32, + deckconfig_id, + get_fuzz_seed(&card, true), + ) + }) + .unwrap_or_else(|| { + with_review_fuzz( + card.get_fuzz_factor(true), + interval, + 1, + req.max_interval, + ) + }); + let due = if card.original_due != 0 { + &mut card.original_due + } else { + &mut card.due + }; + let new_due = (timing.days_elapsed as i32) + - days_elapsed + + card.interval as i32; + if let Some(rescheduler) = &mut rescheduler { + rescheduler.update_due_cnt_per_day( + *due, + new_due, + deckconfig_id, + ); + } + *due = new_due; + // Add a rescheduled revlog entry + self.log_rescheduled_review( + &card, + original_interval, + usn, + )?; } - *due = new_due; - // Add a rescheduled revlog entry - self.log_rescheduled_review(&card, original_interval, usn)?; } } } } + } else { + // clear memory states if item is None + card.memory_state = None; } } else { + // clear FSRS data if FSRS is disabled card.memory_state = None; card.desired_retention = None; + card.decay = None; } self.update_card_inner(&mut card, original, usn)?; } @@ -213,8 +228,6 @@ impl Collection { decay, }) } else { - card.memory_state = None; - card.desired_retention = None; Ok(ComputeMemoryStateResponse { state: None, desired_retention, From 5cc3a2276ba3adeea3e53bb532af3d343c7f475b Mon Sep 17 00:00:00 2001 From: user1823 <92206575+user1823@users.noreply.github.com> Date: Sat, 21 Jun 2025 17:47:18 +0530 Subject: [PATCH 022/101] Fix repeated ticks in reviews graph (#4108) Regressed in #4086 --- ts/routes/graphs/reviews.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/routes/graphs/reviews.ts b/ts/routes/graphs/reviews.ts index 164fc4199..fd3786b5f 100644 --- a/ts/routes/graphs/reviews.ts +++ b/ts/routes/graphs/reviews.ts @@ -141,7 +141,7 @@ export function renderReviews( const yTickFormat = (n: number): string => { if (showTime) { - return timeSpan(n / 1000, true, false, TimespanUnit.Hours); + return timeSpan(n / 1000, true, true, TimespanUnit.Hours); } else { if (Math.round(n) != n) { return ""; From cfd448565ae0e9e917a0cdff1d3a5c5d89ac0fb0 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sat, 21 Jun 2025 19:03:23 +0700 Subject: [PATCH 023/101] Fix sync-server separate compile https://forums.ankiweb.net/t/anki-25-06-beta/62271/96 --- rslib/sync/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rslib/sync/Cargo.toml b/rslib/sync/Cargo.toml index e2d960503..7a8f8534a 100644 --- a/rslib/sync/Cargo.toml +++ b/rslib/sync/Cargo.toml @@ -13,4 +13,4 @@ path = "main.rs" name = "anki-sync-server" [dependencies] -anki.workspace = true +anki = { workspace = true, features = ["rustls"] } From 246fa75a35b3ace140ddc97b45f137eea07ddd1c Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sat, 21 Jun 2025 19:13:26 +0700 Subject: [PATCH 024/101] Create release wheel as part of normal build Avoids the need for a separate publish --- qt/release/{update.sh => build.sh} | 7 +++++-- qt/release/publish.sh | 13 ------------- tools/build | 4 +++- 3 files changed, 8 insertions(+), 16 deletions(-) rename qt/release/{update.sh => build.sh} (91%) delete mode 100755 qt/release/publish.sh diff --git a/qt/release/update.sh b/qt/release/build.sh similarity index 91% rename from qt/release/update.sh rename to qt/release/build.sh index ddd99c398..423638bc4 100755 --- a/qt/release/update.sh +++ b/qt/release/build.sh @@ -2,7 +2,7 @@ set -e -test -f update.sh || { +test -f build.sh || { echo "run from release folder" exit 1 } @@ -63,6 +63,9 @@ echo "Generated pyproject.toml with version $VERSION" # Show diff if .old file exists if [ -f pyproject.toml.old ]; then echo - echo "Differences from previous version:" + echo "Differences from previous release version:" diff -u --color=always pyproject.toml.old pyproject.toml || true fi + +echo "Building wheel..." +"$UV" build --wheel --out-dir "$PROJ_ROOT/out/wheels" diff --git a/qt/release/publish.sh b/qt/release/publish.sh deleted file mode 100755 index 273e34953..000000000 --- a/qt/release/publish.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -# Get the project root (two levels up from qt/release) -PROJ_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" - -# Use extracted uv binary -UV="$PROJ_ROOT/out/extracted/uv/uv" - -rm -rf dist -"$UV" build --wheel - -#UV_PUBLISH_TOKEN=$(pass show w/pypi-api-test) "$UV" publish --index testpypi -UV_PUBLISH_TOKEN=$(pass show w/pypi-api) "$UV" publish diff --git a/tools/build b/tools/build index 3df9456ed..4074ff98d 100755 --- a/tools/build +++ b/tools/build @@ -1,6 +1,8 @@ #!/bin/bash -set -e +set -eo pipefail + rm -rf out/wheels/* RELEASE=2 ./ninja wheels +(cd qt/release && ./build.sh) echo "wheels are in out/wheels" From 782645d92e25b7d84a8f75ae8cfc3eaca39d16de Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sat, 21 Jun 2025 19:13:30 +0700 Subject: [PATCH 025/101] Bump beta version --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index 9079196d0..a38238a29 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -25.06b4 +25.06b5 From eb6c977e082f076dcac3056dc28ea8ecfad4e661 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sun, 22 Jun 2025 20:25:15 +0700 Subject: [PATCH 026/101] Add menu to launcher, and improve terminal display on Windows --- Cargo.toml | 2 +- qt/launcher/src/bin/build_win.rs | 5 +- qt/launcher/src/main.rs | 348 +++++++++++++++++++++++++--- qt/launcher/src/platform/mac.rs | 7 +- qt/launcher/src/platform/unix.rs | 4 +- qt/launcher/src/platform/windows.rs | 61 ++++- 6 files changed, 378 insertions(+), 49 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 61cca8649..d2ce2ce2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -138,7 +138,7 @@ unic-ucd-category = "0.9.0" unicode-normalization = "0.1.24" walkdir = "2.5.0" which = "8.0.0" -winapi = { version = "0.3", features = ["wincon"] } +winapi = { version = "0.3", features = ["wincon", "errhandlingapi", "consoleapi"] } windows = { version = "0.61.3", features = ["Media_SpeechSynthesis", "Media_Core", "Foundation_Collections", "Storage_Streams"] } wiremock = "0.6.3" xz2 = "0.1.7" diff --git a/qt/launcher/src/bin/build_win.rs b/qt/launcher/src/bin/build_win.rs index 959034438..ff385d9ea 100644 --- a/qt/launcher/src/bin/build_win.rs +++ b/qt/launcher/src/bin/build_win.rs @@ -114,10 +114,13 @@ fn copy_files(output_dir: &Path) -> Result<()> { let launcher_dst = output_dir.join("anki.exe"); copy_file(&launcher_src, &launcher_dst)?; - // Copy uv.exe + // Copy uv.exe and uvw.exe let uv_src = PathBuf::from("../../../out/extracted/uv/uv.exe"); let uv_dst = output_dir.join("uv.exe"); copy_file(&uv_src, &uv_dst)?; + let uv_src = PathBuf::from("../../../out/extracted/uv/uvw.exe"); + let uv_dst = output_dir.join("uvw.exe"); + copy_file(&uv_src, &uv_dst)?; println!("Copying support files..."); diff --git a/qt/launcher/src/main.rs b/qt/launcher/src/main.rs index 4f397ad99..40896caa9 100644 --- a/qt/launcher/src/main.rs +++ b/qt/launcher/src/main.rs @@ -4,28 +4,48 @@ #![windows_subsystem = "windows"] use std::io::stdin; +use std::io::stdout; +use std::io::Write; use std::process::Command; +use std::time::SystemTime; +use std::time::UNIX_EPOCH; use anki_io::copy_if_newer; use anki_io::create_dir_all; use anki_io::modified_time; +use anki_io::read_file; use anki_io::remove_file; use anki_io::write_file; use anki_process::CommandExt; use anyhow::Context; use anyhow::Result; +use crate::platform::ensure_terminal_shown; use crate::platform::exec_anki; use crate::platform::get_anki_binary_path; use crate::platform::get_exe_and_resources_dirs; use crate::platform::get_uv_binary_name; use crate::platform::handle_first_launch; -use crate::platform::handle_terminal_launch; use crate::platform::initial_terminal_setup; use crate::platform::launch_anki_detached; mod platform; +#[derive(Debug, Clone)] +pub enum VersionKind { + PyOxidizer(String), + Uv(String), +} + +#[derive(Debug, Clone)] +pub enum MainMenuChoice { + Latest, + KeepExisting, + Version(VersionKind), + ToggleBetas, + Quit, +} + #[derive(Debug, Clone, Default)] pub struct Config { pub show_console: bool, @@ -33,6 +53,9 @@ pub struct Config { fn main() { if let Err(e) = run() { + let mut config: Config = Config::default(); + initial_terminal_setup(&mut config); + eprintln!("Error: {:#}", e); eprintln!("Press enter to close..."); let mut input = String::new(); @@ -43,8 +66,7 @@ fn main() { } fn run() -> Result<()> { - let mut config = Config::default(); - initial_terminal_setup(&mut config); + let mut config: Config = Config::default(); let uv_install_root = dirs::data_local_dir() .context("Unable to determine data_dir")? @@ -62,60 +84,320 @@ fn run() -> Result<()> { // Create install directory and copy project files in create_dir_all(&uv_install_root)?; + let had_user_pyproj = user_pyproject_path.exists(); + if !had_user_pyproj { + // during initial launcher testing, enable betas by default + write_file(&prerelease_marker, "")?; + } + copy_if_newer(&dist_pyproject_path, &user_pyproject_path)?; copy_if_newer(&dist_python_version_path, &user_python_version_path)?; - let pyproject_has_changed = - !user_pyproject_path.exists() || !sync_complete_marker.exists() || { - let pyproject_toml_time = modified_time(&user_pyproject_path)?; - let sync_complete_time = modified_time(&sync_complete_marker)?; - Ok::(pyproject_toml_time > sync_complete_time) - } - .unwrap_or(true); + let pyproject_has_changed = !sync_complete_marker.exists() || { + let pyproject_toml_time = modified_time(&user_pyproject_path)?; + let sync_complete_time = modified_time(&sync_complete_marker)?; + Ok::(pyproject_toml_time > sync_complete_time) + } + .unwrap_or(true); if !pyproject_has_changed { // If venv is already up to date, exec as normal + initial_terminal_setup(&mut config); let anki_bin = get_anki_binary_path(&uv_install_root); exec_anki(&anki_bin, &config)?; return Ok(()); } // we'll need to launch uv; reinvoke ourselves in a terminal so the user can see - handle_terminal_launch()?; + ensure_terminal_shown()?; + print!("\x1B[2J\x1B[H"); // Clear screen and move cursor to top + println!("\x1B[1mAnki Launcher\x1B[0m\n"); - // Remove sync marker before attempting sync - let _ = remove_file(&sync_complete_marker); + // Check if there's an existing installation before removing marker + let has_existing_install = sync_complete_marker.exists(); - // Sync the venv - let mut command = Command::new(&uv_path); - command - .current_dir(&uv_install_root) - .args(["sync", "--upgrade", "--managed-python"]); + loop { + let menu_choice = get_main_menu_choice(has_existing_install, &prerelease_marker); - // Set UV_PRERELEASE=allow if prerelease file exists - if prerelease_marker.exists() { - command.env("UV_PRERELEASE", "allow"); + match menu_choice { + MainMenuChoice::Quit => std::process::exit(0), + MainMenuChoice::KeepExisting => { + // Skip sync, just launch existing installation + break; + } + MainMenuChoice::ToggleBetas => { + // Toggle beta prerelease file + if prerelease_marker.exists() { + let _ = remove_file(&prerelease_marker); + println!("Beta releases disabled."); + } else { + write_file(&prerelease_marker, "")?; + println!("Beta releases enabled."); + } + println!(); + continue; + } + _ => { + // For other choices, update project files and sync + update_pyproject_for_version( + menu_choice.clone(), + dist_pyproject_path.clone(), + user_pyproject_path.clone(), + dist_python_version_path.clone(), + user_python_version_path.clone(), + )?; + + // Remove sync marker before attempting sync + let _ = remove_file(&sync_complete_marker); + + // Sync the venv + let mut command = Command::new(&uv_path); + command.current_dir(&uv_install_root).args([ + "sync", + "--upgrade", + "--managed-python", + ]); + + // Add python version if .python-version file exists + if user_python_version_path.exists() { + let python_version = read_file(&user_python_version_path)?; + let python_version_str = String::from_utf8(python_version) + .context("Invalid UTF-8 in .python-version")?; + let python_version_trimmed = python_version_str.trim(); + command.args(["--python", python_version_trimmed]); + } + + // Set UV_PRERELEASE=allow if beta mode is enabled + if prerelease_marker.exists() { + command.env("UV_PRERELEASE", "allow"); + } + + match command.ensure_success() { + Ok(_) => { + // Sync succeeded, break out of loop + break; + } + Err(e) => { + // If sync fails due to things like a missing wheel on pypi, + // we need to remove the lockfile or uv will cache the bad result. + let _ = remove_file(&uv_lock_path); + println!("Install failed: {:#}", e); + println!(); + continue; + } + } + } + } } - // temporarily force it on during initial beta testing - command.env("UV_PRERELEASE", "allow"); - - if let Err(e) = command.ensure_success() { - // If sync fails due to things like a missing wheel on pypi, - // we need to remove the lockfile or uv will cache the bad result. - let _ = remove_file(&uv_lock_path); - return Err(e.into()); - } - - // Write marker file to indicate successful sync - write_file(&sync_complete_marker, "")?; + // Write marker file to indicate we've completed the sync process + write_sync_marker(&sync_complete_marker)?; // First launch let anki_bin = get_anki_binary_path(&uv_install_root); handle_first_launch(&anki_bin)?; + println!("\nPress enter to start Anki."); + + let mut input = String::new(); + let _ = stdin().read_line(&mut input); + // Then launch the binary as detached subprocess so the terminal can close launch_anki_detached(&anki_bin, &config)?; Ok(()) } + +fn write_sync_marker(sync_complete_marker: &std::path::Path) -> Result<()> { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .context("Failed to get system time")? + .as_secs(); + write_file(sync_complete_marker, timestamp.to_string())?; + Ok(()) +} + +fn get_main_menu_choice( + has_existing_install: bool, + prerelease_marker: &std::path::Path, +) -> MainMenuChoice { + loop { + println!("1) Latest Anki (just press enter)"); + println!("2) Choose a version"); + if has_existing_install { + println!("3) Keep existing version"); + } + println!(); + + let betas_enabled = prerelease_marker.exists(); + println!( + "4) Allow betas: {}", + if betas_enabled { "on" } else { "off" } + ); + println!("5) Quit"); + print!("> "); + let _ = stdout().flush(); + + let mut input = String::new(); + let _ = stdin().read_line(&mut input); + let input = input.trim(); + + println!(); + + return match input { + "" | "1" => MainMenuChoice::Latest, + "2" => MainMenuChoice::Version(get_version_kind()), + "3" => { + if has_existing_install { + MainMenuChoice::KeepExisting + } else { + println!("Invalid input. Please try again.\n"); + continue; + } + } + "4" => MainMenuChoice::ToggleBetas, + "5" => MainMenuChoice::Quit, + _ => { + println!("Invalid input. Please try again."); + continue; + } + }; + } +} + +fn get_version_kind() -> VersionKind { + loop { + println!("Enter the version you want to install:"); + print!("> "); + let _ = stdout().flush(); + + let mut input = String::new(); + let _ = stdin().read_line(&mut input); + let input = input.trim(); + + if input.is_empty() { + println!("Please enter a version."); + continue; + } + + match parse_version_kind(input) { + Some(version_kind) => { + println!(); + return version_kind; + } + None => { + println!("Invalid version format. Please enter a version like 24.10 or 25.06.1 (minimum 2.1.50)"); + continue; + } + } + } +} + +fn update_pyproject_for_version( + menu_choice: MainMenuChoice, + dist_pyproject_path: std::path::PathBuf, + user_pyproject_path: std::path::PathBuf, + dist_python_version_path: std::path::PathBuf, + user_python_version_path: std::path::PathBuf, +) -> Result<()> { + match menu_choice { + MainMenuChoice::Latest => { + let content = read_file(&dist_pyproject_path)?; + write_file(&user_pyproject_path, &content)?; + let python_version_content = read_file(&dist_python_version_path)?; + write_file(&user_python_version_path, &python_version_content)?; + } + MainMenuChoice::KeepExisting => { + // Do nothing - keep existing pyproject.toml and .python-version + } + MainMenuChoice::ToggleBetas => { + // This should not be reached as ToggleBetas is handled in the loop + unreachable!("ToggleBetas should be handled in the main loop"); + } + MainMenuChoice::Version(version_kind) => { + let content = read_file(&dist_pyproject_path)?; + let content_str = + String::from_utf8(content).context("Invalid UTF-8 in pyproject.toml")?; + let updated_content = match &version_kind { + VersionKind::PyOxidizer(version) => { + // Replace package name and add PyQt6 dependencies + content_str.replace( + "anki-release", + &format!( + concat!( + "aqt[qt6]=={}\",\n", + " \"pyqt6==6.6.1\",\n", + " \"pyqt6-qt6==6.6.2\",\n", + " \"pyqt6-webengine==6.6.0\",\n", + " \"pyqt6-webengine-qt6==6.6.2\",\n", + " \"pyqt6_sip==13.6.0" + ), + version + ), + ) + } + VersionKind::Uv(version) => { + content_str.replace("anki-release", &format!("anki-release=={}", version)) + } + }; + write_file(&user_pyproject_path, &updated_content)?; + + // Update .python-version based on version kind + match &version_kind { + VersionKind::PyOxidizer(_) => { + write_file(&user_python_version_path, "3.9")?; + } + VersionKind::Uv(_) => { + copy_if_newer(&dist_python_version_path, &user_python_version_path)?; + } + } + } + MainMenuChoice::Quit => { + std::process::exit(0); + } + } + Ok(()) +} + +fn parse_version_kind(version: &str) -> Option { + let numeric_chars: String = version + .chars() + .filter(|c| c.is_ascii_digit() || *c == '.') + .collect(); + + let parts: Vec<&str> = numeric_chars.split('.').collect(); + + if parts.len() < 2 { + return None; + } + + let major: u32 = match parts[0].parse() { + Ok(val) => val, + Err(_) => return None, + }; + + let minor: u32 = match parts[1].parse() { + Ok(val) => val, + Err(_) => return None, + }; + + let patch: u32 = if parts.len() >= 3 { + match parts[2].parse() { + Ok(val) => val, + Err(_) => return None, + } + } else { + 0 // Default patch to 0 if not provided + }; + + // Reject versions < 2.1.50 + if major == 2 && (minor != 1 || patch < 50) { + return None; + } + + if major < 25 || (major == 25 && minor < 6) { + Some(VersionKind::PyOxidizer(version.to_string())) + } else { + Some(VersionKind::Uv(version.to_string())) + } +} diff --git a/qt/launcher/src/platform/mac.rs b/qt/launcher/src/platform/mac.rs index b5157dd4b..1e7e4a695 100644 --- a/qt/launcher/src/platform/mac.rs +++ b/qt/launcher/src/platform/mac.rs @@ -28,12 +28,9 @@ pub fn launch_anki_detached(anki_bin: &std::path::Path, _config: &crate::Config) Ok(()) } -pub fn handle_terminal_launch() -> Result<()> { +pub fn ensure_terminal_shown() -> Result<()> { let stdout_is_terminal = std::io::IsTerminal::is_terminal(&std::io::stdout()); - if stdout_is_terminal { - print!("\x1B[2J\x1B[H"); // Clear screen and move cursor to top - println!("\x1B[1mPreparing to start Anki...\x1B[0m\n"); - } else { + if !stdout_is_terminal { // If launched from GUI, relaunch in Terminal.app relaunch_in_terminal()?; } diff --git a/qt/launcher/src/platform/unix.rs b/qt/launcher/src/platform/unix.rs index 4155f39d1..65a223f30 100644 --- a/qt/launcher/src/platform/unix.rs +++ b/qt/launcher/src/platform/unix.rs @@ -16,9 +16,7 @@ pub fn initial_terminal_setup(_config: &mut Config) { // No special terminal setup needed on Unix } -pub fn handle_terminal_launch() -> Result<()> { - print!("\x1B[2J\x1B[H"); // Clear screen and move cursor to top - println!("\x1B[1mPreparing to start Anki...\x1B[0m\n"); +pub fn ensure_terminal_shown() -> Result<()> { // Skip terminal relaunch on non-macOS Unix systems as we don't know which // terminal is installed Ok(()) diff --git a/qt/launcher/src/platform/windows.rs b/qt/launcher/src/platform/windows.rs index c536c8c76..4e3752d44 100644 --- a/qt/launcher/src/platform/windows.rs +++ b/qt/launcher/src/platform/windows.rs @@ -7,27 +7,77 @@ use std::process::Command; use anki_process::CommandExt; use anyhow::Context; use anyhow::Result; +use winapi::um::consoleapi; +use winapi::um::errhandlingapi; +use winapi::um::wincon; use crate::Config; -pub fn handle_terminal_launch() -> Result<()> { - // uv will do this itself +pub fn ensure_terminal_shown() -> Result<()> { + ensure_console(); + // // Check if we're already relaunched to prevent infinite recursion + // if std::env::var("ANKI_LAUNCHER_IN_TERMINAL").is_ok() { + // println!("Recurse: Preparing to start Anki...\n"); + // return Ok(()); + // } + + // if have_console { + // } else { + // relaunch_in_cmd()?; + // } Ok(()) } +fn ensure_console() { + unsafe { + if !wincon::GetConsoleWindow().is_null() { + return; + } + + if consoleapi::AllocConsole() == 0 { + let error_code = errhandlingapi::GetLastError(); + eprintln!("unexpected AllocConsole error: {}", error_code); + return; + } + + // This black magic triggers Windows to switch to the new + // ANSI-supporting console host, which is usually only available + // when the app is built with the console subsystem. + let _ = Command::new("cmd").args(&["/C", ""]).status(); + } +} + +fn attach_to_parent_console() -> bool { + unsafe { + if !wincon::GetConsoleWindow().is_null() { + // we have a console already + println!("attach: already had console, false"); + return false; + } + + if wincon::AttachConsole(wincon::ATTACH_PARENT_PROCESS) != 0 { + // successfully attached to parent + println!("attach: true"); + true + } else { + println!("attach: false"); + false + } + } +} + /// If parent process has a console (eg cmd.exe), redirect our output there. /// Sets config.show_console to true if successfully attached to console. pub fn initial_terminal_setup(config: &mut Config) { use std::ffi::CString; use libc_stdhandle::*; - use winapi::um::wincon; - let console_attached = unsafe { wincon::AttachConsole(wincon::ATTACH_PARENT_PROCESS) }; - if console_attached == 0 { + if !attach_to_parent_console() { return; } + // we launched without a console, so we'll need to open stdin/out/err let conin = CString::new("CONIN$").unwrap(); let conout = CString::new("CONOUT$").unwrap(); let r = CString::new("r").unwrap(); @@ -113,6 +163,5 @@ pub fn get_exe_and_resources_dirs() -> Result<(PathBuf, PathBuf)> { } pub fn get_uv_binary_name() -> &'static str { - // Windows uses standard uv binary name "uv.exe" } From d2f818fad286f08d86bc5243cb8bb7c83e3263e9 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sun, 22 Jun 2025 20:57:53 +0700 Subject: [PATCH 027/101] macOS launcher improvements - do mpv initial run in parallel - improve messages, show dots regularly --- qt/aqt/package.py | 74 ++++++++++++++++----------------- qt/launcher/mac/build.sh | 10 ++--- qt/launcher/src/main.rs | 2 + qt/launcher/src/platform/mac.rs | 39 ++++++++++++++++- 4 files changed, 81 insertions(+), 44 deletions(-) diff --git a/qt/aqt/package.py b/qt/aqt/package.py index a0642fca0..f1834b594 100644 --- a/qt/aqt/package.py +++ b/qt/aqt/package.py @@ -23,47 +23,45 @@ def first_run_setup() -> None: if not is_mac: return - def _dot(): - print(".", flush=True, end="") - - _dot() - import anki.collection - - _dot() - import PyQt6.sip - - _dot() - import PyQt6.QtCore - - _dot() - import PyQt6.QtGui - - _dot() - import PyQt6.QtNetwork - - _dot() - import PyQt6.QtQuick - - _dot() - import PyQt6.QtWebChannel - - _dot() - import PyQt6.QtWebEngineCore - - _dot() - import PyQt6.QtWebEngineWidgets - - _dot() + # Import anki_audio first and spawn commands import anki_audio - import PyQt6.QtWidgets audio_pkg_path = Path(anki_audio.__file__).parent - # Invoke mpv and lame - cmd = [Path(""), "--version"] + # Start mpv and lame commands concurrently + processes = [] for cmd_name in ["mpv", "lame"]: - _dot() - cmd[0] = audio_pkg_path / cmd_name - subprocess.run([str(cmd[0]), str(cmd[1])], check=True, capture_output=True) + cmd_path = audio_pkg_path / cmd_name + proc = subprocess.Popen( + [str(cmd_path), "--version"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + processes.append(proc) - print() + # Continue with other imports while commands run + import concurrent.futures + + import bs4 + import flask + import flask_cors + import markdown + import PyQt6.QtCore + import PyQt6.QtGui + import PyQt6.QtNetwork + import PyQt6.QtQuick + import PyQt6.QtWebChannel + import PyQt6.QtWebEngineCore + import PyQt6.QtWebEngineWidgets + import PyQt6.QtWidgets + import PyQt6.sip + import requests + import waitress + + import anki.collection + + from . import _macos_helper + + # Wait for both commands to complete + for proc in processes: + proc.wait() diff --git a/qt/launcher/mac/build.sh b/qt/launcher/mac/build.sh index eb4483488..0ec39ad8f 100755 --- a/qt/launcher/mac/build.sh +++ b/qt/launcher/mac/build.sh @@ -47,8 +47,8 @@ done codesign -vvv "$APP_LAUNCHER" spctl -a "$APP_LAUNCHER" -# Notarize -./notarize.sh "$OUTPUT_DIR" - -# Bundle -./dmg/build.sh "$OUTPUT_DIR" \ No newline at end of file +# Notarize and bundle (skip if NODMG is set) +if [ -z "$NODMG" ]; then + ./notarize.sh "$OUTPUT_DIR" + ./dmg/build.sh "$OUTPUT_DIR" +fi \ No newline at end of file diff --git a/qt/launcher/src/main.rs b/qt/launcher/src/main.rs index 40896caa9..2ad3ac00c 100644 --- a/qt/launcher/src/main.rs +++ b/qt/launcher/src/main.rs @@ -172,6 +172,8 @@ fn run() -> Result<()> { command.env("UV_PRERELEASE", "allow"); } + println!("\x1B[1mUpdating Anki...\x1B[0m\n"); + match command.ensure_success() { Ok(_) => { // Sync succeeded, break out of loop diff --git a/qt/launcher/src/platform/mac.rs b/qt/launcher/src/platform/mac.rs index 1e7e4a695..57d6bcc73 100644 --- a/qt/launcher/src/platform/mac.rs +++ b/qt/launcher/src/platform/mac.rs @@ -3,6 +3,11 @@ use std::os::unix::process::CommandExt; use std::process::Command; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::thread; +use std::time::Duration; use anki_process::CommandExt as AnkiCommandExt; use anyhow::Context; @@ -25,6 +30,9 @@ pub fn launch_anki_detached(anki_bin: &std::path::Path, _config: &crate::Config) .process_group(0) .ensure_spawn()?; std::mem::forget(child); + + println!("Anki will start shortly."); + println!("\x1B[1mYou can close this window.\x1B[0m\n"); Ok(()) } @@ -34,6 +42,10 @@ pub fn ensure_terminal_shown() -> Result<()> { // If launched from GUI, relaunch in Terminal.app relaunch_in_terminal()?; } + + // Set terminal title to "Anki Launcher" + print!("\x1b]0;Anki Launcher\x07"); + Ok(()) } @@ -47,12 +59,37 @@ fn relaunch_in_terminal() -> Result<()> { } pub fn handle_first_launch(anki_bin: &std::path::Path) -> Result<()> { + use std::io::Write; + use std::io::{ + self, + }; + // Pre-validate by running --version to trigger any Gatekeeper checks - println!("\n\x1B[1mThis may take a few minutes. Please wait...\x1B[0m"); + print!("\n\x1B[1mThis may take a few minutes. Please wait\x1B[0m"); + io::stdout().flush().unwrap(); + + // Start progress indicator + let running = Arc::new(AtomicBool::new(true)); + let running_clone = running.clone(); + let progress_thread = thread::spawn(move || { + while running_clone.load(Ordering::Relaxed) { + print!("."); + io::stdout().flush().unwrap(); + thread::sleep(Duration::from_secs(1)); + } + }); + let _ = Command::new(anki_bin) .env("ANKI_FIRST_RUN", "1") .arg("--version") + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) .ensure_success(); + + // Stop progress indicator + running.store(false, Ordering::Relaxed); + progress_thread.join().unwrap(); + println!(); // New line after dots Ok(()) } From b250a2f7245cb41f10581bfca2f89106fc0e366c Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sun, 22 Jun 2025 21:52:44 +0700 Subject: [PATCH 028/101] Add terminal support for *nix --- qt/launcher/lin/build.sh | 43 ++++++++++++++++++------- qt/launcher/src/platform/mac.rs | 16 ++-------- qt/launcher/src/platform/unix.rs | 55 ++++++++++++++++++++++++++++++-- 3 files changed, 87 insertions(+), 27 deletions(-) diff --git a/qt/launcher/lin/build.sh b/qt/launcher/lin/build.sh index e4ddce243..de96a1b50 100755 --- a/qt/launcher/lin/build.sh +++ b/qt/launcher/lin/build.sh @@ -1,9 +1,15 @@ #!/bin/bash +# +# This script currently only supports universal builds on x86_64. +# set -e # Add Linux cross-compilation target rustup target add aarch64-unknown-linux-gnu +# Detect host architecture +HOST_ARCH=$(uname -m) + # Define output paths OUTPUT_DIR="../../../out/launcher" @@ -12,11 +18,18 @@ LAUNCHER_DIR="$OUTPUT_DIR/anki-launcher" # Clean existing output directory rm -rf "$LAUNCHER_DIR" -# Build binaries for both Linux architectures -cargo build -p launcher --release --target x86_64-unknown-linux-gnu -CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \ +# Build binaries based on host architecture +if [ "$HOST_ARCH" = "aarch64" ]; then + # On aarch64 host, only build for aarch64 cargo build -p launcher --release --target aarch64-unknown-linux-gnu -(cd ../../.. && ./ninja extract:uv_lin_arm) +else + # On other hosts, build for both architectures + cargo build -p launcher --release --target x86_64-unknown-linux-gnu + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \ + cargo build -p launcher --release --target aarch64-unknown-linux-gnu + # Extract uv_lin_arm for cross-compilation + (cd ../../.. && ./ninja extract:uv_lin_arm) +fi # Create output directory mkdir -p "$LAUNCHER_DIR" @@ -24,13 +37,21 @@ mkdir -p "$LAUNCHER_DIR" # Copy binaries and support files TARGET_DIR=${CARGO_TARGET_DIR:-../../../target} -# Copy launcher binaries with architecture suffixes -cp "$TARGET_DIR/x86_64-unknown-linux-gnu/release/launcher" "$LAUNCHER_DIR/launcher.amd64" -cp "$TARGET_DIR/aarch64-unknown-linux-gnu/release/launcher" "$LAUNCHER_DIR/launcher.arm64" - -# Copy uv binaries with architecture suffixes -cp "../../../out/extracted/uv/uv" "$LAUNCHER_DIR/uv.amd64" -cp "../../../out/extracted/uv_lin_arm/uv" "$LAUNCHER_DIR/uv.arm64" +# Copy binaries with architecture suffixes +if [ "$HOST_ARCH" = "aarch64" ]; then + # On aarch64 host, copy arm64 binary to both locations + cp "$TARGET_DIR/aarch64-unknown-linux-gnu/release/launcher" "$LAUNCHER_DIR/launcher.amd64" + cp "$TARGET_DIR/aarch64-unknown-linux-gnu/release/launcher" "$LAUNCHER_DIR/launcher.arm64" + # Copy uv binary to both locations + cp "../../../out/extracted/uv/uv" "$LAUNCHER_DIR/uv.amd64" + cp "../../../out/extracted/uv/uv" "$LAUNCHER_DIR/uv.arm64" +else + # On other hosts, copy architecture-specific binaries + cp "$TARGET_DIR/x86_64-unknown-linux-gnu/release/launcher" "$LAUNCHER_DIR/launcher.amd64" + cp "$TARGET_DIR/aarch64-unknown-linux-gnu/release/launcher" "$LAUNCHER_DIR/launcher.arm64" + cp "../../../out/extracted/uv/uv" "$LAUNCHER_DIR/uv.amd64" + cp "../../../out/extracted/uv_lin_arm/uv" "$LAUNCHER_DIR/uv.arm64" +fi # Copy support files from lin directory for file in README.md anki.1 anki.desktop anki.png anki.xml anki.xpm install.sh uninstall.sh anki; do diff --git a/qt/launcher/src/platform/mac.rs b/qt/launcher/src/platform/mac.rs index 57d6bcc73..2369f538a 100644 --- a/qt/launcher/src/platform/mac.rs +++ b/qt/launcher/src/platform/mac.rs @@ -15,6 +15,7 @@ use anyhow::Result; // Re-export Unix functions that macOS uses pub use super::unix::{ + ensure_terminal_shown, exec_anki, get_anki_binary_path, initial_terminal_setup, @@ -36,20 +37,7 @@ pub fn launch_anki_detached(anki_bin: &std::path::Path, _config: &crate::Config) Ok(()) } -pub fn ensure_terminal_shown() -> Result<()> { - let stdout_is_terminal = std::io::IsTerminal::is_terminal(&std::io::stdout()); - if !stdout_is_terminal { - // If launched from GUI, relaunch in Terminal.app - relaunch_in_terminal()?; - } - - // Set terminal title to "Anki Launcher" - print!("\x1b]0;Anki Launcher\x07"); - - Ok(()) -} - -fn relaunch_in_terminal() -> Result<()> { +pub fn relaunch_in_terminal() -> Result<()> { let current_exe = std::env::current_exe().context("Failed to get current executable path")?; Command::new("open") .args(["-a", "Terminal"]) diff --git a/qt/launcher/src/platform/unix.rs b/qt/launcher/src/platform/unix.rs index 65a223f30..324bf5aa3 100644 --- a/qt/launcher/src/platform/unix.rs +++ b/qt/launcher/src/platform/unix.rs @@ -3,6 +3,7 @@ #![allow(dead_code)] +use std::io::IsTerminal; use std::path::PathBuf; use std::process::Command; @@ -17,8 +18,58 @@ pub fn initial_terminal_setup(_config: &mut Config) { } pub fn ensure_terminal_shown() -> Result<()> { - // Skip terminal relaunch on non-macOS Unix systems as we don't know which - // terminal is installed + let stdout_is_terminal = IsTerminal::is_terminal(&std::io::stdout()); + if !stdout_is_terminal { + // If launched from GUI, try to relaunch in a terminal + crate::platform::relaunch_in_terminal()?; + } + + // Set terminal title to "Anki Launcher" + print!("\x1b]2;Anki Launcher\x07"); + + Ok(()) +} + +#[cfg(not(target_os = "macos"))] +pub fn relaunch_in_terminal() -> Result<()> { + let current_exe = std::env::current_exe().context("Failed to get current executable path")?; + + // Try terminals in order of preference + let terminals = [ + ("x-terminal-emulator", vec!["-e"]), + ("gnome-terminal", vec!["--"]), + ("konsole", vec!["-e"]), + ("xfce4-terminal", vec!["-e"]), + ("alacritty", vec!["-e"]), + ("kitty", vec![]), + ("foot", vec![]), + ("urxvt", vec!["-e"]), + ("xterm", vec!["-e"]), + ]; + + for (terminal_cmd, args) in &terminals { + // Check if terminal exists + if Command::new("which") + .arg(terminal_cmd) + .output() + .map(|o| o.status.success()) + .unwrap_or(false) + { + // Try to launch the terminal + let mut cmd = Command::new(terminal_cmd); + if args.is_empty() { + cmd.arg(¤t_exe); + } else { + cmd.args(args).arg(¤t_exe); + } + + if cmd.spawn().is_ok() { + std::process::exit(0); + } + } + } + + // If no terminal worked, continue without relaunching Ok(()) } From a73f1507ba6a0e2174b8f9487d83a87278cc5d3d Mon Sep 17 00:00:00 2001 From: llama Date: Wed, 25 Jun 2025 19:08:25 +0800 Subject: [PATCH 029/101] use KeyboardEvent.key instead of code (#4114) --- ts/lib/tag-editor/TagInput.svelte | 2 +- ts/lib/tslib/keys.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ts/lib/tag-editor/TagInput.svelte b/ts/lib/tag-editor/TagInput.svelte index a8d76bcee..31d3b51f6 100644 --- a/ts/lib/tag-editor/TagInput.svelte +++ b/ts/lib/tag-editor/TagInput.svelte @@ -166,7 +166,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } function onKeydown(event: KeyboardEvent): void { - switch (event.code) { + switch (event.key) { case "Enter": onEnter(event); break; diff --git a/ts/lib/tslib/keys.ts b/ts/lib/tslib/keys.ts index 9bd6b42d8..58f571fac 100644 --- a/ts/lib/tslib/keys.ts +++ b/ts/lib/tslib/keys.ts @@ -90,7 +90,7 @@ export function keyToPlatformString(key: string): string { } export function isArrowLeft(event: KeyboardEvent): boolean { - if (event.code === "ArrowLeft") { + if (event.key === "ArrowLeft") { return true; } @@ -98,7 +98,7 @@ export function isArrowLeft(event: KeyboardEvent): boolean { } export function isArrowRight(event: KeyboardEvent): boolean { - if (event.code === "ArrowRight") { + if (event.key === "ArrowRight") { return true; } @@ -106,7 +106,7 @@ export function isArrowRight(event: KeyboardEvent): boolean { } export function isArrowUp(event: KeyboardEvent): boolean { - if (event.code === "ArrowUp") { + if (event.key === "ArrowUp") { return true; } @@ -114,7 +114,7 @@ export function isArrowUp(event: KeyboardEvent): boolean { } export function isArrowDown(event: KeyboardEvent): boolean { - if (event.code === "ArrowDown") { + if (event.key === "ArrowDown") { return true; } From 06195d1268ae01115dad79452d824a14df641f8c Mon Sep 17 00:00:00 2001 From: llama Date: Wed, 25 Jun 2025 19:15:45 +0800 Subject: [PATCH 030/101] add bottom and right margins to account for focus outline (#4115) --- ts/lib/tag-editor/TagEditor.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/lib/tag-editor/TagEditor.svelte b/ts/lib/tag-editor/TagEditor.svelte index cbbbf3f57..eb033ef7a 100644 --- a/ts/lib/tag-editor/TagEditor.svelte +++ b/ts/lib/tag-editor/TagEditor.svelte @@ -510,7 +510,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html border: 1px solid var(--border); border-radius: var(--border-radius); padding: 6px; - margin: 1px; + margin: 1px 3px 3px 1px; &:focus-within { outline-offset: -1px; From d6f93fab767dfca0522ef129f9c613c0ac0ce767 Mon Sep 17 00:00:00 2001 From: llama Date: Wed, 25 Jun 2025 19:20:31 +0800 Subject: [PATCH 031/101] adjust top toolbar height on body class update (#4120) --- qt/aqt/toolbar.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qt/aqt/toolbar.py b/qt/aqt/toolbar.py index 44f2ee66c..be547b5ba 100644 --- a/qt/aqt/toolbar.py +++ b/qt/aqt/toolbar.py @@ -87,6 +87,7 @@ class TopWebView(ToolbarWebView): else: self.flatten() + self.adjustHeightToFit() self.show() def _onHeight(self, qvar: int | None) -> None: From 992fb054bd2ada03cd9950c22f777a714e2291f1 Mon Sep 17 00:00:00 2001 From: Jarrett Ye Date: Wed, 25 Jun 2025 21:40:51 +0800 Subject: [PATCH 032/101] Refactor FSRS data clearing into Card::clear_fsrs_data (#4123) Extracted repeated logic for clearing FSRS-related fields into a new Card::clear_fsrs_data() method. Updated set_deck and FSRS disabling code paths to use this method for improved code reuse and maintainability. --- rslib/src/card/mod.rs | 10 +++++++--- rslib/src/scheduler/fsrs/memory_state.rs | 4 +--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/rslib/src/card/mod.rs b/rslib/src/card/mod.rs index 49d952ecf..598ac602b 100644 --- a/rslib/src/card/mod.rs +++ b/rslib/src/card/mod.rs @@ -185,12 +185,16 @@ impl Card { self.usn = usn; } - /// Caller must ensure provided deck exists and is not filtered. - fn set_deck(&mut self, deck: DeckId) { - self.remove_from_filtered_deck_restoring_queue(); + pub fn clear_fsrs_data(&mut self) { self.memory_state = None; self.desired_retention = None; self.decay = None; + } + + /// Caller must ensure provided deck exists and is not filtered. + fn set_deck(&mut self, deck: DeckId) { + self.remove_from_filtered_deck_restoring_queue(); + self.clear_fsrs_data(); self.deck_id = deck; } diff --git a/rslib/src/scheduler/fsrs/memory_state.rs b/rslib/src/scheduler/fsrs/memory_state.rs index d01cde767..b592e4da4 100644 --- a/rslib/src/scheduler/fsrs/memory_state.rs +++ b/rslib/src/scheduler/fsrs/memory_state.rs @@ -188,9 +188,7 @@ impl Collection { } } else { // clear FSRS data if FSRS is disabled - card.memory_state = None; - card.desired_retention = None; - card.decay = None; + card.clear_fsrs_data(); } self.update_card_inner(&mut card, original, usn)?; } From 630bdd31893d5113b81cd60612cef3ebf146d5b4 Mon Sep 17 00:00:00 2001 From: Luc Mcgrady Date: Wed, 25 Jun 2025 14:44:47 +0100 Subject: [PATCH 033/101] Fix/Optimize button alignment (#4117) * Fix/Button alignment * add hr --- ts/routes/deck-options/FsrsOptions.svelte | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ts/routes/deck-options/FsrsOptions.svelte b/ts/routes/deck-options/FsrsOptions.svelte index f573a0278..a2eb36fe3 100644 --- a/ts/routes/deck-options/FsrsOptions.svelte +++ b/ts/routes/deck-options/FsrsOptions.svelte @@ -391,7 +391,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -

+
+ +
@@ -419,4 +421,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html align-content: center; flex-wrap: wrap; } + + hr { + border-top: 1px solid var(--border); + opacity: 1; + } From 7edd9221acb93f02a02227d9b4a576a5ed89466f Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 27 Jun 2025 16:05:03 +0700 Subject: [PATCH 034/101] Avoid Qt's automatic About labeling It gets confused by our launcher process, and provides no way to alter the default assigned text while keeping the About role on a Mac. --- ftl/qt/qt-accel.ftl | 1 + qt/aqt/forms/main.ui | 2 +- qt/aqt/main.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ftl/qt/qt-accel.ftl b/ftl/qt/qt-accel.ftl index 327cd6c46..6c832b368 100644 --- a/ftl/qt/qt-accel.ftl +++ b/ftl/qt/qt-accel.ftl @@ -1,4 +1,5 @@ qt-accel-about = &About +qt-accel-about-mac = About Anki... qt-accel-cards = &Cards qt-accel-check-database = &Check Database qt-accel-check-media = Check &Media diff --git a/qt/aqt/forms/main.ui b/qt/aqt/forms/main.ui index 596ea985c..0687d4ef3 100644 --- a/qt/aqt/forms/main.ui +++ b/qt/aqt/forms/main.ui @@ -138,7 +138,7 @@ qt_accel_about - QAction::AboutRole + QAction::MenuRole::ApplicationSpecificRole diff --git a/qt/aqt/main.py b/qt/aqt/main.py index bc28e287b..8e01208a4 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -1405,6 +1405,7 @@ title="{}" {}>{}""".format( qconnect(m.actionDocumentation.triggered, self.onDocumentation) qconnect(m.actionDonate.triggered, self.onDonate) qconnect(m.actionAbout.triggered, self.onAbout) + m.actionAbout.setText(tr.qt_accel_about_mac()) # Edit qconnect(m.actionUndo.triggered, self.undo) From 9b287dc51ac8d7081cd54312bdbc390753f180e7 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Mon, 23 Jun 2025 16:59:50 +0700 Subject: [PATCH 035/101] Python dependency/wheel tweaks - Use --locked to assert that the lockfile won't change, so we need to explicitly 'uv lock' when making changes. Still trying to get to the bottom of why the lockfile sometimes has editable entries, which break things when switching between platforms. - Exclude __pycache__ from wheels - Move the typing stubs to our dev deps (https://github.com/ankitects/anki/pull/4074#pullrequestreview-2948088436) --- CLAUDE.md | 2 +- build/configure/src/aqt.rs | 7 +++++- build/configure/src/pylib.rs | 3 ++- build/runner/src/pyenv.rs | 2 +- pylib/hatch_build.py | 10 ++++++++- pylib/pyproject.toml | 4 +--- pyproject.toml | 10 +++++++++ qt/hatch_build.py | 9 ++++++-- qt/pyproject.toml | 7 ------ uv.lock | 42 ++++++++++++++++++------------------ 10 files changed, 58 insertions(+), 38 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 6ec6db642..3be5cc70b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -21,7 +21,7 @@ Please do this as a final step before marking a task as completed. During development, you can build/check subsections of our code: - Rust: 'cargo check' -- Python: './tools/dmypy' +- Python: './tools/dmypy', and if wheel-related, './ninja wheels' - TypeScript/Svelte: './ninja check:svelte' Be mindful that some changes (such as modifications to .proto files) may diff --git a/build/configure/src/aqt.rs b/build/configure/src/aqt.rs index 0a9b32270..83be77e91 100644 --- a/build/configure/src/aqt.rs +++ b/build/configure/src/aqt.rs @@ -342,7 +342,12 @@ fn build_wheel(build: &mut Build) -> Result<()> { name: "aqt", version: anki_version(), platform: None, - deps: inputs![":qt:aqt", glob!("qt/aqt/**"), "qt/pyproject.toml"], + deps: inputs![ + ":qt:aqt", + glob!("qt/aqt/**"), + "qt/pyproject.toml", + "qt/hatch_build.py" + ], }, ) } diff --git a/build/configure/src/pylib.rs b/build/configure/src/pylib.rs index bcef1ecc4..21820ae8b 100644 --- a/build/configure/src/pylib.rs +++ b/build/configure/src/pylib.rs @@ -68,7 +68,8 @@ pub fn build_pylib(build: &mut Build) -> Result<()> { deps: inputs![ ":pylib:anki", glob!("pylib/anki/**"), - "pylib/pyproject.toml" + "pylib/pyproject.toml", + "pylib/hatch_build.py" ], }, )?; diff --git a/build/runner/src/pyenv.rs b/build/runner/src/pyenv.rs index 0bd5ec662..d64c8fb3f 100644 --- a/build/runner/src/pyenv.rs +++ b/build/runner/src/pyenv.rs @@ -35,7 +35,7 @@ pub fn setup_pyenv(args: PyenvArgs) { run_command( Command::new(args.uv_bin) .env("UV_PROJECT_ENVIRONMENT", args.pyenv_folder.clone()) - .args(["sync", "--frozen"]) + .args(["sync", "--locked"]) .args(args.extra_args), ); diff --git a/pylib/hatch_build.py b/pylib/hatch_build.py index c3539da56..9e8ee9799 100644 --- a/pylib/hatch_build.py +++ b/pylib/hatch_build.py @@ -35,8 +35,16 @@ class CustomBuildHook(BuildHookInterface): assert generated_root.exists(), "you should build with --wheel" for path in generated_root.rglob("*"): - if path.is_file(): + if path.is_file() and not self._should_exclude(path): relative_path = path.relative_to(generated_root) # Place files under anki/ in the distribution dist_path = "anki" / relative_path force_include[str(path)] = str(dist_path) + + def _should_exclude(self, path: Path) -> bool: + """Check if a file should be excluded from the wheel.""" + # Exclude __pycache__ + path_str = str(path) + if "/__pycache__/" in path_str: + return True + return False diff --git a/pylib/pyproject.toml b/pylib/pyproject.toml index 555f30c86..d6d41f970 100644 --- a/pylib/pyproject.toml +++ b/pylib/pyproject.toml @@ -10,10 +10,8 @@ dependencies = [ "orjson", "protobuf>=4.21", "requests[socks]", + # remove after we update to min python 3.11+ "typing_extensions", - "types-protobuf", - "types-requests", - "types-orjson", # platform-specific dependencies "distro; sys_platform != 'darwin' and sys_platform != 'win32'", "psutil; sys_platform == 'win32'", diff --git a/pyproject.toml b/pyproject.toml index f5443e229..2e47ee2f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,16 @@ dev = [ "colorama", # for isort --color "wheel", "hatchling", # for type checking hatch_build.py files + "mock", + "types-protobuf", + "types-requests", + "types-orjson", + "types-decorator", + "types-flask", + "types-flask-cors", + "types-markdown", + "types-waitress", + "types-pywin32", ] [project.optional-dependencies] diff --git a/qt/hatch_build.py b/qt/hatch_build.py index fc716a57f..aaf345842 100644 --- a/qt/hatch_build.py +++ b/qt/hatch_build.py @@ -67,11 +67,16 @@ class CustomBuildHook(BuildHookInterface): def _should_exclude(self, path: Path) -> bool: """Check if a file should be excluded from the wheel.""" - # Match the exclusions from write_wheel.py exclude_aqt function + path_str = str(path) + + # Exclude __pycache__ + if "/__pycache__/" in path_str: + return True + if path.suffix in [".ui", ".scss", ".map", ".ts"]: return True if path.name.startswith("tsconfig"): return True - if "/aqt/data" in str(path): + if "/aqt/data" in path_str: return True return False diff --git a/qt/pyproject.toml b/qt/pyproject.toml index e6537c76c..ab5f50263 100644 --- a/qt/pyproject.toml +++ b/qt/pyproject.toml @@ -15,13 +15,6 @@ dependencies = [ "pywin32; sys.platform == 'win32'", "anki-mac-helper; sys.platform == 'darwin'", "pip-system-certs!=5.1", - "mock", - "types-decorator", - "types-flask", - "types-flask-cors", - "types-markdown", - "types-waitress", - "types-pywin32", "pyqt6>=6.2", "pyqt6-webengine>=6.2", # anki dependency is added dynamically in hatch_build.py with exact version diff --git a/uv.lock b/uv.lock index 348d14dd0..ce2f9536d 100644 --- a/uv.lock +++ b/uv.lock @@ -59,9 +59,6 @@ dependencies = [ { name = "protobuf" }, { name = "psutil", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "requests", extra = ["socks"] }, - { name = "types-orjson" }, - { name = "types-protobuf" }, - { name = "types-requests" }, { name = "typing-extensions" }, ] @@ -75,9 +72,6 @@ requires-dist = [ { name = "protobuf", specifier = ">=4.21" }, { name = "psutil", marker = "sys_platform == 'win32'" }, { name = "requests", extras = ["socks"] }, - { name = "types-orjson" }, - { name = "types-protobuf" }, - { name = "types-requests" }, { name = "typing-extensions" }, ] @@ -111,11 +105,21 @@ dev = [ { name = "colorama" }, { name = "hatchling" }, { name = "isort" }, + { name = "mock" }, { name = "mypy" }, { name = "mypy-protobuf" }, { name = "pychromedevtools" }, { name = "pylint" }, { name = "pytest" }, + { name = "types-decorator" }, + { name = "types-flask" }, + { name = "types-flask-cors" }, + { name = "types-markdown" }, + { name = "types-orjson" }, + { name = "types-protobuf" }, + { name = "types-pywin32" }, + { name = "types-requests" }, + { name = "types-waitress" }, { name = "wheel" }, ] @@ -133,11 +137,21 @@ dev = [ { name = "colorama" }, { name = "hatchling" }, { name = "isort" }, + { name = "mock" }, { name = "mypy" }, { name = "mypy-protobuf" }, { name = "pychromedevtools" }, { name = "pylint" }, { name = "pytest" }, + { name = "types-decorator" }, + { name = "types-flask" }, + { name = "types-flask-cors" }, + { name = "types-markdown" }, + { name = "types-orjson" }, + { name = "types-protobuf" }, + { name = "types-pywin32" }, + { name = "types-requests" }, + { name = "types-waitress" }, { name = "wheel" }, ] @@ -158,7 +172,6 @@ dependencies = [ { name = "flask" }, { name = "flask-cors" }, { name = "jsonschema" }, - { name = "mock" }, { name = "pip-system-certs", version = "4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "pip-system-certs", version = "5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "psutil", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, @@ -173,12 +186,6 @@ dependencies = [ { name = "pywin32", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "requests" }, { name = "send2trash" }, - { name = "types-decorator" }, - { name = "types-flask" }, - { name = "types-flask-cors" }, - { name = "types-markdown" }, - { name = "types-pywin32" }, - { name = "types-waitress" }, { name = "waitress" }, ] @@ -223,7 +230,6 @@ requires-dist = [ { name = "flask" }, { name = "flask-cors" }, { name = "jsonschema" }, - { name = "mock" }, { name = "pip-system-certs", specifier = "!=5.1" }, { name = "psutil", marker = "sys_platform == 'win32'" }, { name = "pyqt6", specifier = ">=6.2" }, @@ -251,12 +257,6 @@ requires-dist = [ { name = "pywin32", marker = "sys_platform == 'win32'" }, { name = "requests" }, { name = "send2trash" }, - { name = "types-decorator" }, - { name = "types-flask" }, - { name = "types-flask-cors" }, - { name = "types-markdown" }, - { name = "types-pywin32" }, - { name = "types-waitress" }, { name = "waitress", specifier = ">=2.0.0" }, ] provides-extras = ["audio", "qt", "qt66", "qt67", "qt69"] @@ -595,7 +595,7 @@ name = "importlib-metadata" version = "8.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp" }, + { name = "zipp", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } wheels = [ From 73edf23954928bae3e262f88114595ccff783bde Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Tue, 24 Jun 2025 13:05:06 +0700 Subject: [PATCH 036/101] Drop Pauker and SuperMemo importers from legacy importer The legacy importer has only been kept around to support some add-ons, and these are so infrequently used that they're better off shifted to add-ons (even they even still work) --- LICENSE | 2 - ftl/core/importing.ftl | 4 +- pylib/anki/importing/__init__.py | 4 - pylib/anki/importing/pauker.py | 94 ----- pylib/anki/importing/supermemo_xml.py | 484 -------------------------- pylib/pyproject.toml | 1 - pylib/tests/test_importing.py | 17 - tools/minilints/src/main.rs | 5 - uv.lock | 2 - 9 files changed, 2 insertions(+), 611 deletions(-) delete mode 100644 pylib/anki/importing/pauker.py delete mode 100644 pylib/anki/importing/supermemo_xml.py diff --git a/LICENSE b/LICENSE index 033dc2a0a..456a7cfd6 100644 --- a/LICENSE +++ b/LICENSE @@ -6,8 +6,6 @@ The following included source code items use a license other than AGPL3: In the pylib folder: - * The SuperMemo importer: GPL3 and 0BSD. - * The Pauker importer: BSD-3. * statsbg.py: CC BY 4.0. In the qt folder: diff --git a/ftl/core/importing.ftl b/ftl/core/importing.ftl index 70bc5f4d1..3b9f7c401 100644 --- a/ftl/core/importing.ftl +++ b/ftl/core/importing.ftl @@ -65,7 +65,6 @@ importing-with-deck-configs-help = If enabled, any deck options that the deck sharer included will also be imported. Otherwise, all decks will be assigned the default preset. importing-packaged-anki-deckcollection-apkg-colpkg-zip = Packaged Anki Deck/Collection (*.apkg *.colpkg *.zip) -importing-pauker-18-lesson-paugz = Pauker 1.8 Lesson (*.pau.gz) # the '|' character importing-pipe = Pipe # Warning displayed when the csv import preview table is clipped (some columns were hidden) @@ -78,7 +77,6 @@ importing-rows-had-num1d-fields-expected-num2d = '{ $row }' had { $found } field importing-selected-file-was-not-in-utf8 = Selected file was not in UTF-8 format. Please see the importing section of the manual. importing-semicolon = Semicolon importing-skipped = Skipped -importing-supermemo-xml-export-xml = Supermemo XML export (*.xml) importing-tab = Tab importing-tag-modified-notes = Tag modified notes: importing-text-separated-by-tabs-or-semicolons = Text separated by tabs or semicolons (*) @@ -252,3 +250,5 @@ importing-importing-collection = Importing collection... importing-unable-to-import-filename = Unable to import { $filename }: file type not supported importing-notes-that-could-not-be-imported = Notes that could not be imported as note type has changed: { $val } importing-added = Added +importing-pauker-18-lesson-paugz = Pauker 1.8 Lesson (*.pau.gz) +importing-supermemo-xml-export-xml = Supermemo XML export (*.xml) diff --git a/pylib/anki/importing/__init__.py b/pylib/anki/importing/__init__.py index cfc2cac3f..d4fccc643 100644 --- a/pylib/anki/importing/__init__.py +++ b/pylib/anki/importing/__init__.py @@ -11,8 +11,6 @@ from anki.importing.apkg import AnkiPackageImporter from anki.importing.base import Importer from anki.importing.csvfile import TextImporter from anki.importing.mnemo import MnemosyneImporter -from anki.importing.pauker import PaukerImporter -from anki.importing.supermemo_xml import SupermemoXmlImporter # type: ignore from anki.lang import TR @@ -24,8 +22,6 @@ def importers(col: Collection) -> Sequence[tuple[str, type[Importer]]]: AnkiPackageImporter, ), (col.tr.importing_mnemosyne_20_deck_db(), MnemosyneImporter), - (col.tr.importing_supermemo_xml_export_xml(), SupermemoXmlImporter), - (col.tr.importing_pauker_18_lesson_paugz(), PaukerImporter), ] anki.hooks.importing_importers(importers) return importers diff --git a/pylib/anki/importing/pauker.py b/pylib/anki/importing/pauker.py deleted file mode 100644 index ea5c45082..000000000 --- a/pylib/anki/importing/pauker.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright: Andreas Klauer -# License: BSD-3 - -# pylint: disable=invalid-name - -import gzip -import html -import math -import random -import time -import xml.etree.ElementTree as ET - -from anki.importing.noteimp import ForeignCard, ForeignNote, NoteImporter -from anki.stdmodels import _legacy_add_forward_reverse - -ONE_DAY = 60 * 60 * 24 - - -class PaukerImporter(NoteImporter): - """Import Pauker 1.8 Lesson (*.pau.gz)""" - - needMapper = False - allowHTML = True - - def run(self): - model = _legacy_add_forward_reverse(self.col) - model["name"] = "Pauker" - self.col.models.save(model, updateReqs=False) - self.col.models.set_current(model) - self.model = model - self.initMapping() - NoteImporter.run(self) - - def fields(self): - """Pauker is Front/Back""" - return 2 - - def foreignNotes(self): - """Build and return a list of notes.""" - notes = [] - - try: - f = gzip.open(self.file) - tree = ET.parse(f) # type: ignore - lesson = tree.getroot() - assert lesson.tag == "Lesson" - finally: - f.close() - - index = -4 - - for batch in lesson.findall("./Batch"): - index += 1 - - for card in batch.findall("./Card"): - # Create a note for this card. - front = card.findtext("./FrontSide/Text") - back = card.findtext("./ReverseSide/Text") - note = ForeignNote() - assert front and back - note.fields = [ - html.escape(x.strip()) - .replace("\n", "
") - .replace(" ", "  ") - for x in [front, back] - ] - notes.append(note) - - # Determine due date for cards. - frontdue = card.find("./FrontSide[@LearnedTimestamp]") - backdue = card.find("./ReverseSide[@Batch][@LearnedTimestamp]") - - if frontdue is not None: - note.cards[0] = self._learnedCard( - index, int(frontdue.attrib["LearnedTimestamp"]) - ) - - if backdue is not None: - note.cards[1] = self._learnedCard( - int(backdue.attrib["Batch"]), - int(backdue.attrib["LearnedTimestamp"]), - ) - - return notes - - def _learnedCard(self, batch, timestamp): - ivl = math.exp(batch) - now = time.time() - due = ivl - (now - timestamp / 1000.0) / ONE_DAY - fc = ForeignCard() - fc.due = self.col.sched.today + int(due + 0.5) - fc.ivl = random.randint(int(ivl * 0.90), int(ivl + 0.5)) - fc.factor = random.randint(1500, 2500) - return fc diff --git a/pylib/anki/importing/supermemo_xml.py b/pylib/anki/importing/supermemo_xml.py deleted file mode 100644 index 202592c2e..000000000 --- a/pylib/anki/importing/supermemo_xml.py +++ /dev/null @@ -1,484 +0,0 @@ -# Copyright: petr.michalec@gmail.com -# License: GNU GPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -# pytype: disable=attribute-error -# type: ignore -# pylint: disable=C -from __future__ import annotations - -import re -import sys -import time -import unicodedata -from string import capwords -from xml.dom import minidom -from xml.dom.minidom import Element, Text - -from anki.collection import Collection -from anki.importing.noteimp import ForeignCard, ForeignNote, NoteImporter -from anki.stdmodels import _legacy_add_basic_model - - -class SmartDict(dict): - """ - See http://www.peterbe.com/plog/SmartDict - Copyright 2005, Peter Bengtsson, peter@fry-it.com - 0BSD - - A smart dict can be instantiated either from a pythonic dict - or an instance object (eg. SQL recordsets) but it ensures that you can - do all the convenient lookups such as x.first_name, x['first_name'] or - x.get('first_name'). - """ - - def __init__(self, *a, **kw) -> None: - if a: - if isinstance(type(a[0]), dict): - kw.update(a[0]) - elif isinstance(type(a[0]), object): - kw.update(a[0].__dict__) - elif hasattr(a[0], "__class__") and a[0].__class__.__name__ == "SmartDict": - kw.update(a[0].__dict__) - - dict.__init__(self, **kw) - self.__dict__ = self - - -class SuperMemoElement(SmartDict): - "SmartDict wrapper to store SM Element data" - - def __init__(self, *a, **kw) -> None: - SmartDict.__init__(self, *a, **kw) - # default content - self.__dict__["lTitle"] = None - self.__dict__["Title"] = None - self.__dict__["Question"] = None - self.__dict__["Answer"] = None - self.__dict__["Count"] = None - self.__dict__["Type"] = None - self.__dict__["ID"] = None - self.__dict__["Interval"] = None - self.__dict__["Lapses"] = None - self.__dict__["Repetitions"] = None - self.__dict__["LastRepetiton"] = None - self.__dict__["AFactor"] = None - self.__dict__["UFactor"] = None - - -# This is an AnkiImporter -class SupermemoXmlImporter(NoteImporter): - needMapper = False - allowHTML = True - - """ - Supermemo XML export's to Anki parser. - Goes through a SM collection and fetch all elements. - - My SM collection was a big mess where topics and items were mixed. - I was unable to parse my content in a regular way like for loop on - minidom.getElementsByTagName() etc. My collection had also an - limitation, topics were splited into branches with max 100 items - on each. Learning themes were in deep structure. I wanted to have - full title on each element to be stored in tags. - - Code should be upgrade to support importing of SM2006 exports. - """ - - def __init__(self, col: Collection, file: str) -> None: - """Initialize internal variables. - Pameters to be exposed to GUI are stored in self.META""" - NoteImporter.__init__(self, col, file) - m = _legacy_add_basic_model(self.col) - m["name"] = "Supermemo" - self.col.models.save(m) - self.initMapping() - - self.lines = None - self.numFields = int(2) - - # SmXmlParse VARIABLES - self.xmldoc = None - self.pieces = [] - self.cntBuf = [] # to store last parsed data - self.cntElm = [] # to store SM Elements data - self.cntCol = [] # to store SM Colections data - - # store some meta info related to parse algorithm - # SmartDict works like dict / class wrapper - self.cntMeta = SmartDict() - self.cntMeta.popTitles = False - self.cntMeta.title = [] - - # META stores controls of import script, should be - # exposed to import dialog. These are default values. - self.META = SmartDict() - self.META.resetLearningData = False # implemented - self.META.onlyMemorizedItems = False # implemented - self.META.loggerLevel = 2 # implemented 0no,1info,2error,3debug - self.META.tagAllTopics = True - self.META.pathsToBeTagged = [ - "English for beginners", - "Advanced English 97", - "Phrasal Verbs", - ] # path patterns to be tagged - in gui entered like 'Advanced English 97|My Vocablary' - self.META.tagMemorizedItems = True # implemented - self.META.logToStdOutput = False # implemented - - self.notes = [] - - ## TOOLS - - def _fudgeText(self, text: str) -> str: - "Replace sm syntax to Anki syntax" - text = text.replace("\n\r", "
") - text = text.replace("\n", "
") - return text - - def _unicode2ascii(self, str: str) -> str: - "Remove diacritic punctuation from strings (titles)" - return "".join( - [ - c - for c in unicodedata.normalize("NFKD", str) - if not unicodedata.combining(c) - ] - ) - - def _decode_htmlescapes(self, html: str) -> str: - """Unescape HTML code.""" - # In case of bad formatted html you can import MinimalSoup etc.. see BeautifulSoup source code - from bs4 import BeautifulSoup - - # my sm2004 also ecaped & char in escaped sequences. - html = re.sub("&", "&", html) - - # https://anki.tenderapp.com/discussions/ankidesktop/39543-anki-is-replacing-the-character-by-when-i-exit-the-html-edit-mode-ctrlshiftx - if html.find(">") < 0: - return html - - # unescaped solitary chars < or > that were ok for minidom confuse btfl soup - # html = re.sub(u'>',u'>',html) - # html = re.sub(u'<',u'<',html) - - return str(BeautifulSoup(html, "html.parser")) - - def _afactor2efactor(self, af: float) -> float: - # Adapted from - - # Ranges for A-factors and E-factors - af_min = 1.2 - af_max = 6.9 - ef_min = 1.3 - ef_max = 3.3 - - # Sanity checks for the A-factor - if af < af_min: - af = af_min - elif af > af_max: - af = af_max - - # Scale af to the range 0..1 - af_scaled = (af - af_min) / (af_max - af_min) - # Rescale to the interval ef_min..ef_max - ef = ef_min + af_scaled * (ef_max - ef_min) - - return ef - - ## DEFAULT IMPORTER METHODS - - def foreignNotes(self) -> list[ForeignNote]: - # Load file and parse it by minidom - self.loadSource(self.file) - - # Migrating content / time consuming part - # addItemToCards is called for each sm element - self.logger("Parsing started.") - self.parse() - self.logger("Parsing done.") - - # Return imported cards - self.total = len(self.notes) - self.log.append("%d cards imported." % self.total) - return self.notes - - def fields(self) -> int: - return 2 - - ## PARSER METHODS - - def addItemToCards(self, item: SuperMemoElement) -> None: - "This method actually do conversion" - - # new anki card - note = ForeignNote() - - # clean Q and A - note.fields.append(self._fudgeText(self._decode_htmlescapes(item.Question))) - note.fields.append(self._fudgeText(self._decode_htmlescapes(item.Answer))) - note.tags = [] - - # pre-process scheduling data - # convert learning data - if ( - not self.META.resetLearningData - and int(item.Interval) >= 1 - and getattr(item, "LastRepetition", None) - ): - # migration of LearningData algorithm - tLastrep = time.mktime(time.strptime(item.LastRepetition, "%d.%m.%Y")) - tToday = time.time() - card = ForeignCard() - card.ivl = int(item.Interval) - card.lapses = int(item.Lapses) - card.reps = int(item.Repetitions) + int(item.Lapses) - nextDue = tLastrep + (float(item.Interval) * 86400.0) - remDays = int((nextDue - time.time()) / 86400) - card.due = self.col.sched.today + remDays - card.factor = int( - self._afactor2efactor(float(item.AFactor.replace(",", "."))) * 1000 - ) - note.cards[0] = card - - # categories & tags - # it's worth to have every theme (tree structure of sm collection) stored in tags, but sometimes not - # you can deceide if you are going to tag all toppics or just that containing some pattern - tTaggTitle = False - for pattern in self.META.pathsToBeTagged: - if ( - item.lTitle is not None - and pattern.lower() in " ".join(item.lTitle).lower() - ): - tTaggTitle = True - break - if tTaggTitle or self.META.tagAllTopics: - # normalize - remove diacritic punctuation from unicode chars to ascii - item.lTitle = [self._unicode2ascii(topic) for topic in item.lTitle] - - # Transform xyz / aaa / bbb / ccc on Title path to Tag xyzAaaBbbCcc - # clean things like [999] or [111-2222] from title path, example: xyz / [1000-1200] zyx / xyz - # clean whitespaces - # set Capital letters for first char of the word - tmp = list( - {re.sub(r"(\[[0-9]+\])", " ", i).replace("_", " ") for i in item.lTitle} - ) - tmp = list({re.sub(r"(\W)", " ", i) for i in tmp}) - tmp = list({re.sub("^[0-9 ]+$", "", i) for i in tmp}) - tmp = list({capwords(i).replace(" ", "") for i in tmp}) - tags = [j[0].lower() + j[1:] for j in tmp if j.strip() != ""] - - note.tags += tags - - if self.META.tagMemorizedItems and int(item.Interval) > 0: - note.tags.append("Memorized") - - self.logger("Element tags\t- " + repr(note.tags), level=3) - - self.notes.append(note) - - def logger(self, text: str, level: int = 1) -> None: - "Wrapper for Anki logger" - - dLevels = {0: "", 1: "Info", 2: "Verbose", 3: "Debug"} - if level <= self.META.loggerLevel: - # self.deck.updateProgress(_(text)) - - if self.META.logToStdOutput: - print( - self.__class__.__name__ - + " - " - + dLevels[level].ljust(9) - + " -\t" - + text - ) - - # OPEN AND LOAD - def openAnything(self, source): - """Open any source / actually only opening of files is used - @return an open handle which must be closed after use, i.e., handle.close()""" - - if source == "-": - return sys.stdin - - # try to open with urllib (if source is http, ftp, or file URL) - import urllib.error - import urllib.parse - import urllib.request - - try: - return urllib.request.urlopen(source) - except OSError: - pass - - # try to open with native open function (if source is pathname) - try: - return open(source, encoding="utf8") - except OSError: - pass - - # treat source as string - import io - - return io.StringIO(str(source)) - - def loadSource(self, source: str) -> None: - """Load source file and parse with xml.dom.minidom""" - self.source = source - self.logger("Load started...") - sock = open(self.source, encoding="utf8") - self.xmldoc = minidom.parse(sock).documentElement - sock.close() - self.logger("Load done.") - - # PARSE - def parse(self, node: Text | Element | None = None) -> None: - "Parse method - parses document elements" - - if node is None and self.xmldoc is not None: - node = self.xmldoc - - _method = "parse_%s" % node.__class__.__name__ - if hasattr(self, _method): - parseMethod = getattr(self, _method) - parseMethod(node) - else: - self.logger("No handler for method %s" % _method, level=3) - - def parse_Document(self, node): - "Parse XML document" - - self.parse(node.documentElement) - - def parse_Element(self, node: Element) -> None: - "Parse XML element" - - _method = "do_%s" % node.tagName - if hasattr(self, _method): - handlerMethod = getattr(self, _method) - handlerMethod(node) - else: - self.logger("No handler for method %s" % _method, level=3) - # print traceback.print_exc() - - def parse_Text(self, node: Text) -> None: - "Parse text inside elements. Text is stored into local buffer." - - text = node.data - self.cntBuf.append(text) - - # def parse_Comment(self, node): - # """ - # Source can contain XML comments, but we ignore them - # """ - # pass - - # DO - def do_SuperMemoCollection(self, node: Element) -> None: - "Process SM Collection" - - for child in node.childNodes: - self.parse(child) - - def do_SuperMemoElement(self, node: Element) -> None: - "Process SM Element (Type - Title,Topics)" - - self.logger("=" * 45, level=3) - - self.cntElm.append(SuperMemoElement()) - self.cntElm[-1]["lTitle"] = self.cntMeta["title"] - - # parse all child elements - for child in node.childNodes: - self.parse(child) - - # strip all saved strings, just for sure - for key in list(self.cntElm[-1].keys()): - if hasattr(self.cntElm[-1][key], "strip"): - self.cntElm[-1][key] = self.cntElm[-1][key].strip() - - # pop current element - smel = self.cntElm.pop() - - # Process cntElm if is valid Item (and not an Topic etc..) - # if smel.Lapses != None and smel.Interval != None and smel.Question != None and smel.Answer != None: - if smel.Title is None and smel.Question is not None and smel.Answer is not None: - if smel.Answer.strip() != "" and smel.Question.strip() != "": - # migrate only memorized otherway skip/continue - if self.META.onlyMemorizedItems and not (int(smel.Interval) > 0): - self.logger("Element skipped \t- not memorized ...", level=3) - else: - # import sm element data to Anki - self.addItemToCards(smel) - self.logger("Import element \t- " + smel["Question"], level=3) - - # print element - self.logger("-" * 45, level=3) - for key in list(smel.keys()): - self.logger( - "\t{} {}".format((key + ":").ljust(15), smel[key]), level=3 - ) - else: - self.logger("Element skipped \t- no valid Q and A ...", level=3) - - else: - # now we know that item was topic - # parsing of whole node is now finished - - # test if it's really topic - if smel.Title is not None: - # remove topic from title list - t = self.cntMeta["title"].pop() - self.logger("End of topic \t- %s" % (t), level=2) - - def do_Content(self, node: Element) -> None: - "Process SM element Content" - - for child in node.childNodes: - if hasattr(child, "tagName") and child.firstChild is not None: - self.cntElm[-1][child.tagName] = child.firstChild.data - - def do_LearningData(self, node: Element) -> None: - "Process SM element LearningData" - - for child in node.childNodes: - if hasattr(child, "tagName") and child.firstChild is not None: - self.cntElm[-1][child.tagName] = child.firstChild.data - - # It's being processed in do_Content now - # def do_Question(self, node): - # for child in node.childNodes: self.parse(child) - # self.cntElm[-1][node.tagName]=self.cntBuf.pop() - - # It's being processed in do_Content now - # def do_Answer(self, node): - # for child in node.childNodes: self.parse(child) - # self.cntElm[-1][node.tagName]=self.cntBuf.pop() - - def do_Title(self, node: Element) -> None: - "Process SM element Title" - - t = self._decode_htmlescapes(node.firstChild.data) - self.cntElm[-1][node.tagName] = t - self.cntMeta["title"].append(t) - self.cntElm[-1]["lTitle"] = self.cntMeta["title"] - self.logger("Start of topic \t- " + " / ".join(self.cntMeta["title"]), level=2) - - def do_Type(self, node: Element) -> None: - "Process SM element Type" - - if len(self.cntBuf) >= 1: - self.cntElm[-1][node.tagName] = self.cntBuf.pop() - - -# if __name__ == '__main__': - -# for testing you can start it standalone - -# file = u'/home/epcim/hg2g/dev/python/sm2anki/ADVENG2EXP.xxe.esc.zaloha_FINAL.xml' -# file = u'/home/epcim/hg2g/dev/python/anki/libanki/tests/importing/supermemo/original_ENGLISHFORBEGGINERS_noOEM.xml' -# file = u'/home/epcim/hg2g/dev/python/anki/libanki/tests/importing/supermemo/original_ENGLISHFORBEGGINERS_oem_1250.xml' -# file = str(sys.argv[1]) -# impo = SupermemoXmlImporter(Deck(),file) -# impo.foreignCards() - -# sys.exit(1) - -# vim: ts=4 sts=2 ft=python diff --git a/pylib/pyproject.toml b/pylib/pyproject.toml index d6d41f970..70bb7aba2 100644 --- a/pylib/pyproject.toml +++ b/pylib/pyproject.toml @@ -4,7 +4,6 @@ dynamic = ["version"] requires-python = ">=3.9" license = "AGPL-3.0-or-later" dependencies = [ - "beautifulsoup4", "decorator", "markdown", "orjson", diff --git a/pylib/tests/test_importing.py b/pylib/tests/test_importing.py index 191de51f4..b7b63de26 100644 --- a/pylib/tests/test_importing.py +++ b/pylib/tests/test_importing.py @@ -13,7 +13,6 @@ from anki.importing import ( Anki2Importer, AnkiPackageImporter, MnemosyneImporter, - SupermemoXmlImporter, TextImporter, ) from tests.shared import getEmptyCol, getUpgradeDeckPath @@ -306,22 +305,6 @@ def test_csv_tag_only_if_modified(): col.close() -@pytest.mark.filterwarnings("ignore:Using or importing the ABCs") -def test_supermemo_xml_01_unicode(): - col = getEmptyCol() - file = str(os.path.join(testDir, "support", "supermemo1.xml")) - i = SupermemoXmlImporter(col, file) - # i.META.logToStdOutput = True - i.run() - assert i.total == 1 - cid = col.db.scalar("select id from cards") - c = col.get_card(cid) - # Applies A Factor-to-E Factor conversion - assert c.factor == 2879 - assert c.reps == 7 - col.close() - - def test_mnemo(): col = getEmptyCol() file = str(os.path.join(testDir, "support", "mnemo.db")) diff --git a/tools/minilints/src/main.rs b/tools/minilints/src/main.rs index 3a3c06f2c..2650ec648 100644 --- a/tools/minilints/src/main.rs +++ b/tools/minilints/src/main.rs @@ -21,12 +21,7 @@ use walkdir::WalkDir; const NONSTANDARD_HEADER: &[&str] = &[ "./pylib/anki/_vendor/stringcase.py", - "./pylib/anki/importing/pauker.py", - "./pylib/anki/importing/supermemo_xml.py", "./pylib/anki/statsbg.py", - "./pylib/tools/protoc-gen-mypy.py", - "./python/pyqt/install.py", - "./python/write_wheel.py", "./qt/aqt/mpv.py", "./qt/aqt/winpaths.py", ]; diff --git a/uv.lock b/uv.lock index ce2f9536d..62c66e494 100644 --- a/uv.lock +++ b/uv.lock @@ -51,7 +51,6 @@ wheels = [ name = "anki" source = { editable = "pylib" } dependencies = [ - { name = "beautifulsoup4" }, { name = "decorator" }, { name = "distro", marker = "(sys_platform != 'darwin' and sys_platform != 'win32') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (sys_platform == 'darwin' and extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (sys_platform == 'win32' and extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "markdown" }, @@ -64,7 +63,6 @@ dependencies = [ [package.metadata] requires-dist = [ - { name = "beautifulsoup4" }, { name = "decorator" }, { name = "distro", marker = "sys_platform != 'darwin' and sys_platform != 'win32'" }, { name = "markdown" }, From de7de82f769f58d20c814fbdb5274aa366e887ef Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Thu, 26 Jun 2025 16:21:39 +0700 Subject: [PATCH 037/101] Refactor launcher + various tweaks - Launcher can now be accessed via Tools>Upgrade/Downgrade - Anki closes automatically on update - When launcher not available, show update link like in the past - It appears that access to the modern console host requires an app to be built with the windows console subsystem, so we introduce an extra anki-console.exe binary to relaunch ourselves with. Solves https://forums.ankiweb.net/t/new-online-installer-launcher/62745/50 - Windows now requires you to close the terminal like on a Mac, as I couldn't figure out how to have it automatically close. Suggestions welcome! - Reduce the amount of duplicate/near-duplicate code in the various platform files, and improve readability - Add a helper to install the current code into the launcher env - Fix cargo test failing to build on ARM64 Windows --- Cargo.lock | 1 + Cargo.toml | 2 +- ftl/qt/qt-accel.ftl | 1 + ftl/qt/qt-misc.ftl | 2 +- qt/aqt/forms/main.ui | 10 +- qt/aqt/main.py | 13 ++ qt/aqt/update.py | 54 ++++++-- qt/launcher/Cargo.toml | 5 + qt/launcher/src/bin/anki_console.rs | 58 +++++++++ qt/launcher/src/bin/build_win.rs | 10 +- qt/launcher/src/main.rs | 187 +++++++++++++++++----------- qt/launcher/src/platform/mac.rs | 70 ++--------- qt/launcher/src/platform/mod.rs | 108 ++++++++++++++-- qt/launcher/src/platform/unix.rs | 74 ----------- qt/launcher/src/platform/windows.rs | 152 +++++----------------- qt/launcher/win/anki-console.bat | 5 - qt/launcher/win/build.bat | 9 +- rslib/sync/Cargo.toml | 5 + tools/update-launcher-env | 15 +++ tools/update-launcher-env.bat | 8 ++ 20 files changed, 435 insertions(+), 354 deletions(-) create mode 100644 qt/launcher/src/bin/anki_console.rs delete mode 100644 qt/launcher/win/anki-console.bat create mode 100755 tools/update-launcher-env create mode 100644 tools/update-launcher-env.bat diff --git a/Cargo.lock b/Cargo.lock index 03f9e63c8..04e7c6c76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3543,6 +3543,7 @@ dependencies = [ "anki_io", "anki_process", "anyhow", + "camino", "dirs 6.0.0", "embed-resource", "libc", diff --git a/Cargo.toml b/Cargo.toml index d2ce2ce2a..61cca8649 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -138,7 +138,7 @@ unic-ucd-category = "0.9.0" unicode-normalization = "0.1.24" walkdir = "2.5.0" which = "8.0.0" -winapi = { version = "0.3", features = ["wincon", "errhandlingapi", "consoleapi"] } +winapi = { version = "0.3", features = ["wincon"] } windows = { version = "0.61.3", features = ["Media_SpeechSynthesis", "Media_Core", "Foundation_Collections", "Storage_Streams"] } wiremock = "0.6.3" xz2 = "0.1.7" diff --git a/ftl/qt/qt-accel.ftl b/ftl/qt/qt-accel.ftl index 6c832b368..3ab54eb24 100644 --- a/ftl/qt/qt-accel.ftl +++ b/ftl/qt/qt-accel.ftl @@ -46,3 +46,4 @@ qt-accel-zoom-editor-in = Zoom Editor &In qt-accel-zoom-editor-out = Zoom Editor &Out qt-accel-create-backup = Create &Backup qt-accel-load-backup = &Revert to Backup +qt-accel-upgrade-downgrade = Upgrade/Downgrade diff --git a/ftl/qt/qt-misc.ftl b/ftl/qt/qt-misc.ftl index 60c22ef8b..d7bbef990 100644 --- a/ftl/qt/qt-misc.ftl +++ b/ftl/qt/qt-misc.ftl @@ -73,7 +73,7 @@ qt-misc-second = qt-misc-layout-auto-enabled = Responsive layout enabled qt-misc-layout-vertical-enabled = Vertical layout enabled qt-misc-layout-horizontal-enabled = Horizontal layout enabled -qt-misc-please-restart-to-update-anki = Please restart Anki to update to the latest version. +qt-misc-open-anki-launcher = Change to a different Anki version? ## deprecated- these strings will be removed in the future, and do not need ## to be translated diff --git a/qt/aqt/forms/main.ui b/qt/aqt/forms/main.ui index 0687d4ef3..bffc67ad0 100644 --- a/qt/aqt/forms/main.ui +++ b/qt/aqt/forms/main.ui @@ -46,7 +46,7 @@ 0 0 667 - 24 + 43 @@ -93,6 +93,7 @@ + @@ -130,7 +131,7 @@ Ctrl+P - QAction::PreferencesRole + QAction::MenuRole::PreferencesRole
@@ -283,6 +284,11 @@ qt_accel_load_backup + + + qt_accel_upgrade_downgrade + + diff --git a/qt/aqt/main.py b/qt/aqt/main.py index 8e01208a4..b261cd34e 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -1308,6 +1308,14 @@ title="{}" {}>{}""".format( def onPrefs(self) -> None: aqt.dialogs.open("Preferences", self) + def on_upgrade_downgrade(self) -> None: + if not askUser(tr.qt_misc_open_anki_launcher()): + return + + from aqt.update import update_and_restart + + update_and_restart() + def onNoteTypes(self) -> None: import aqt.models @@ -1389,6 +1397,8 @@ title="{}" {}>{}""".format( ########################################################################## def setupMenus(self) -> None: + from aqt.update import have_launcher + m = self.form # File @@ -1418,6 +1428,9 @@ title="{}" {}>{}""".format( qconnect(m.actionCreateFiltered.triggered, self.onCram) qconnect(m.actionEmptyCards.triggered, self.onEmptyCards) qconnect(m.actionNoteTypes.triggered, self.onNoteTypes) + qconnect(m.action_upgrade_downgrade.triggered, self.on_upgrade_downgrade) + if not have_launcher(): + m.action_upgrade_downgrade.setVisible(False) qconnect(m.actionPreferences.triggered, self.onPrefs) # View diff --git a/qt/aqt/update.py b/qt/aqt/update.py index d8e92426c..61fec8e6b 100644 --- a/qt/aqt/update.py +++ b/qt/aqt/update.py @@ -1,7 +1,11 @@ # Copyright: Ankitects Pty Ltd and contributors # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +from __future__ import annotations + +import contextlib import os +import subprocess from pathlib import Path import aqt @@ -10,7 +14,7 @@ from anki.collection import CheckForUpdateResponse, Collection from anki.utils import dev_mode, int_time, int_version, is_mac, is_win, plat_desc from aqt.operations import QueryOp from aqt.qt import * -from aqt.utils import show_info, show_warning, showText, tr +from aqt.utils import openLink, show_warning, showText, tr def check_for_update() -> None: @@ -80,22 +84,56 @@ def prompt_to_update(mw: aqt.AnkiQt, ver: str) -> None: # ignore this update mw.pm.meta["suppressUpdate"] = ver elif ret == QMessageBox.StandardButton.Yes: - update_and_restart() + if have_launcher(): + update_and_restart() + else: + openLink(aqt.appWebsiteDownloadSection) + + +def _anki_launcher_path() -> str | None: + return os.getenv("ANKI_LAUNCHER") + + +def have_launcher() -> bool: + return _anki_launcher_path() is not None def update_and_restart() -> None: - """Download and install the update, then restart Anki.""" - update_on_next_run() - # todo: do this automatically in the future - show_info(tr.qt_misc_please_restart_to_update_anki()) + from aqt import mw + + launcher = _anki_launcher_path() + assert launcher + + _trigger_launcher_run() + + with contextlib.suppress(ResourceWarning): + env = os.environ.copy() + creationflags = 0 + if sys.platform == "win32": + creationflags = ( + subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS + ) + subprocess.Popen( + [launcher], + start_new_session=True, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + env=env, + creationflags=creationflags, + ) + + mw.app.quit() -def update_on_next_run() -> None: +def _trigger_launcher_run() -> None: """Bump the mtime on pyproject.toml in the local data directory to trigger an update on next run.""" try: # Get the local data directory equivalent to Rust's dirs::data_local_dir() if is_win: - data_dir = Path(os.environ.get("LOCALAPPDATA", "")) + from .winpaths import get_local_appdata + + data_dir = Path(get_local_appdata()) elif is_mac: data_dir = Path.home() / "Library" / "Application Support" else: # Linux diff --git a/qt/launcher/Cargo.toml b/qt/launcher/Cargo.toml index 45ca11e9b..735cd892e 100644 --- a/qt/launcher/Cargo.toml +++ b/qt/launcher/Cargo.toml @@ -11,6 +11,7 @@ rust-version.workspace = true anki_io.workspace = true anki_process.workspace = true anyhow.workspace = true +camino.workspace = true dirs.workspace = true [target.'cfg(windows)'.dependencies] @@ -22,5 +23,9 @@ libc-stdhandle.workspace = true name = "build_win" path = "src/bin/build_win.rs" +[[bin]] +name = "anki-console" +path = "src/bin/anki_console.rs" + [target.'cfg(windows)'.build-dependencies] embed-resource.workspace = true diff --git a/qt/launcher/src/bin/anki_console.rs b/qt/launcher/src/bin/anki_console.rs new file mode 100644 index 000000000..596377ba1 --- /dev/null +++ b/qt/launcher/src/bin/anki_console.rs @@ -0,0 +1,58 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +#![windows_subsystem = "console"] + +use std::env; +use std::io::stdin; +use std::process::Command; + +use anyhow::Context; +use anyhow::Result; + +fn main() { + if let Err(e) = run() { + eprintln!("Error: {:#}", e); + std::process::exit(1); + } +} + +fn run() -> Result<()> { + let current_exe = env::current_exe().context("Failed to get current executable path")?; + let exe_dir = current_exe + .parent() + .context("Failed to get executable directory")?; + + let anki_exe = exe_dir.join("anki.exe"); + + if !anki_exe.exists() { + anyhow::bail!("anki.exe not found in the same directory"); + } + + // Forward all command line arguments to anki.exe + let args: Vec = env::args().skip(1).collect(); + + let mut cmd = Command::new(&anki_exe); + cmd.args(&args); + + if std::env::var("ANKI_IMPLICIT_CONSOLE").is_err() { + // if directly invoked by the user, signal the launcher that the + // user wants a Python console + std::env::set_var("ANKI_CONSOLE", "1"); + } + + // Wait for the process to complete and forward its exit code + let status = cmd.status().context("Failed to execute anki.exe")?; + if !status.success() { + println!("\nPress enter to close."); + let mut input = String::new(); + let _ = stdin().read_line(&mut input); + } + + if let Some(code) = status.code() { + std::process::exit(code); + } else { + // Process was terminated by a signal + std::process::exit(1); + } +} diff --git a/qt/launcher/src/bin/build_win.rs b/qt/launcher/src/bin/build_win.rs index ff385d9ea..3ad2c7ce0 100644 --- a/qt/launcher/src/bin/build_win.rs +++ b/qt/launcher/src/bin/build_win.rs @@ -114,6 +114,12 @@ fn copy_files(output_dir: &Path) -> Result<()> { let launcher_dst = output_dir.join("anki.exe"); copy_file(&launcher_src, &launcher_dst)?; + // Copy anki-console binary + let console_src = + PathBuf::from(CARGO_TARGET_DIR).join("x86_64-pc-windows-msvc/release/anki-console.exe"); + let console_dst = output_dir.join("anki-console.exe"); + copy_file(&console_src, &console_dst)?; + // Copy uv.exe and uvw.exe let uv_src = PathBuf::from("../../../out/extracted/uv/uv.exe"); let uv_dst = output_dir.join("uv.exe"); @@ -133,14 +139,12 @@ fn copy_files(output_dir: &Path) -> Result<()> { output_dir.join(".python-version"), )?; - // Copy anki-console.bat - copy_file("anki-console.bat", output_dir.join("anki-console.bat"))?; - Ok(()) } fn sign_binaries(output_dir: &Path) -> Result<()> { sign_file(&output_dir.join("anki.exe"))?; + sign_file(&output_dir.join("anki-console.exe"))?; sign_file(&output_dir.join("uv.exe"))?; Ok(()) } diff --git a/qt/launcher/src/main.rs b/qt/launcher/src/main.rs index 2ad3ac00c..b2535f410 100644 --- a/qt/launcher/src/main.rs +++ b/qt/launcher/src/main.rs @@ -16,21 +16,34 @@ use anki_io::modified_time; use anki_io::read_file; use anki_io::remove_file; use anki_io::write_file; +use anki_io::ToUtf8Path; use anki_process::CommandExt; use anyhow::Context; use anyhow::Result; use crate::platform::ensure_terminal_shown; -use crate::platform::exec_anki; -use crate::platform::get_anki_binary_path; use crate::platform::get_exe_and_resources_dirs; use crate::platform::get_uv_binary_name; -use crate::platform::handle_first_launch; -use crate::platform::initial_terminal_setup; -use crate::platform::launch_anki_detached; +use crate::platform::launch_anki_after_update; +use crate::platform::launch_anki_normally; mod platform; +// todo: -c appearing as app name now + +struct State { + has_existing_install: bool, + prerelease_marker: std::path::PathBuf, + uv_install_root: std::path::PathBuf, + uv_path: std::path::PathBuf, + user_pyproject_path: std::path::PathBuf, + user_python_version_path: std::path::PathBuf, + dist_pyproject_path: std::path::PathBuf, + dist_python_version_path: std::path::PathBuf, + uv_lock_path: std::path::PathBuf, + sync_complete_marker: std::path::PathBuf, +} + #[derive(Debug, Clone)] pub enum VersionKind { PyOxidizer(String), @@ -46,16 +59,8 @@ pub enum MainMenuChoice { Quit, } -#[derive(Debug, Clone, Default)] -pub struct Config { - pub show_console: bool, -} - fn main() { if let Err(e) = run() { - let mut config: Config = Config::default(); - initial_terminal_setup(&mut config); - eprintln!("Error: {:#}", e); eprintln!("Press enter to close..."); let mut input = String::new(); @@ -66,58 +71,92 @@ fn main() { } fn run() -> Result<()> { - let mut config: Config = Config::default(); - let uv_install_root = dirs::data_local_dir() .context("Unable to determine data_dir")? .join("AnkiProgramFiles"); - let sync_complete_marker = uv_install_root.join(".sync_complete"); - let prerelease_marker = uv_install_root.join("prerelease"); let (exe_dir, resources_dir) = get_exe_and_resources_dirs()?; - let dist_pyproject_path = resources_dir.join("pyproject.toml"); - let user_pyproject_path = uv_install_root.join("pyproject.toml"); - let dist_python_version_path = resources_dir.join(".python-version"); - let user_python_version_path = uv_install_root.join(".python-version"); - let uv_lock_path = uv_install_root.join("uv.lock"); - let uv_path: std::path::PathBuf = exe_dir.join(get_uv_binary_name()); + + let state = State { + has_existing_install: uv_install_root.join(".sync_complete").exists(), + prerelease_marker: uv_install_root.join("prerelease"), + uv_install_root: uv_install_root.clone(), + uv_path: exe_dir.join(get_uv_binary_name()), + user_pyproject_path: uv_install_root.join("pyproject.toml"), + user_python_version_path: uv_install_root.join(".python-version"), + dist_pyproject_path: resources_dir.join("pyproject.toml"), + dist_python_version_path: resources_dir.join(".python-version"), + uv_lock_path: uv_install_root.join("uv.lock"), + sync_complete_marker: uv_install_root.join(".sync_complete"), + }; // Create install directory and copy project files in - create_dir_all(&uv_install_root)?; - let had_user_pyproj = user_pyproject_path.exists(); + create_dir_all(&state.uv_install_root)?; + let had_user_pyproj = state.user_pyproject_path.exists(); if !had_user_pyproj { // during initial launcher testing, enable betas by default - write_file(&prerelease_marker, "")?; + write_file(&state.prerelease_marker, "")?; } - copy_if_newer(&dist_pyproject_path, &user_pyproject_path)?; - copy_if_newer(&dist_python_version_path, &user_python_version_path)?; + copy_if_newer(&state.dist_pyproject_path, &state.user_pyproject_path)?; + copy_if_newer( + &state.dist_python_version_path, + &state.user_python_version_path, + )?; - let pyproject_has_changed = !sync_complete_marker.exists() || { - let pyproject_toml_time = modified_time(&user_pyproject_path)?; - let sync_complete_time = modified_time(&sync_complete_marker)?; + let pyproject_has_changed = !state.sync_complete_marker.exists() || { + let pyproject_toml_time = modified_time(&state.user_pyproject_path)?; + let sync_complete_time = modified_time(&state.sync_complete_marker)?; Ok::(pyproject_toml_time > sync_complete_time) } .unwrap_or(true); if !pyproject_has_changed { - // If venv is already up to date, exec as normal - initial_terminal_setup(&mut config); - let anki_bin = get_anki_binary_path(&uv_install_root); - exec_anki(&anki_bin, &config)?; + // If venv is already up to date, launch Anki normally + let args: Vec = std::env::args().skip(1).collect(); + let cmd = build_python_command(&state.uv_install_root, &args)?; + launch_anki_normally(cmd)?; return Ok(()); } - // we'll need to launch uv; reinvoke ourselves in a terminal so the user can see + // If we weren't in a terminal, respawn ourselves in one ensure_terminal_shown()?; + print!("\x1B[2J\x1B[H"); // Clear screen and move cursor to top println!("\x1B[1mAnki Launcher\x1B[0m\n"); - // Check if there's an existing installation before removing marker - let has_existing_install = sync_complete_marker.exists(); + main_menu_loop(&state)?; + // Write marker file to indicate we've completed the sync process + write_sync_marker(&state.sync_complete_marker)?; + + #[cfg(target_os = "macos")] + { + let cmd = build_python_command(&state.uv_install_root, &[])?; + platform::mac::prepare_for_launch_after_update(cmd)?; + } + + if cfg!(unix) && !cfg!(target_os = "macos") { + println!("\nPress enter to start Anki."); + let mut input = String::new(); + let _ = stdin().read_line(&mut input); + } else { + // on Windows/macOS, the user needs to close the terminal/console + // currently, but ideas on how we can avoid this would be good! + println!("Anki will start shortly."); + println!("\x1B[1mYou can close this window.\x1B[0m\n"); + } + + let cmd = build_python_command(&state.uv_install_root, &[])?; + launch_anki_after_update(cmd)?; + + Ok(()) +} + +fn main_menu_loop(state: &State) -> Result<()> { loop { - let menu_choice = get_main_menu_choice(has_existing_install, &prerelease_marker); + let menu_choice = + get_main_menu_choice(state.has_existing_install, &state.prerelease_marker); match menu_choice { MainMenuChoice::Quit => std::process::exit(0), @@ -127,40 +166,40 @@ fn run() -> Result<()> { } MainMenuChoice::ToggleBetas => { // Toggle beta prerelease file - if prerelease_marker.exists() { - let _ = remove_file(&prerelease_marker); + if state.prerelease_marker.exists() { + let _ = remove_file(&state.prerelease_marker); println!("Beta releases disabled."); } else { - write_file(&prerelease_marker, "")?; + write_file(&state.prerelease_marker, "")?; println!("Beta releases enabled."); } println!(); continue; } - _ => { + choice @ (MainMenuChoice::Latest | MainMenuChoice::Version(_)) => { // For other choices, update project files and sync update_pyproject_for_version( - menu_choice.clone(), - dist_pyproject_path.clone(), - user_pyproject_path.clone(), - dist_python_version_path.clone(), - user_python_version_path.clone(), + choice, + state.dist_pyproject_path.clone(), + state.user_pyproject_path.clone(), + state.dist_python_version_path.clone(), + state.user_python_version_path.clone(), )?; // Remove sync marker before attempting sync - let _ = remove_file(&sync_complete_marker); + let _ = remove_file(&state.sync_complete_marker); // Sync the venv - let mut command = Command::new(&uv_path); - command.current_dir(&uv_install_root).args([ + let mut command = Command::new(&state.uv_path); + command.current_dir(&state.uv_install_root).args([ "sync", "--upgrade", "--managed-python", ]); // Add python version if .python-version file exists - if user_python_version_path.exists() { - let python_version = read_file(&user_python_version_path)?; + if state.user_python_version_path.exists() { + let python_version = read_file(&state.user_python_version_path)?; let python_version_str = String::from_utf8(python_version) .context("Invalid UTF-8 in .python-version")?; let python_version_trimmed = python_version_str.trim(); @@ -168,7 +207,7 @@ fn run() -> Result<()> { } // Set UV_PRERELEASE=allow if beta mode is enabled - if prerelease_marker.exists() { + if state.prerelease_marker.exists() { command.env("UV_PRERELEASE", "allow"); } @@ -182,7 +221,7 @@ fn run() -> Result<()> { Err(e) => { // If sync fails due to things like a missing wheel on pypi, // we need to remove the lockfile or uv will cache the bad result. - let _ = remove_file(&uv_lock_path); + let _ = remove_file(&state.uv_lock_path); println!("Install failed: {:#}", e); println!(); continue; @@ -191,22 +230,6 @@ fn run() -> Result<()> { } } } - - // Write marker file to indicate we've completed the sync process - write_sync_marker(&sync_complete_marker)?; - - // First launch - let anki_bin = get_anki_binary_path(&uv_install_root); - handle_first_launch(&anki_bin)?; - - println!("\nPress enter to start Anki."); - - let mut input = String::new(); - let _ = stdin().read_line(&mut input); - - // Then launch the binary as detached subprocess so the terminal can close - launch_anki_detached(&anki_bin, &config)?; - Ok(()) } @@ -403,3 +426,25 @@ fn parse_version_kind(version: &str) -> Option { Some(VersionKind::Uv(version.to_string())) } } + +fn build_python_command(uv_install_root: &std::path::Path, args: &[String]) -> Result { + let python_exe = if cfg!(target_os = "windows") { + let show_console = std::env::var("ANKI_CONSOLE").is_ok(); + if show_console { + uv_install_root.join(".venv/Scripts/python.exe") + } else { + uv_install_root.join(".venv/Scripts/pythonw.exe") + } + } else { + uv_install_root.join(".venv/bin/python") + }; + + let mut cmd = Command::new(python_exe); + cmd.args(["-c", "import aqt; aqt.run()"]); + cmd.args(args); + // tell the Python code it was invoked by the launcher, and updating is + // available + cmd.env("ANKI_LAUNCHER", std::env::current_exe()?.utf8()?.as_str()); + + Ok(cmd) +} diff --git a/qt/launcher/src/platform/mac.rs b/qt/launcher/src/platform/mac.rs index 2369f538a..ab2c4b8fb 100644 --- a/qt/launcher/src/platform/mac.rs +++ b/qt/launcher/src/platform/mac.rs @@ -1,7 +1,8 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -use std::os::unix::process::CommandExt; +use std::io; +use std::io::Write; use std::process::Command; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; @@ -13,45 +14,7 @@ use anki_process::CommandExt as AnkiCommandExt; use anyhow::Context; use anyhow::Result; -// Re-export Unix functions that macOS uses -pub use super::unix::{ - ensure_terminal_shown, - exec_anki, - get_anki_binary_path, - initial_terminal_setup, -}; - -pub fn launch_anki_detached(anki_bin: &std::path::Path, _config: &crate::Config) -> Result<()> { - use std::process::Stdio; - - let child = Command::new(anki_bin) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .process_group(0) - .ensure_spawn()?; - std::mem::forget(child); - - println!("Anki will start shortly."); - println!("\x1B[1mYou can close this window.\x1B[0m\n"); - Ok(()) -} - -pub fn relaunch_in_terminal() -> Result<()> { - let current_exe = std::env::current_exe().context("Failed to get current executable path")?; - Command::new("open") - .args(["-a", "Terminal"]) - .arg(current_exe) - .ensure_spawn()?; - std::process::exit(0); -} - -pub fn handle_first_launch(anki_bin: &std::path::Path) -> Result<()> { - use std::io::Write; - use std::io::{ - self, - }; - +pub fn prepare_for_launch_after_update(mut cmd: Command) -> Result<()> { // Pre-validate by running --version to trigger any Gatekeeper checks print!("\n\x1B[1mThis may take a few minutes. Please wait\x1B[0m"); io::stdout().flush().unwrap(); @@ -67,7 +30,7 @@ pub fn handle_first_launch(anki_bin: &std::path::Path) -> Result<()> { } }); - let _ = Command::new(anki_bin) + let _ = cmd .env("ANKI_FIRST_RUN", "1") .arg("--version") .stdout(std::process::Stdio::null()) @@ -81,22 +44,11 @@ pub fn handle_first_launch(anki_bin: &std::path::Path) -> Result<()> { Ok(()) } -pub fn get_exe_and_resources_dirs() -> Result<(std::path::PathBuf, std::path::PathBuf)> { - let exe_dir = std::env::current_exe() - .context("Failed to get current executable path")? - .parent() - .context("Failed to get executable directory")? - .to_owned(); - - let resources_dir = exe_dir - .parent() - .context("Failed to get parent directory")? - .join("Resources"); - - Ok((exe_dir, resources_dir)) -} - -pub fn get_uv_binary_name() -> &'static str { - // macOS uses standard uv binary name - "uv" +pub fn relaunch_in_terminal() -> Result<()> { + let current_exe = std::env::current_exe().context("Failed to get current executable path")?; + Command::new("open") + .args(["-a", "Terminal"]) + .arg(current_exe) + .ensure_spawn()?; + std::process::exit(0); } diff --git a/qt/launcher/src/platform/mod.rs b/qt/launcher/src/platform/mod.rs index bb7208abe..9dc74f8e9 100644 --- a/qt/launcher/src/platform/mod.rs +++ b/qt/launcher/src/platform/mod.rs @@ -1,18 +1,108 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -#[cfg(unix)] +#[cfg(all(unix, not(target_os = "macos")))] mod unix; #[cfg(target_os = "macos")] -mod mac; +pub mod mac; #[cfg(target_os = "windows")] -mod windows; +pub mod windows; -#[cfg(target_os = "macos")] -pub use mac::*; -#[cfg(all(unix, not(target_os = "macos")))] -pub use unix::*; -#[cfg(target_os = "windows")] -pub use windows::*; +use std::path::PathBuf; + +use anki_process::CommandExt; +use anyhow::Context; +use anyhow::Result; + +pub fn get_exe_and_resources_dirs() -> Result<(PathBuf, PathBuf)> { + let exe_dir = std::env::current_exe() + .context("Failed to get current executable path")? + .parent() + .context("Failed to get executable directory")? + .to_owned(); + + let resources_dir = if cfg!(target_os = "macos") { + // On macOS, resources are in ../Resources relative to the executable + exe_dir + .parent() + .context("Failed to get parent directory")? + .join("Resources") + } else { + // On other platforms, resources are in the same directory as executable + exe_dir.clone() + }; + + Ok((exe_dir, resources_dir)) +} + +pub fn get_uv_binary_name() -> &'static str { + if cfg!(target_os = "windows") { + "uv.exe" + } else if cfg!(target_os = "macos") { + "uv" + } else if cfg!(target_arch = "x86_64") { + "uv.amd64" + } else { + "uv.arm64" + } +} + +pub fn launch_anki_after_update(mut cmd: std::process::Command) -> Result<()> { + use std::process::Stdio; + + cmd.stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()); + + #[cfg(windows)] + { + use std::os::windows::process::CommandExt; + const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; + const DETACHED_PROCESS: u32 = 0x00000008; + cmd.creation_flags(CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS); + } + + #[cfg(unix)] + { + use std::os::unix::process::CommandExt; + cmd.process_group(0); + } + + let child = cmd.ensure_spawn()?; + std::mem::forget(child); + + Ok(()) +} + +pub fn launch_anki_normally(mut cmd: std::process::Command) -> Result<()> { + #[cfg(windows)] + { + crate::platform::windows::attach_to_parent_console(); + cmd.ensure_success()?; + } + #[cfg(unix)] + cmd.ensure_exec()?; + Ok(()) +} + +#[cfg(windows)] +pub use windows::ensure_terminal_shown; + +#[cfg(unix)] +pub fn ensure_terminal_shown() -> Result<()> { + use std::io::IsTerminal; + + let stdout_is_terminal = IsTerminal::is_terminal(&std::io::stdout()); + if !stdout_is_terminal { + #[cfg(target_os = "macos")] + mac::relaunch_in_terminal()?; + #[cfg(not(target_os = "macos"))] + unix::relaunch_in_terminal()?; + } + + // Set terminal title to "Anki Launcher" + print!("\x1b]2;Anki Launcher\x07"); + Ok(()) +} diff --git a/qt/launcher/src/platform/unix.rs b/qt/launcher/src/platform/unix.rs index 324bf5aa3..0430bfa96 100644 --- a/qt/launcher/src/platform/unix.rs +++ b/qt/launcher/src/platform/unix.rs @@ -1,36 +1,11 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -#![allow(dead_code)] - -use std::io::IsTerminal; -use std::path::PathBuf; use std::process::Command; -use anki_process::CommandExt as AnkiCommandExt; use anyhow::Context; use anyhow::Result; -use crate::Config; - -pub fn initial_terminal_setup(_config: &mut Config) { - // No special terminal setup needed on Unix -} - -pub fn ensure_terminal_shown() -> Result<()> { - let stdout_is_terminal = IsTerminal::is_terminal(&std::io::stdout()); - if !stdout_is_terminal { - // If launched from GUI, try to relaunch in a terminal - crate::platform::relaunch_in_terminal()?; - } - - // Set terminal title to "Anki Launcher" - print!("\x1b]2;Anki Launcher\x07"); - - Ok(()) -} - -#[cfg(not(target_os = "macos"))] pub fn relaunch_in_terminal() -> Result<()> { let current_exe = std::env::current_exe().context("Failed to get current executable path")?; @@ -72,52 +47,3 @@ pub fn relaunch_in_terminal() -> Result<()> { // If no terminal worked, continue without relaunching Ok(()) } - -pub fn get_anki_binary_path(uv_install_root: &std::path::Path) -> PathBuf { - uv_install_root.join(".venv/bin/anki") -} - -pub fn launch_anki_detached(anki_bin: &std::path::Path, config: &Config) -> Result<()> { - // On non-macOS Unix systems, we don't need to detach since we never spawned a - // terminal - exec_anki(anki_bin, config) -} - -pub fn handle_first_launch(_anki_bin: &std::path::Path) -> Result<()> { - // No special first launch handling needed for generic Unix systems - Ok(()) -} - -pub fn exec_anki(anki_bin: &std::path::Path, _config: &Config) -> Result<()> { - let args: Vec = std::env::args().skip(1).collect(); - Command::new(anki_bin) - .args(args) - .ensure_exec() - .map_err(anyhow::Error::new) -} - -pub fn get_exe_and_resources_dirs() -> Result<(PathBuf, PathBuf)> { - let exe_dir = std::env::current_exe() - .context("Failed to get current executable path")? - .parent() - .context("Failed to get executable directory")? - .to_owned(); - - // On generic Unix systems, assume resources are in the same directory as - // executable - let resources_dir = exe_dir.clone(); - - Ok((exe_dir, resources_dir)) -} - -pub fn get_uv_binary_name() -> &'static str { - // Use architecture-specific uv binary for non-Mac Unix systems - if cfg!(target_arch = "x86_64") { - "uv.amd64" - } else if cfg!(target_arch = "aarch64") { - "uv.arm64" - } else { - // Fallback to generic uv for other architectures - "uv" - } -} diff --git a/qt/launcher/src/platform/windows.rs b/qt/launcher/src/platform/windows.rs index 4e3752d44..0a701c07a 100644 --- a/qt/launcher/src/platform/windows.rs +++ b/qt/launcher/src/platform/windows.rs @@ -1,82 +1,71 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -use std::path::PathBuf; use std::process::Command; -use anki_process::CommandExt; use anyhow::Context; use anyhow::Result; -use winapi::um::consoleapi; -use winapi::um::errhandlingapi; use winapi::um::wincon; -use crate::Config; - pub fn ensure_terminal_shown() -> Result<()> { - ensure_console(); - // // Check if we're already relaunched to prevent infinite recursion - // if std::env::var("ANKI_LAUNCHER_IN_TERMINAL").is_ok() { - // println!("Recurse: Preparing to start Anki...\n"); - // return Ok(()); - // } - - // if have_console { - // } else { - // relaunch_in_cmd()?; - // } - Ok(()) -} - -fn ensure_console() { unsafe { if !wincon::GetConsoleWindow().is_null() { - return; + // We already have a console, no need to spawn anki-console.exe + return Ok(()); } - - if consoleapi::AllocConsole() == 0 { - let error_code = errhandlingapi::GetLastError(); - eprintln!("unexpected AllocConsole error: {}", error_code); - return; - } - - // This black magic triggers Windows to switch to the new - // ANSI-supporting console host, which is usually only available - // when the app is built with the console subsystem. - let _ = Command::new("cmd").args(&["/C", ""]).status(); } + + if std::env::var("ANKI_IMPLICIT_CONSOLE").is_ok() && attach_to_parent_console() { + // Successfully attached to parent console + reconnect_stdio_to_console(); + return Ok(()); + } + + // No console available, spawn anki-console.exe and exit + let current_exe = std::env::current_exe().context("Failed to get current executable path")?; + let exe_dir = current_exe + .parent() + .context("Failed to get executable directory")?; + + let console_exe = exe_dir.join("anki-console.exe"); + + if !console_exe.exists() { + anyhow::bail!("anki-console.exe not found in the same directory"); + } + + // Spawn anki-console.exe without waiting + Command::new(&console_exe) + .env("ANKI_IMPLICIT_CONSOLE", "1") + .spawn() + .context("Failed to spawn anki-console.exe")?; + + // Exit immediately after spawning + std::process::exit(0); } -fn attach_to_parent_console() -> bool { +pub fn attach_to_parent_console() -> bool { unsafe { if !wincon::GetConsoleWindow().is_null() { // we have a console already - println!("attach: already had console, false"); return false; } if wincon::AttachConsole(wincon::ATTACH_PARENT_PROCESS) != 0 { // successfully attached to parent - println!("attach: true"); + reconnect_stdio_to_console(); true } else { - println!("attach: false"); false } } } -/// If parent process has a console (eg cmd.exe), redirect our output there. -/// Sets config.show_console to true if successfully attached to console. -pub fn initial_terminal_setup(config: &mut Config) { +/// Reconnect stdin/stdout/stderr to the console. +fn reconnect_stdio_to_console() { use std::ffi::CString; use libc_stdhandle::*; - if !attach_to_parent_console() { - return; - } - // we launched without a console, so we'll need to open stdin/out/err let conin = CString::new("CONIN$").unwrap(); let conout = CString::new("CONOUT$").unwrap(); @@ -89,79 +78,4 @@ pub fn initial_terminal_setup(config: &mut Config) { libc::freopen(conout.as_ptr(), w.as_ptr(), stdout()); libc::freopen(conout.as_ptr(), w.as_ptr(), stderr()); } - - config.show_console = true; -} - -pub fn get_anki_binary_path(uv_install_root: &std::path::Path) -> std::path::PathBuf { - uv_install_root.join(".venv/Scripts/anki.exe") -} - -fn build_python_command( - anki_bin: &std::path::Path, - args: &[String], - config: &Config, -) -> Result { - let venv_dir = anki_bin - .parent() - .context("Failed to get venv Scripts directory")? - .parent() - .context("Failed to get venv directory")?; - - // Use python.exe if show_console is true, otherwise pythonw.exe - let python_exe = if config.show_console { - venv_dir.join("Scripts/python.exe") - } else { - venv_dir.join("Scripts/pythonw.exe") - }; - - let mut cmd = Command::new(python_exe); - cmd.args(["-c", "import aqt; aqt.run()"]); - cmd.args(args); - - Ok(cmd) -} - -pub fn launch_anki_detached(anki_bin: &std::path::Path, config: &Config) -> Result<()> { - use std::os::windows::process::CommandExt; - use std::process::Stdio; - - const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; - const DETACHED_PROCESS: u32 = 0x00000008; - - let mut cmd = build_python_command(anki_bin, &[], config)?; - cmd.stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .creation_flags(CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS) - .ensure_spawn()?; - Ok(()) -} - -pub fn handle_first_launch(_anki_bin: &std::path::Path) -> Result<()> { - Ok(()) -} - -pub fn exec_anki(anki_bin: &std::path::Path, config: &Config) -> Result<()> { - let args: Vec = std::env::args().skip(1).collect(); - let mut cmd = build_python_command(anki_bin, &args, config)?; - cmd.ensure_success()?; - Ok(()) -} - -pub fn get_exe_and_resources_dirs() -> Result<(PathBuf, PathBuf)> { - let exe_dir = std::env::current_exe() - .context("Failed to get current executable path")? - .parent() - .context("Failed to get executable directory")? - .to_owned(); - - // On Windows, resources dir is the same as exe_dir - let resources_dir = exe_dir.clone(); - - Ok((exe_dir, resources_dir)) -} - -pub fn get_uv_binary_name() -> &'static str { - "uv.exe" } diff --git a/qt/launcher/win/anki-console.bat b/qt/launcher/win/anki-console.bat deleted file mode 100644 index a565fa7b6..000000000 --- a/qt/launcher/win/anki-console.bat +++ /dev/null @@ -1,5 +0,0 @@ -@echo off -"%~dp0"\anki %* -pause - - diff --git a/qt/launcher/win/build.bat b/qt/launcher/win/build.bat index b21831462..da574f210 100644 --- a/qt/launcher/win/build.bat +++ b/qt/launcher/win/build.bat @@ -1,5 +1,10 @@ @echo off -set CODESIGN=1 -REM set NO_COMPRESS=1 +if "%NOCOMP%"=="1" ( + set NO_COMPRESS=1 + set CODESIGN=0 +) else ( + set CODESIGN=1 + set NO_COMPRESS=0 +) cargo run --bin build_win diff --git a/rslib/sync/Cargo.toml b/rslib/sync/Cargo.toml index 7a8f8534a..d23b4f380 100644 --- a/rslib/sync/Cargo.toml +++ b/rslib/sync/Cargo.toml @@ -13,4 +13,9 @@ path = "main.rs" name = "anki-sync-server" [dependencies] + +[target.'cfg(windows)'.dependencies] +anki = { workspace = true, features = ["native-tls"] } + +[target.'cfg(not(windows))'.dependencies] anki = { workspace = true, features = ["rustls"] } diff --git a/tools/update-launcher-env b/tools/update-launcher-env new file mode 100755 index 000000000..c84569f55 --- /dev/null +++ b/tools/update-launcher-env @@ -0,0 +1,15 @@ +#!/bin/bash +# +# Install our latest anki/aqt code into the launcher venv + +set -e + +rm -rf out/wheels +./ninja wheels +if [[ "$OSTYPE" == "darwin"* ]]; then + export VIRTUAL_ENV=$HOME/Library/Application\ Support/AnkiProgramFiles/.venv +else + export VIRTUAL_ENV=$HOME/.local/share/AnkiProgramFiles/.venv +fi +./out/extracted/uv/uv pip install out/wheels/* + diff --git a/tools/update-launcher-env.bat b/tools/update-launcher-env.bat new file mode 100644 index 000000000..9b0b814c6 --- /dev/null +++ b/tools/update-launcher-env.bat @@ -0,0 +1,8 @@ +@echo off +rem +rem Install our latest anki/aqt code into the launcher venv + +rmdir /s /q out\wheels 2>nul +call tools\ninja wheels +set VIRTUAL_ENV=%LOCALAPPDATA%\AnkiProgramFiles\.venv +for %%f in (out\wheels\*.whl) do out\extracted\uv\uv pip install "%%f" \ No newline at end of file From bedab0a54b89819933dd1605fce105d9b60f639b Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 27 Jun 2025 16:07:42 +0700 Subject: [PATCH 038/101] Drop psutil from wheel requirements We're not using it ourselves, and usage appears isolated to a couple of add-ons (notably ankirestart) --- pylib/pyproject.toml | 1 - qt/pyproject.toml | 1 - uv.lock | 19 ------------------- 3 files changed, 21 deletions(-) diff --git a/pylib/pyproject.toml b/pylib/pyproject.toml index 70bb7aba2..23e10077f 100644 --- a/pylib/pyproject.toml +++ b/pylib/pyproject.toml @@ -13,7 +13,6 @@ dependencies = [ "typing_extensions", # platform-specific dependencies "distro; sys_platform != 'darwin' and sys_platform != 'win32'", - "psutil; sys_platform == 'win32'", ] [build-system] diff --git a/qt/pyproject.toml b/qt/pyproject.toml index ab5f50263..101123718 100644 --- a/qt/pyproject.toml +++ b/qt/pyproject.toml @@ -11,7 +11,6 @@ dependencies = [ "requests", "send2trash", "waitress>=2.0.0", - "psutil; sys.platform == 'win32'", "pywin32; sys.platform == 'win32'", "anki-mac-helper; sys.platform == 'darwin'", "pip-system-certs!=5.1", diff --git a/uv.lock b/uv.lock index 62c66e494..868bb55e6 100644 --- a/uv.lock +++ b/uv.lock @@ -56,7 +56,6 @@ dependencies = [ { name = "markdown" }, { name = "orjson" }, { name = "protobuf" }, - { name = "psutil", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "requests", extra = ["socks"] }, { name = "typing-extensions" }, ] @@ -68,7 +67,6 @@ requires-dist = [ { name = "markdown" }, { name = "orjson" }, { name = "protobuf", specifier = ">=4.21" }, - { name = "psutil", marker = "sys_platform == 'win32'" }, { name = "requests", extras = ["socks"] }, { name = "typing-extensions" }, ] @@ -172,7 +170,6 @@ dependencies = [ { name = "jsonschema" }, { name = "pip-system-certs", version = "4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "pip-system-certs", version = "5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, - { name = "psutil", marker = "sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "pyqt6", version = "6.6.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt66' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "pyqt6", version = "6.7.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt67' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt' and extra != 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, { name = "pyqt6", version = "6.8.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-3-aqt-qt' or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt69') or (extra == 'extra-3-aqt-qt67' and extra == 'extra-3-aqt-qt69')" }, @@ -229,7 +226,6 @@ requires-dist = [ { name = "flask-cors" }, { name = "jsonschema" }, { name = "pip-system-certs", specifier = "!=5.1" }, - { name = "psutil", marker = "sys_platform == 'win32'" }, { name = "pyqt6", specifier = ">=6.2" }, { name = "pyqt6", marker = "extra == 'qt'", specifier = "==6.8.0" }, { name = "pyqt6", marker = "extra == 'qt66'", specifier = "==6.6.1" }, @@ -1003,21 +999,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/af/ab3c51ab7507a7325e98ffe691d9495ee3d3aa5f589afad65ec920d39821/protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e", size = 168724, upload-time = "2025-05-28T19:25:53.926Z" }, ] -[[package]] -name = "psutil" -version = "7.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003, upload-time = "2025-02-13T21:54:07.946Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051, upload-time = "2025-02-13T21:54:12.36Z" }, - { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535, upload-time = "2025-02-13T21:54:16.07Z" }, - { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004, upload-time = "2025-02-13T21:54:18.662Z" }, - { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986, upload-time = "2025-02-13T21:54:21.811Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544, upload-time = "2025-02-13T21:54:24.68Z" }, - { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053, upload-time = "2025-02-13T21:54:34.31Z" }, - { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885, upload-time = "2025-02-13T21:54:37.486Z" }, -] - [[package]] name = "pychromedevtools" version = "1.0.4" From ae6cf98f4083e75bb0ee69eee80480ece536225f Mon Sep 17 00:00:00 2001 From: Luc Mcgrady Date: Fri, 27 Jun 2025 10:28:35 +0100 Subject: [PATCH 039/101] Fix/unapplied scss (#4103) * deck options + change notetype * graphs * image occlusion * congrats * imports * ./check * style * $lib * delete unused index.ts files --- .../change-notetype/ChangeNotetypePage.svelte | 2 + .../change-notetype/change-notetype-base.scss | 6 +- ts/routes/change-notetype/index.ts | 41 ----------- ts/routes/congrats/CongratsPage.svelte | 2 + ts/routes/congrats/index.ts | 2 - ts/routes/deck-options/DeckOptionsPage.svelte | 2 + ts/routes/deck-options/index.ts | 65 ----------------- ts/routes/graphs/GraphsPage.svelte | 2 + ts/routes/graphs/index.ts | 72 ------------------- .../image-occlusion/ImageOcclusionPage.svelte | 2 + .../image-occlusion/image-occlusion-base.scss | 8 +-- ts/routes/image-occlusion/index.ts | 59 --------------- .../ImportAnkiPackagePage.svelte | 2 + .../import-anki-package-base.scss | 8 +-- ts/routes/import-anki-package/index.ts | 52 -------------- ts/routes/import-csv/ImportCsvPage.svelte | 2 + ts/routes/import-csv/import-csv-base.scss | 8 +-- ts/routes/import-csv/index.ts | 62 ---------------- ts/routes/import-page/ImportPage.svelte | 2 + ts/routes/import-page/import-page-base.scss | 6 +- ts/routes/import-page/index.ts | 54 -------------- 21 files changed, 34 insertions(+), 425 deletions(-) delete mode 100644 ts/routes/change-notetype/index.ts delete mode 100644 ts/routes/deck-options/index.ts delete mode 100644 ts/routes/graphs/index.ts delete mode 100644 ts/routes/image-occlusion/index.ts delete mode 100644 ts/routes/import-anki-package/index.ts delete mode 100644 ts/routes/import-csv/index.ts delete mode 100644 ts/routes/import-page/index.ts diff --git a/ts/routes/change-notetype/ChangeNotetypePage.svelte b/ts/routes/change-notetype/ChangeNotetypePage.svelte index db25aea2e..e07d088fb 100644 --- a/ts/routes/change-notetype/ChangeNotetypePage.svelte +++ b/ts/routes/change-notetype/ChangeNotetypePage.svelte @@ -3,6 +3,8 @@ Copyright: Ankitects Pty Ltd and contributors License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -->
-
-