mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 05:52:22 -04:00
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
This commit is contained in:
parent
bbf533b172
commit
04996c77f3
197 changed files with 5695 additions and 6648 deletions
|
@ -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"
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
"ftl/usage",
|
||||
"licenses.json",
|
||||
".dmypy.json",
|
||||
"qt/bundle/PyOxidizer",
|
||||
"target",
|
||||
".mypy_cache",
|
||||
"extra",
|
||||
|
|
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -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
|
||||
|
||||
|
|
|
@ -2,4 +2,3 @@
|
|||
py_version=39
|
||||
known_first_party=anki,aqt,tests
|
||||
profile=black
|
||||
extend_skip=qt/bundle
|
||||
|
|
|
@ -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
|
||||
|
|
1
.python-version
Normal file
1
.python-version
Normal file
|
@ -0,0 +1 @@
|
|||
3.13.5
|
|
@ -1,2 +1,2 @@
|
|||
target-version = "py39"
|
||||
extend-exclude = ["qt/bundle"]
|
||||
extend-exclude = []
|
||||
|
|
|
@ -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}"
|
||||
}
|
||||
|
|
1950
Cargo.lock
generated
1950
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
131
Cargo.toml
131
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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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::<Vec<_>>()],
|
||||
);
|
||||
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 {})
|
||||
}
|
44
build/configure/src/launcher.rs
Normal file
44
build/configure/src/launcher.rs
Normal file
|
@ -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",
|
||||
};
|
|
@ -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)?;
|
||||
|
|
|
@ -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<Platform> {
|
||||
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<Platform> {
|
||||
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<Platform> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
],
|
||||
},
|
||||
)?;
|
||||
|
|
|
@ -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<Platform>,
|
||||
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::<u32>().unwrap_or(0).to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.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(())
|
||||
|
|
|
@ -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"
|
||||
];
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
126
build/ninja_gen/src/bin/update_protoc.rs
Normal file
126
build/ninja_gen/src/bin/update_protoc.rs
Normal file
|
@ -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<String, Box<dyn Error>> {
|
||||
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<String, Box<dyn Error>> {
|
||||
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<String, Box<dyn Error>> {
|
||||
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<dyn Error>> {
|
||||
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<dyn Error>> {
|
||||
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(())
|
||||
}
|
144
build/ninja_gen/src/bin/update_uv.rs
Normal file
144
build/ninja_gen/src/bin/update_uv.rs
Normal file
|
@ -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<String, Box<dyn Error>> {
|
||||
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: "<sha256> <filename>"
|
||||
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<String, Box<dyn Error>> {
|
||||
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<String, Box<dyn Error>> {
|
||||
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<dyn Error>> {
|
||||
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<dyn Error>> {
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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<String> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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<P>(path: P) -> String
|
||||
where
|
||||
P: AsRef<str>,
|
||||
{
|
||||
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"))),
|
||||
]));
|
||||
}
|
|
@ -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;
|
|
@ -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(())
|
||||
|
|
|
@ -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('\\', "/")
|
||||
}
|
||||
|
|
|
@ -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<String>,
|
||||
#[arg(long, allow_hyphen_values(true))]
|
||||
venv_args: Vec<String>,
|
||||
#[arg(trailing_var_arg = true)]
|
||||
extra_args: Vec<String>,
|
||||
}
|
||||
|
||||
/// 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");
|
||||
}
|
||||
|
|
|
@ -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"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
|
||||
|
|
|
@ -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**:
|
||||
|
||||
|
|
|
@ -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))
|
|
@ -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)
|
|
@ -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)
|
2
ninja
2
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."
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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]:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -174,7 +174,7 @@ from revlog where type != {REVLOG_RESCHED} and id > ? """
|
|||
cards=cards, seconds=float(thetime)
|
||||
)
|
||||
# again/pass count
|
||||
b += "<br>" + "Again count: %s" % bold(failed)
|
||||
b += "<br>" + "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 += "<br>"
|
||||
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(
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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 = (
|
||||
|
|
42
pylib/hatch_build.py
Normal file
42
pylib/hatch_build.py
Normal file
|
@ -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)
|
35
pylib/pyproject.toml
Normal file
35
pylib/pyproject.toml
Normal file
|
@ -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"
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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"
|
||||
}
|
||||
]
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -1,2 +0,0 @@
|
|||
pip-tools
|
||||
colorama # required on windows
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -1,3 +0,0 @@
|
|||
pyqt5==5.14.1
|
||||
pyqtwebengine==5.14.0
|
||||
pyqt5_sip==12.8.1
|
|
@ -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
|
|
@ -1,3 +0,0 @@
|
|||
pyqt5==5.15.5
|
||||
pyqtwebengine==5.15.5
|
||||
pyqt5_sip==12.9.0
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -1,2 +0,0 @@
|
|||
pywin32
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
@ -1 +0,0 @@
|
|||
..\out\pyenv\scripts\pip-compile --resolver=backtracking --allow-unsafe --no-header --strip-extras --generate-hashes requirements.win.in
|
10
python/version.py
Normal file
10
python/version.py
Normal file
|
@ -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()
|
|
@ -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,
|
||||
)
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -165,6 +165,7 @@ _mbox: QMessageBox | None = None
|
|||
|
||||
class ErrorHandler(QObject):
|
||||
"Catch stderr and write into buffer."
|
||||
|
||||
ivl = 100
|
||||
fatal_error_encountered = False
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
629
qt/bundle/Cargo.lock
generated
629
qt/bundle/Cargo.lock
generated
|
@ -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",
|
||||
]
|
|
@ -1,60 +0,0 @@
|
|||
[package]
|
||||
name = "anki"
|
||||
version = "0.0.0"
|
||||
authors = ["Ankitects Pty Ltd and contributors <https://help.ankiweb.net>"]
|
||||
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
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 12a249f686484c5e212ba800e1e7f18c7c4b1b27
|
|
@ -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<String>, 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");
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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(())
|
||||
}
|
|
@ -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<Utf8PathBuf>,
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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(())
|
||||
}
|
|
@ -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()
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue