From e3d11448f2a5d44be9d56bfd5aaf494496d2a549 Mon Sep 17 00:00:00 2001
From: Nadzeya H <84857215+nadzyah@users.noreply.github.com>
Date: Tue, 13 Aug 2024 20:09:08 +0300
Subject: [PATCH] Create client minimal implementation (#181)
---
.github/workflows/test_client.yaml | 3 +-
.../workflows/test_hwlib_debian_build.yaml | 6 +
Cargo.lock | 426 +++++++----
README.md | 156 ++--
client/README.md | 14 +-
client/hwctl/Cargo.toml | 4 +-
client/hwctl/src/main.rs | 44 +-
client/hwlib/Cargo.toml | 21 +-
client/hwlib/debian/rules | 2 +-
client/hwlib/pytests/test_cert_status.py | 13 +-
client/hwlib/src/collectors/cpuid.rs | 141 ++++
client/hwlib/src/collectors/cpuinfo.rs | 199 ++++++
client/hwlib/src/collectors/hardware_info.rs | 326 +++++++++
client/hwlib/src/collectors/mod.rs | 24 +
client/hwlib/src/collectors/os_info.rs | 114 +++
client/hwlib/src/constants.rs | 26 +
client/hwlib/src/helpers.rs | 58 ++
client/hwlib/src/lib.rs | 118 +--
client/hwlib/src/models/devices.rs | 54 +-
client/hwlib/src/models/mod.rs | 3 +-
client/hwlib/src/models/rbody.rs | 58 --
client/hwlib/src/models/request_validators.rs | 167 +++++
.../hwlib/src/models/response_validators.rs | 66 ++
client/hwlib/src/models/software.rs | 13 +-
client/hwlib/src/py_bindings.rs | 37 +-
client/hwlib/tests/test_cert_status.rs | 30 -
client/hwlib/tests/test_data/DMI | Bin 0 -> 5821 bytes
client/hwlib/tests/test_data/cpuinfo | 56 ++
client/hwlib/tests/test_data/cpuinfo_max_freq | 1 +
.../tests/test_data/device-tree/compatible | 1 +
.../hwlib/tests/test_data/device-tree/model | 1 +
.../hwlib/tests/test_data/smbios_entry_point | Bin 0 -> 24 bytes
client/hwlib/tests/test_data/version | 1 +
client/hwlib/tox.ini | 1 -
server/hwapi/data_models/repository.py | 13 +-
server/hwapi/external/c3/client.py | 7 +-
server/poetry.lock | 674 +++++++++---------
37 files changed, 2100 insertions(+), 778 deletions(-)
create mode 100644 client/hwlib/src/collectors/cpuid.rs
create mode 100644 client/hwlib/src/collectors/cpuinfo.rs
create mode 100644 client/hwlib/src/collectors/hardware_info.rs
create mode 100644 client/hwlib/src/collectors/mod.rs
create mode 100644 client/hwlib/src/collectors/os_info.rs
create mode 100644 client/hwlib/src/constants.rs
create mode 100644 client/hwlib/src/helpers.rs
delete mode 100644 client/hwlib/src/models/rbody.rs
create mode 100644 client/hwlib/src/models/request_validators.rs
create mode 100644 client/hwlib/src/models/response_validators.rs
delete mode 100644 client/hwlib/tests/test_cert_status.rs
create mode 100644 client/hwlib/tests/test_data/DMI
create mode 100644 client/hwlib/tests/test_data/cpuinfo
create mode 100644 client/hwlib/tests/test_data/cpuinfo_max_freq
create mode 100644 client/hwlib/tests/test_data/device-tree/compatible
create mode 100644 client/hwlib/tests/test_data/device-tree/model
create mode 100644 client/hwlib/tests/test_data/smbios_entry_point
create mode 100644 client/hwlib/tests/test_data/version
diff --git a/.github/workflows/test_client.yaml b/.github/workflows/test_client.yaml
index 10947490..c2c2ce4d 100644
--- a/.github/workflows/test_client.yaml
+++ b/.github/workflows/test_client.yaml
@@ -49,8 +49,9 @@ jobs:
- run: |
rustup component add clippy
cargo clippy --all-features --verbose
+ - run: cargo clippy --tests --verbose
- name: Run cargo test
- run: cargo test -- --test-threads=1
+ run: cargo test
py-bindings-test:
runs-on: [self-hosted, linux, large, jammy, x64]
diff --git a/.github/workflows/test_hwlib_debian_build.yaml b/.github/workflows/test_hwlib_debian_build.yaml
index 76151880..c38088bb 100644
--- a/.github/workflows/test_hwlib_debian_build.yaml
+++ b/.github/workflows/test_hwlib_debian_build.yaml
@@ -26,6 +26,12 @@ jobs:
steps:
- name: Checkout the repository
uses: actions/checkout@v4
+ # Prevent lxd-agent from getting restarted on apt upgrades.
+ # https://warthogs.atlassian.net/browse/ISD-2139
+ - name: Disable lxd-agent restart
+ run: |
+ mkdir -p /etc/needrestart/conf.d
+ echo '$nrconf{override_rc}{qr(^lxd-agent.service$)} = 0;' | sudo tee /etc/needrestart/conf.d/lxdagent.conf
- name: Download dependencies
run: |
set -xeu
diff --git a/Cargo.lock b/Cargo.lock
index e4140027..1d9ca747 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -17,6 +17,21 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
+
[[package]]
name = "atomic-waker"
version = "1.1.2"
@@ -70,15 +85,15 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "bytes"
-version = "1.6.0"
+version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
+checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
[[package]]
name = "cc"
-version = "1.1.1"
+version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "907d8581360765417f8f2e0e7d602733bbed60156b4465b7617243689ef9b83d"
+checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292"
[[package]]
name = "cfg-if"
@@ -86,21 +101,52 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+[[package]]
+name = "core-foundation"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d"
+dependencies = [
+ "core-foundation-sys 0.6.2",
+ "libc",
+]
+
[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
- "core-foundation-sys",
+ "core-foundation-sys 0.8.7",
"libc",
]
[[package]]
name = "core-foundation-sys"
-version = "0.8.6"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "encoding_rs"
@@ -202,6 +248,15 @@ dependencies = [
"pin-utils",
]
+[[package]]
+name = "getopts"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
+dependencies = [
+ "unicode-width",
+]
+
[[package]]
name = "getrandom"
version = "0.2.15"
@@ -269,9 +324,9 @@ dependencies = [
[[package]]
name = "http-body"
-version = "1.0.0"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
"bytes",
"http",
@@ -300,7 +355,9 @@ checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
name = "hwctl"
version = "0.1.0"
dependencies = [
+ "anyhow",
"hwlib",
+ "serde_json",
"tokio",
]
@@ -308,11 +365,16 @@ dependencies = [
name = "hwlib"
version = "0.1.0"
dependencies = [
- "once_cell",
+ "anyhow",
+ "itertools",
+ "lazy_static",
"pyo3",
+ "regex",
"reqwest",
"serde",
"serde_json",
+ "smbios-lib",
+ "time",
"tokio",
]
@@ -371,9 +433,9 @@ dependencies = [
[[package]]
name = "hyper-util"
-version = "0.1.6"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956"
+checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
dependencies = [
"bytes",
"futures-channel",
@@ -401,9 +463,9 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.2.6"
+version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
+checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
dependencies = [
"equivalent",
"hashbrown",
@@ -415,12 +477,31 @@ version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
+[[package]]
+name = "io-kit-sys"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f21dcc74995dd4cd090b147e79789f8d65959cbfb5f0b118002db869ea3bd0a0"
+dependencies = [
+ "core-foundation-sys 0.6.2",
+ "mach 0.2.3",
+]
+
[[package]]
name = "ipnet"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
+[[package]]
+name = "itertools"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+dependencies = [
+ "either",
+]
+
[[package]]
name = "itoa"
version = "1.0.11"
@@ -429,13 +510,19 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "js-sys"
-version = "0.3.69"
+version = "0.3.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
[[package]]
name = "libc"
version = "0.2.155"
@@ -449,20 +536,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
-name = "lock_api"
-version = "0.4.12"
+name = "log"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+
+[[package]]
+name = "mach"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1"
dependencies = [
- "autocfg",
- "scopeguard",
+ "libc",
]
[[package]]
-name = "log"
-version = "0.4.22"
+name = "mach"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
+dependencies = [
+ "libc",
+]
[[package]]
name = "memchr"
@@ -496,13 +591,14 @@ dependencies = [
[[package]]
name = "mio"
-version = "0.8.11"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
+checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
dependencies = [
+ "hermit-abi",
"libc",
"wasi",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -523,20 +619,16 @@ dependencies = [
]
[[package]]
-name = "num_cpus"
-version = "1.16.0"
+name = "num-conv"
+version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
-dependencies = [
- "hermit-abi",
- "libc",
-]
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "object"
-version = "0.36.1"
+version = "0.36.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce"
+checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9"
dependencies = [
"memchr",
]
@@ -591,29 +683,6 @@ dependencies = [
"vcpkg",
]
-[[package]]
-name = "parking_lot"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
-dependencies = [
- "lock_api",
- "parking_lot_core",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.9.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
-dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall",
- "smallvec",
- "windows-targets 0.52.6",
-]
-
[[package]]
name = "percent-encoding"
version = "2.3.1"
@@ -660,9 +729,15 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "portable-atomic"
-version = "1.6.0"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
+checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "proc-macro2"
@@ -675,9 +750,9 @@ dependencies = [
[[package]]
name = "pyo3"
-version = "0.22.1"
+version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e99090d12f6182924499253aaa1e73bf15c69cea8d2774c3c781e35badc3548"
+checksum = "831e8e819a138c36e212f3af3fd9eeffed6bf1510a805af35b0edee5ffa59433"
dependencies = [
"cfg-if",
"indoc",
@@ -693,9 +768,9 @@ dependencies = [
[[package]]
name = "pyo3-build-config"
-version = "0.22.1"
+version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7879eb018ac754bba32cb0eec7526391c02c14a093121857ed09fbf1d1057d41"
+checksum = "1e8730e591b14492a8945cdff32f089250b05f5accecf74aeddf9e8272ce1fa8"
dependencies = [
"once_cell",
"target-lexicon",
@@ -703,9 +778,9 @@ dependencies = [
[[package]]
name = "pyo3-ffi"
-version = "0.22.1"
+version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce2baa5559a411fc1cf519295f24c34b53d5d725818bc96b5abf94762da09041"
+checksum = "5e97e919d2df92eb88ca80a037969f44e5e70356559654962cbb3316d00300c6"
dependencies = [
"libc",
"pyo3-build-config",
@@ -713,9 +788,9 @@ dependencies = [
[[package]]
name = "pyo3-macros"
-version = "0.22.1"
+version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "049621c20a23f2def20f4fe67978d1da8d8a883d64b9c21362f3b776e254edc7"
+checksum = "eb57983022ad41f9e683a599f2fd13c3664d7063a3ac5714cae4b7bee7d3f206"
dependencies = [
"proc-macro2",
"pyo3-macros-backend",
@@ -725,9 +800,9 @@ dependencies = [
[[package]]
name = "pyo3-macros-backend"
-version = "0.22.1"
+version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e969ee2e025435f1819d31a275ba4bb9cbbdf3ac535227fdbd85b9322ffe144"
+checksum = "ec480c0c51ddec81019531705acac51bcdbeae563557c982aa8263bb96880372"
dependencies = [
"heck",
"proc-macro2",
@@ -746,14 +821,34 @@ dependencies = [
]
[[package]]
-name = "redox_syscall"
-version = "0.5.2"
+name = "regex"
+version = "1.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
dependencies = [
- "bitflags 2.6.0",
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
]
+[[package]]
+name = "regex-automata"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+
[[package]]
name = "reqwest"
version = "0.12.5"
@@ -833,9 +928,9 @@ dependencies = [
[[package]]
name = "rustls"
-version = "0.23.11"
+version = "0.23.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4828ea528154ae444e5a642dbb7d5623354030dc9822b83fd9bb79683c7399d0"
+checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
dependencies = [
"once_cell",
"rustls-pki-types",
@@ -846,9 +941,9 @@ dependencies = [
[[package]]
name = "rustls-pemfile"
-version = "2.1.2"
+version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
+checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
dependencies = [
"base64",
"rustls-pki-types",
@@ -856,15 +951,15 @@ dependencies = [
[[package]]
name = "rustls-pki-types"
-version = "1.7.0"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
+checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
[[package]]
name = "rustls-webpki"
-version = "0.102.5"
+version = "0.102.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78"
+checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e"
dependencies = [
"ring",
"rustls-pki-types",
@@ -886,49 +981,43 @@ dependencies = [
"windows-sys 0.52.0",
]
-[[package]]
-name = "scopeguard"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
-
[[package]]
name = "security-framework"
-version = "2.11.0"
+version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0"
+checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags 2.6.0",
- "core-foundation",
- "core-foundation-sys",
+ "core-foundation 0.9.4",
+ "core-foundation-sys 0.8.7",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
-version = "2.11.0"
+version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7"
+checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
dependencies = [
- "core-foundation-sys",
+ "core-foundation-sys 0.8.7",
"libc",
]
[[package]]
name = "serde"
-version = "1.0.204"
+version = "1.0.207"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
+checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.204"
+version = "1.0.207"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
+checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e"
dependencies = [
"proc-macro2",
"quote",
@@ -937,11 +1026,12 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.120"
+version = "1.0.124"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
+checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d"
dependencies = [
"itoa",
+ "memchr",
"ryu",
"serde",
]
@@ -958,15 +1048,6 @@ dependencies = [
"serde",
]
-[[package]]
-name = "signal-hook-registry"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
-dependencies = [
- "libc",
-]
-
[[package]]
name = "slab"
version = "0.4.9"
@@ -982,6 +1063,22 @@ version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+[[package]]
+name = "smbios-lib"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7585696daa0d95717d4ff4dc78a5d2e4e8091d85bafe92f28397de5190d7b00d"
+dependencies = [
+ "core-foundation 0.6.4",
+ "core-foundation-sys 0.6.2",
+ "getopts",
+ "io-kit-sys",
+ "libc",
+ "mach 0.3.2",
+ "serde",
+ "serde_json",
+]
+
[[package]]
name = "socket2"
version = "0.5.7"
@@ -1006,9 +1103,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
-version = "2.0.70"
+version = "2.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16"
+checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7"
dependencies = [
"proc-macro2",
"quote",
@@ -1028,7 +1125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
dependencies = [
"bitflags 1.3.2",
- "core-foundation",
+ "core-foundation 0.9.4",
"system-configuration-sys",
]
@@ -1038,26 +1135,58 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
dependencies = [
- "core-foundation-sys",
+ "core-foundation-sys 0.8.7",
"libc",
]
[[package]]
name = "target-lexicon"
-version = "0.12.15"
+version = "0.12.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4873307b7c257eddcb50c9bedf158eb669578359fb28428bef438fec8e6ba7c2"
+checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "tempfile"
-version = "3.10.1"
+version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
dependencies = [
"cfg-if",
"fastrand",
+ "once_cell",
"rustix",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "time"
+version = "0.3.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
+dependencies = [
+ "deranged",
+ "itoa",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
+name = "time-macros"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
+dependencies = [
+ "num-conv",
+ "time-core",
]
[[package]]
@@ -1077,28 +1206,25 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
-version = "1.38.0"
+version = "1.39.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
+checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
- "num_cpus",
- "parking_lot",
"pin-project-lite",
- "signal-hook-registry",
"socket2",
"tokio-macros",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
name = "tokio-macros"
-version = "2.3.0"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
+checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
@@ -1212,6 +1338,12 @@ dependencies = [
"tinyvec",
]
+[[package]]
+name = "unicode-width"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
+
[[package]]
name = "unindent"
version = "0.2.3"
@@ -1258,19 +1390,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
-version = "0.2.92"
+version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
dependencies = [
"cfg-if",
+ "once_cell",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.92"
+version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
dependencies = [
"bumpalo",
"log",
@@ -1283,9 +1416,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
-version = "0.4.42"
+version = "0.4.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
+checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed"
dependencies = [
"cfg-if",
"js-sys",
@@ -1295,9 +1428,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.92"
+version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1305,9 +1438,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.92"
+version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
dependencies = [
"proc-macro2",
"quote",
@@ -1318,15 +1451,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.92"
+version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
[[package]]
name = "web-sys"
-version = "0.3.69"
+version = "0.3.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
+checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -1350,6 +1483,15 @@ dependencies = [
"windows-targets 0.52.6",
]
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
[[package]]
name = "windows-targets"
version = "0.48.5"
diff --git a/README.md b/README.md
index 9b234247..83b758f8 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,3 @@
-
# Hardware Information API (hwapi)
The repo contains the API server and client for retrieving hardware information.
@@ -41,14 +40,6 @@ export IMPORT_TOOL_PATH=./scripts/seed_db.py
docker compose up --attach-dependencies --build hwapi-dev
```
-To verify that it works, you can make the following request from the host
-(you should receive the Certified response):
-
-```bash
-curl http://0.0.0.0:8080/v1/certification/status -X POST -H "Content-Type: application/json" \
--d '{"vendor": "Dell", "model": "ChengMing 3980"}' -s | python3 -m json.tool
-```
-
### Load the data from C3
This approach populates the DB with the data from C3 (staging instance by default).
@@ -61,13 +52,6 @@ To build and run the container with staging data, execute the following command:
docker compose up --attach-dependencies --build hwapi-dev
```
-To verify that it works, make the following request from the host (you should receive the Certified response):
-
-```bash
-curl http://0.0.0.0:8080/v1/certification/status -X POST -H "Content-Type: application/json" \
--d '{"vendor": "HP", "model": "Z8 G4 Workstation"}' -s | python3 -m json.tool
-```
-
Alternatively, you can specify another C3 host (like production or the local one) by specifying the `C3_URL`:
```bash
@@ -92,15 +76,9 @@ You can retrieve API schema in HTML, YAML, and JSON formats:
## Build the library (`hwlib`)
-For now, the library contains the function to return a sample certification
-status. It depends on the environment variable `CERTIFICATION_STATUS` and
-accepts the following values:
-
-- `0`: The system has not been seen (default behaviour even if the env variable
- is not defined).
-- `1`: The system is partially certified (we haven't seen this specific system,
- but some of its hardware components have been tested on other systems).
-- `2`: This system has been certified (but probably for other Ubuntu release).
+Currently, the library contains the functionality to collect the information about
+the machine hardware and OS, send the request to the server, and get the machine
+certification status.
```bash
cd client/hwlib
@@ -109,40 +87,112 @@ cargo build
## Build and run the reference CLI tool (`hwctl`)
+To check the machine certification status, run the following command. It sends the request
+to the server URL defined by `HW_API_URL` environment value (https://hw.ubuntu.com
+by default). The library requires root access
+since we collect the hardware information using SMBIOS data. If you're running it
+on a device that doesn't have SMBIOS data available, root privileges are not required.
+
```bash
-export CERTIFICATION_STATUS=2
-cd client/hwctl
-cargo run
+cargo build
+sudo ./target/debug/hwctl
```
-This is the output you should get running the commands above:
+To send the request to a different server, run the tool the following way:
```bash
- Finished dev [unoptimized + debuginfo] target(s) in 2.00s
-Object {
- "bios": Object {
- "firmware_revision": String("1.0"),
- "release_date": String("2020-01-01"),
- "revision": String("rev1"),
- "vendor": String("BIOSVendor"),
- "version": String("v1.0"),
- },
- "os": Object {
- "codename": String("focal"),
- "description": String("Ubuntu 20.04.1 LTS"),
- "distributor": String("Ubuntu"),
- "kernel": Object {
- "name": String("Linux"),
- "signature": String("Sample Signature"),
- "version": String("5.4.0-42-generic"),
- },
- "loaded_modules": Array [
- String("module1"),
- String("module2"),
- ],
- "version": String("20.04"),
+cargo build
+sudo HW_API_URL=https://your.server.url ./target/debug/hwctl
+```
+
+This is the output should look a similar way:
+
+```
+Request:
+{
+ "architecture": "amd64",
+ "bios": {
+ "firmware_revision": "1.13",
+ "release_date": "2023-03-14",
+ "revision": "1.13.0",
+ "vendor": "Dell Inc.",
+ "version": "1.13.0"
+ },
+ "board": {
+ "manufacturer": "Dell Inc.",
+ "product_name": "0F0W8W",
+ "version": "A00"
+ },
+ "chassis": {
+ "chassis_type": "Notebook",
+ "manufacturer": "Dell Inc.",
+ "sku": "Notebook",
+ "version": ""
+ },
+ "model": "Inspiron 14 5420",
+ "os": {
+ "codename": "noble",
+ "distributor": "Ubuntu",
+ "kernel": {
+ "loaded_modules": [
+ "tls",
+ "nft_masq",
+ "truncated for this example",
+ ],
+ "name": "Linux",
+ "signature": null,
+ "version": "6.8.0-38-generic"
},
- "status": String("Certified"),
+ "version": "24.04"
+ },
+ "pci_peripherals": [],
+ "processor": {
+ "codename": "Unknown",
+ "frequency": 4400,
+ "manufacturer": "Intel(R) Corporation",
+ "version": "12th Gen Intel(R) Core(TM) i5-1235U"
+ },
+ "usb_peripherals": [],
+ "vendor": "Dell Inc."
+}
+
+Response:
+{
+ "architecture": "amd64",
+ "audio": null,
+ "available_releases": [
+ {
+ "codename": "focal",
+ "distributor": "Ubuntu",
+ "kernel": {
+ "loaded_modules": [],
+ "name": null,
+ "signature": null,
+ "version": "5.14.0-1010-oem"
+ },
+ "version": "20.04 LTS"
+ }
+ ],
+ "bios": {
+ "firmware_revision": "1.13",
+ "release_date": null,
+ "revision": null,
+ "vendor": "Dell",
+ "version": "1.13.0"
+ },
+ "board": {
+ "manufacturer": "Dell",
+ "product_name": "0F0W8W",
+ "version": "A00"
+ },
+ "chassis": null,
+ "gpu": null,
+ "network": null,
+ "pci_peripherals": [],
+ "status": "Related Certified System Exists",
+ "usb_peripherals": [],
+ "video": null,
+ "wireless": null
}
```
diff --git a/client/README.md b/client/README.md
index 37bc2f22..d4ae7388 100644
--- a/client/README.md
+++ b/client/README.md
@@ -57,16 +57,14 @@ $ cd hwlib
$ maturin develop
```
-Now you can use the lib in your Python code:
+Now you can use the lib in your Python code. The library requires root access
+since we collect the hardware information using SMBIOS data. If you're running it
+on a device that doesn't have SMBIOS data available, root privileges are not required.
```python
+$ sudo /path/to/venv/bin/python3 # or `python3`
>>> import hwlib
->>> hwlib.get_certification_status("https://example.com")
-{'status':'Not Seen'}
->>> import os
->>> os.environ["CERTIFICATION_STATUS"] = "2"
->>> hwlib.get_certification_status("https://example.com")
-{'bios': {'firmware_revision': '1.0', 'release_date': '2020-01-01', 'revision': 'rev1', 'vendor': 'BIOSVendor', 'version': 'v1.0'}, 'os': {'codename': 'focal', 'description': 'Ubuntu 20.04.1 LTS', 'distributor': 'Ubuntu', 'kernel': {'name': 'Linux', 'signature': 'Sample Signature', 'version': '5.4.0-42-generic'}, 'loaded_modules': ['module1', 'module2'], 'version': '20.04'}, 'status': 'Certified'}
+>>> hwlib.get_certification_status("https://hw.ubuntu.com")
```
@@ -75,7 +73,7 @@ Now you can use the lib in your Python code:
Since we're using python bindings, this library contains tests for both Rust and Python code.
To execute them, run the following commands in the `hwlib/` directory:
-* Run Rust tests: `$ cargo test -- --test-threads=1`
+* Run Rust tests: `$ cargo test`
* For Python tests, you need to have `tox` on your system installed: `pip install tox`.
Then, you can run Python tests with tox `$ tox`
diff --git a/client/hwctl/Cargo.toml b/client/hwctl/Cargo.toml
index 1781b928..c327e982 100644
--- a/client/hwctl/Cargo.toml
+++ b/client/hwctl/Cargo.toml
@@ -11,5 +11,7 @@ tier = "2"
all-features = false
[target.'cfg(target_os = "linux")'.dependencies]
-tokio = { version = "1.38.0", features = ["full"], default-features = false }
+tokio = { version = "1.38.0", features = ["rt", "macros"], default-features = false }
hwlib = { path = "../hwlib", version = "0.1.0" }
+serde_json = "~1.0.0"
+anyhow = "~1.0.0"
diff --git a/client/hwctl/src/main.rs b/client/hwctl/src/main.rs
index b75a6ca0..2c29c795 100644
--- a/client/hwctl/src/main.rs
+++ b/client/hwctl/src/main.rs
@@ -1,19 +1,49 @@
-use hwlib::get_certification_status;
-use std::env;
+/* Copyright 2024 Canonical Ltd.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Written by:
+ * Canonical Ltd
+ * Nadzeya Hutsko
+ */
+
+use anyhow::Result;
use std::process::exit;
+use hwlib::{
+ models::request_validators::{CertificationStatusRequest, Paths},
+ send_certification_status_request,
+};
+
#[tokio::main]
-async fn main() {
- let url = env::var("HW_API_URL").unwrap_or_else(|_| String::from("https://hw.ubuntu.com"));
- let response = get_certification_status(&url).await;
+async fn main() -> Result<()> {
+ let cert_status_request = CertificationStatusRequest::new(Paths::default())?;
+ println!(
+ "Request:\n{}",
+ serde_json::to_string_pretty(&cert_status_request)?
+ );
+ let url = std::env::var("HW_API_URL").unwrap_or_else(|_| String::from("https://hw.ubuntu.com"));
+ let response = send_certification_status_request(url, &cert_status_request).await;
match response {
Ok(response) => {
- println!("{:#?}", response);
+ println!("\nResponse:\n{}", serde_json::to_string_pretty(&response)?);
exit(0);
}
Err(e) => {
- eprintln!("ERROR: {}", e);
+ eprintln!("{}", e);
exit(1);
}
}
diff --git a/client/hwlib/Cargo.toml b/client/hwlib/Cargo.toml
index a8d8c2ff..a3a4a828 100644
--- a/client/hwlib/Cargo.toml
+++ b/client/hwlib/Cargo.toml
@@ -4,7 +4,11 @@ description = "collects and sends hardware information to hwapi to find testing
license = "GPL-3.0-only"
version = "0.1.0"
edition = "2021"
-authors = []
+authors = [
+ "Canonical Hardware Certification ",
+ "Nadzeya Hutsko ",
+ "Matias Piipari ",
+]
documentation = "https://github.com/canonical/hardware-api/"
homepage = "https://github.com/canonical/hardware-api/"
repository = "https://github.com/canonical/hardware-api/"
@@ -21,11 +25,16 @@ crate-type = [ "cdylib", "rlib",]
[target.'cfg(all(target_os = "linux"))'.dependencies]
serde_json = "~1.0.0"
-once_cell = "1.19.0"
-reqwest = { version = "0.12.0", features = [ "json",] }
-serde = { version = "~1.0.0", features = [ "derive",] }
-tokio = { version = "1.38.0", features = [ "full", "test-util",], default-features = false }
-pyo3 = { version = "0.22.0", features = [ "extension-module",] }
+reqwest = { version = "0.12.0", features = ["json"] }
+serde = { version = "~1.0.0", features = ["derive"] }
+tokio = { version = "1.38.0", features = ["rt", "rt-multi-thread", "test-util"], default-features = false }
+pyo3 = { version = "0.22.0", features = ["extension-module"] }
+smbios-lib = "0.9.0"
+regex = "1.10.0"
+time = { version = "~0.3.0", features = ["macros" ,"parsing", "formatting"]}
+anyhow = "~1.0.0"
+itertools = "0.13.0"
+lazy_static = "1.5.0"
[package.metadata.deb]
maintainer = "Canonical Hardware Certification "
diff --git a/client/hwlib/debian/rules b/client/hwlib/debian/rules
index ee4fe3f5..89753eb1 100755
--- a/client/hwlib/debian/rules
+++ b/client/hwlib/debian/rules
@@ -6,4 +6,4 @@ export CARGO_HOME=$(CURDIR)/debian/
dh $@ --buildsystem cargo
override_dh_auto_test:
- /usr/share/cargo/bin/cargo test -- --test-threads=1
+ /usr/share/cargo/bin/cargo test --offline
diff --git a/client/hwlib/pytests/test_cert_status.py b/client/hwlib/pytests/test_cert_status.py
index 71e6f3c2..03b9bbdc 100644
--- a/client/hwlib/pytests/test_cert_status.py
+++ b/client/hwlib/pytests/test_cert_status.py
@@ -18,12 +18,9 @@
# Nadzeya Hutsko
-from hwlib import get_certification_status
+def test_send_certification_request():
+ """Verify that we can import and call the library in the python code"""
+ import hwlib
-
-def test_get_certification_status():
- """Verify that we can use the library in the python code"""
- url = "http://example.com"
-
- result = get_certification_status(url)
- assert isinstance(result, dict)
+ assert hasattr(hwlib, "send_certification_request")
+ assert callable(hwlib.send_certification_request)
diff --git a/client/hwlib/src/collectors/cpuid.rs b/client/hwlib/src/collectors/cpuid.rs
new file mode 100644
index 00000000..109c45e3
--- /dev/null
+++ b/client/hwlib/src/collectors/cpuid.rs
@@ -0,0 +1,141 @@
+/* Copyright 2024 Canonical Ltd.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+use anyhow::{anyhow, Result};
+use smbioslib;
+use std::fmt::{self, Display};
+
+#[derive(Clone, Debug)]
+pub struct CpuId([u8; 8]);
+
+impl CpuId {
+ pub fn from_smbios(proc_info: &smbioslib::SMBiosProcessorInformation) -> Result {
+ let cpu_id = proc_info
+ .processor_id()
+ .ok_or_else(|| anyhow!("processor ID not found"))?;
+ Ok(Self(*cpu_id))
+ }
+
+ pub fn codename(&self) -> Option {
+ if self.0.is_empty() {
+ return None;
+ }
+ Some(self.to_human_friendly().unwrap_or("Unknown").to_string())
+ }
+
+ /// Implement the same logic as used in checkbox to get CPU codename
+ /// https://github.com/canonical/checkbox/blob/904692d/providers/base/bin/cpuid.py
+ fn to_human_friendly(&self) -> Option<&'static str> {
+ const KNOWN_CPU_IDS: &[(&str, &[&str])] = &[
+ ("Amber Lake", &["0x806e9"]),
+ ("AMD EPYC", &["0x800f12"]),
+ ("AMD Genoa", &["0xa10f11"]),
+ ("AMD Lisbon", &["0x100f81"]),
+ ("AMD Magny-Cours", &["0x100f91"]),
+ ("AMD Milan", &["0xa00f11"]),
+ ("AMD Milan-X", &["0xa00f12"]),
+ ("AMD ROME", &["0x830f10"]),
+ ("AMD Ryzen", &["0x810f81"]),
+ ("AMD Bergamo", &["0xaa0f01"]),
+ ("AMD Siena SP6", &["0xaa0f02"]),
+ ("AMD Raphael", &["0xa60f12"]),
+ ("Broadwell", &["0x4067", "0x306d4", "0x5066", "0x406f"]),
+ ("Canon Lake", &["0x6066"]),
+ ("Cascade Lake", &["0x50655", "0x50656", "0x50657"]),
+ (
+ "Coffee Lake",
+ &["0x806ea", "0x906ea", "0x906eb", "0x906ec", "0x906ed"],
+ ),
+ ("Comet Lake", &["0x806ec", "0xa065"]),
+ ("Cooper Lake", &["0x5065a", "0x5065b"]),
+ ("Emerald Rapids", &["0xc06f2"]),
+ ("Haswell", &["0x306c", "0x4065", "0x4066", "0x306f"]),
+ ("Hygon Dhyana Plus", &["0x900f22"]),
+ ("Ice Lake", &["0x606e6", "0x606a6", "0x706e6", "0x606c1"]),
+ ("Ivy Bridge", &["0x306a", "0x306e"]),
+ ("Kaby Lake", &["0x806e9", "0x906e9"]),
+ ("Knights Landing", &["0x5067"]),
+ ("Knights Mill", &["0x8065"]),
+ ("Nehalem", &["0x106a", "0x106e5", "0x206e"]),
+ ("Pineview", &["0x106ca"]),
+ ("Penryn", &["0x1067a"]),
+ ("Raptor Lake", &["0xb0671", "0xb06f2", "0xb06f5", "0xb06a2"]),
+ ("Rocket Lake", &["0xa0671"]),
+ ("Sandy Bridge", &["0x206a", "0x206d6", "0x206d7"]),
+ (
+ "Sapphire Rapids",
+ &["0x806f3", "0x806f6", "0x806f7", "0x806f8"],
+ ),
+ ("Skylake", &["0x406e3", "0x506e3", "0x50654", "0x50652"]),
+ ("Tiger Lake", &["0x806c1"]),
+ ("Alder Lake", &["0x906a4", "0x906A3", "0x90675", "0x90672"]),
+ ("Westmere", &["0x2065", "0x206c", "0x206f"]),
+ ("Whisky Lake", &["0x806eb", "0x806ec"]),
+ ];
+ let cpu_id_hex = format!("0x{:x}{:02x}{:02x}", self.0[2], self.0[1], self.0[0]);
+ KNOWN_CPU_IDS.iter().find_map(|(name, ids)| {
+ ids.iter()
+ .find(|&&id| cpu_id_hex.contains(id))
+ .map(|_| *name)
+ })
+ }
+}
+
+impl Display for CpuId {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.codename().unwrap_or_default())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_cpu_id_codename() {
+ assert_eq!(
+ CpuId([0xe9, 0x06, 0x08, 0x00, 0xff, 0xfb, 0xeb, 0xbf])
+ .codename()
+ .unwrap(),
+ "Amber Lake"
+ );
+ assert_eq!(
+ CpuId([0x11, 0x0f, 0xa1, 0x00, 0xff, 0xfb, 0x8b, 0x17])
+ .codename()
+ .unwrap(),
+ "AMD Genoa"
+ );
+ assert_eq!(
+ CpuId([0x71, 0x06, 0x05, 0x00, 0xff, 0xfb, 0xeb, 0xbf])
+ .codename()
+ .unwrap(),
+ "Knights Landing"
+ );
+ assert_eq!(
+ CpuId([0x71, 0x06, 0x0b, 0x00, 0xff, 0xfb, 0xeb, 0xbf])
+ .codename()
+ .unwrap(),
+ "Raptor Lake"
+ );
+ }
+
+ #[test]
+ fn test_unknown_cpuids() {
+ assert_eq!(CpuId(u64::MAX.to_be_bytes()).codename().unwrap(), "Unknown");
+ assert_eq!(CpuId(u64::MIN.to_be_bytes()).codename().unwrap(), "Unknown");
+ }
+}
diff --git a/client/hwlib/src/collectors/cpuinfo.rs b/client/hwlib/src/collectors/cpuinfo.rs
new file mode 100644
index 00000000..57ffd64b
--- /dev/null
+++ b/client/hwlib/src/collectors/cpuinfo.rs
@@ -0,0 +1,199 @@
+/* Copyright 2024 Canonical Ltd.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Written by:
+ * Nadzeya Hutsko
+ */
+
+use anyhow::Result;
+use itertools::Itertools;
+use std::{collections::HashMap, fs::read_to_string, path::Path};
+
+#[derive(Debug)]
+pub struct CpuInfo {
+ pub platform: String,
+ pub cores_count: usize,
+ pub cpu_type: String,
+ pub model: String,
+ pub model_number: String,
+ pub model_version: String,
+ pub model_revision: String,
+ pub cache: Option,
+ pub bogomips: Option,
+ pub speed: u64,
+}
+
+impl CpuInfo {
+ /// Parse cpuinfo file the same way it's done in checkbox:
+ /// https://github.com/canonical/checkbox/blob/3789fdd/checkbox-support/checkbox_support/parsers/cpuinfo.py
+ pub fn from_file(cpuinfo_filepath: &Path) -> Result {
+ let mut attributes: HashMap<&str, &str> = HashMap::new();
+ let mut cores_count = 0;
+
+ let raw_cpuinfo = read_to_string(cpuinfo_filepath)?;
+ for line in raw_cpuinfo.lines() {
+ let trimmed_line = line.trim();
+ if trimmed_line.is_empty() {
+ continue;
+ }
+
+ if let Some((key, value)) = trimmed_line.split(':').collect_tuple() {
+ let key = key.trim_end();
+ if key == "processor" {
+ cores_count += 1;
+ }
+ attributes.insert(key, value.trim_start());
+ }
+ }
+
+ let arch = std::env::consts::ARCH;
+ let speed_str = attributes.get("cpu MHz").copied();
+ let speed = speed_str
+ .map(CpuSpeed::try_from)
+ .transpose()?
+ .unwrap_or_default()
+ .get_m_hz_as_int();
+
+ let platform = arch.to_string();
+ let model = attributes
+ .get("Model")
+ .or_else(|| attributes.get("model name"))
+ .unwrap_or(&arch)
+ .to_string();
+
+ let cpu_type = attributes.get("vendor_id").unwrap_or(&arch).to_string();
+ let model_number = attributes
+ .remove("cpu family")
+ .unwrap_or_default()
+ .to_string();
+ let model_version = attributes.remove("model").unwrap_or_default().to_string();
+ let model_revision = attributes
+ .remove("stepping")
+ .unwrap_or_default()
+ .to_string();
+ let cache = attributes
+ .remove("cache size")
+ .map(parse_cache_size)
+ .transpose()?;
+ let bogomips = attributes
+ .remove("bogomips")
+ .map(parse_bogomips)
+ .transpose()?;
+
+ Ok(CpuInfo {
+ platform,
+ cores_count,
+ cpu_type,
+ model,
+ model_number,
+ model_version,
+ model_revision,
+ cache,
+ bogomips,
+ speed,
+ })
+ }
+}
+
+#[derive(Debug, Default)]
+pub struct CpuFrequency {
+ m_hz: u64,
+}
+
+impl CpuFrequency {
+ /// Read max CPU frequency from file and parse it in MHz as it's done in checkbox.
+ /// https://github.com/canonical/checkbox/blob/3789fdd/providers/resource/bin/cpuinfo_resource.py#L56-L63
+ pub fn from_k_hz_file(max_cpu_frequency_filepath: &Path) -> Result {
+ let raw_freq = read_to_string(max_cpu_frequency_filepath)?;
+ let k_hz: u64 = raw_freq.trim().parse()?;
+ Ok(Self::from_k_hz(k_hz))
+ }
+
+ /// Create a CpuFrequency from a frequency in kHz
+ pub const fn from_k_hz(k_hz: u64) -> Self {
+ Self { m_hz: k_hz / 1000 } // Convert kHz to MHz
+ }
+
+ /// Create a CpuFrequency from a frequency in MHz
+ pub const fn from_m_hz(m_hz: u64) -> Self {
+ Self { m_hz }
+ }
+
+ pub const fn get_m_hz(&self) -> u64 {
+ self.m_hz
+ }
+}
+
+#[derive(Debug, Default)]
+struct CpuSpeed {
+ m_hz: f64,
+}
+
+impl CpuSpeed {
+ /// Get the frequency in MHz as a rounded integer
+ fn get_m_hz_as_int(&self) -> u64 {
+ self.m_hz.round() as u64
+ }
+}
+
+impl TryFrom<&str> for CpuSpeed {
+ type Error = anyhow::Error;
+
+ fn try_from(raw: &str) -> Result {
+ Ok(CpuSpeed {
+ m_hz: raw.parse::()?,
+ })
+ }
+}
+
+fn parse_cache_size(cache_size: &str) -> Result {
+ Ok(cache_size
+ .strip_suffix(" KB")
+ .unwrap_or(cache_size)
+ .parse::()?)
+}
+
+fn parse_bogomips(bogomips: &str) -> Result {
+ let bogo_str = bogomips.replace(' ', "");
+ Ok(bogo_str.parse::().map(|b| b.round() as u64)?)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::{CpuFrequency, CpuInfo};
+ use crate::helpers::test_utils::get_test_filepath;
+
+ #[test]
+ fn test_parsing_cpuinfo() {
+ let cpuinfo = CpuInfo::from_file(&get_test_filepath("cpuinfo")).unwrap();
+ assert_eq!(cpuinfo.platform, std::env::consts::ARCH);
+ assert_eq!(cpuinfo.cores_count, 2);
+ assert_eq!(cpuinfo.cpu_type, "GenuineIntel");
+ assert_eq!(cpuinfo.model, "Intel(R) Celeron(R) 6305E @ 1.80GHz");
+ assert_eq!(cpuinfo.model_number, "6");
+ assert_eq!(cpuinfo.model_version, "140");
+ assert_eq!(cpuinfo.model_revision, "1");
+ assert_eq!(cpuinfo.cache.unwrap(), 4096);
+ assert_eq!(cpuinfo.bogomips.unwrap(), 3610);
+ }
+
+ #[test]
+ fn test_read_max_cpu_frequency() {
+ let cpu_freq =
+ CpuFrequency::from_k_hz_file(&get_test_filepath("cpuinfo_max_freq")).unwrap();
+ assert_eq!(cpu_freq.m_hz, 1800);
+ }
+}
diff --git a/client/hwlib/src/collectors/hardware_info.rs b/client/hwlib/src/collectors/hardware_info.rs
new file mode 100644
index 00000000..9e4d803e
--- /dev/null
+++ b/client/hwlib/src/collectors/hardware_info.rs
@@ -0,0 +1,326 @@
+/* Copyright 2024 Canonical Ltd.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Written by:
+ * Nadzeya Hutsko
+ */
+
+use anyhow::Result;
+use smbioslib::{
+ SMBiosBaseboardInformation, SMBiosData, SMBiosEntryPoint32, SMBiosEntryPoint64,
+ SMBiosInformation, SMBiosProcessorInformation, SMBiosSystemChassisInformation,
+ SMBiosSystemInformation, SMBiosVersion,
+};
+use std::{fs::read_to_string, io::ErrorKind, path::Path};
+use time::{macros::format_description, Date};
+
+use crate::{
+ collectors::{
+ cpuid::CpuId,
+ cpuinfo::{CpuFrequency, CpuInfo},
+ },
+ helpers::append_to_pathbuf,
+ models::devices::{Bios, Board, Chassis, Processor},
+};
+
+pub fn load_smbios_data(entry_filepath: &Path, table_filepath: &Path) -> Option {
+ match table_load_from_device(entry_filepath, table_filepath) {
+ Ok(data) => Some(data),
+ Err(e) => {
+ eprintln!("failed to load SMBIOS data: {}.", e);
+ None
+ }
+ }
+}
+
+impl TryFrom<&SMBiosInformation<'_>> for Bios {
+ type Error = anyhow::Error;
+
+ fn try_from(bios_info: &SMBiosInformation) -> Result {
+ let release_date_str = bios_info.release_date().to_string();
+ let release_date_format = format_description!("[month]/[day]/[year]");
+ let release_date_parsed = Date::parse(&release_date_str, &release_date_format)?;
+ let output_format = format_description!("[year]-[month]-[day]");
+ let release_date = release_date_parsed.format(&output_format)?;
+ let firmware_revision = match (
+ bios_info.system_bios_major_release(),
+ bios_info.system_bios_minor_release(),
+ ) {
+ (Some(major), Some(minor)) => Some(format!("{}.{}", major, minor)),
+ _ => None,
+ };
+ Ok(Bios {
+ firmware_revision,
+ release_date: Some(release_date),
+ revision: Some(bios_info.version().to_string()),
+ vendor: bios_info.vendor().to_string(),
+ version: bios_info.version().to_string(),
+ })
+ }
+}
+
+/// Retrieve CPU information from SMBIOS
+impl TryFrom<(&SMBiosProcessorInformation<'_>, &Path)> for Processor {
+ type Error = anyhow::Error;
+
+ fn try_from(value: (&SMBiosProcessorInformation, &Path)) -> Result {
+ let (processor_info, max_cpu_frequency_filepath) = value;
+ let cpu_id = CpuId::from_smbios(processor_info)?;
+ let cpu_freq = CpuFrequency::from_k_hz_file(max_cpu_frequency_filepath)?.get_m_hz();
+ Ok(Processor {
+ codename: cpu_id.codename().unwrap_or_else(|| "Unknown".to_string()),
+ frequency: cpu_freq,
+ manufacturer: processor_info.processor_manufacturer().to_string(),
+ version: processor_info.processor_version().to_string(),
+ })
+ }
+}
+
+/// Retrieve CPU information from cpuinfo file
+impl TryFrom<(&Path, &Path)> for Processor {
+ type Error = anyhow::Error;
+
+ fn try_from(value: (&Path, &Path)) -> Result {
+ let (cpuinfo_filepath, max_cpu_frequency_filepath) = value;
+ let cpu_info = CpuInfo::from_file(cpuinfo_filepath)?;
+ let cpu_freq = CpuFrequency::from_k_hz_file(max_cpu_frequency_filepath)?.get_m_hz();
+ Ok(Processor {
+ codename: String::new(),
+ frequency: cpu_freq,
+ manufacturer: cpu_info.cpu_type,
+ version: cpu_info.model,
+ })
+ }
+}
+
+impl TryFrom<&SMBiosSystemChassisInformation<'_>> for Chassis {
+ type Error = anyhow::Error;
+
+ fn try_from(chassis_info: &SMBiosSystemChassisInformation) -> Result {
+ let chassis_type = chassis_info
+ .chassis_type()
+ .ok_or("failed to get chassis type")
+ .unwrap()
+ .to_string();
+ let manufacturer = chassis_info.manufacturer().ok().unwrap_or_default();
+ let sku = chassis_info.sku_number().ok().unwrap_or_default();
+ let version = chassis_info.version().ok().unwrap_or_default();
+ Ok(Chassis {
+ chassis_type,
+ manufacturer,
+ sku,
+ version,
+ })
+ }
+}
+
+impl TryFrom<&SMBiosBaseboardInformation<'_>> for Board {
+ type Error = anyhow::Error;
+
+ fn try_from(board_info: &SMBiosBaseboardInformation) -> Result {
+ let manufacturer = board_info.manufacturer().ok().unwrap_or_default();
+ let product_name = board_info.product().ok().unwrap_or_default();
+ let version = board_info.version().ok().unwrap_or_default();
+ Ok(Board {
+ manufacturer,
+ product_name,
+ version,
+ })
+ }
+}
+
+impl TryFrom<&Path> for Board {
+ type Error = anyhow::Error;
+
+ fn try_from(device_tree_filepath: &Path) -> Result {
+ let try_read = |file| {
+ read_to_string(file)
+ .as_ref()
+ .map(|s| s.trim())
+ .unwrap_or("Unknown")
+ .to_string()
+ };
+ // Since it's still not clear how to fetch the manufacturer in such case,
+ // we set the 'Unknown' value to it.
+ let manufacturer = String::from("Unknown");
+ let product_name = try_read(append_to_pathbuf(
+ device_tree_filepath.to_path_buf(),
+ "model",
+ ));
+ let version = try_read(append_to_pathbuf(
+ device_tree_filepath.to_path_buf(),
+ "compatible",
+ ));
+ Ok(Board {
+ manufacturer,
+ product_name,
+ version,
+ })
+ }
+}
+
+pub struct SystemInfo {
+ pub product_name: String,
+ pub manufacturer: String,
+}
+
+impl SystemInfo {
+ pub fn try_from_smbios(system_data: &SMBiosSystemInformation) -> Result {
+ Ok(SystemInfo {
+ product_name: system_data.product_name().ok().unwrap_or_default(),
+ manufacturer: system_data.manufacturer().ok().unwrap_or_default(),
+ })
+ }
+}
+
+#[cfg(target_os = "linux")]
+/// Overwrite smbioslib::table_load_from_device to use parameters for files
+/// so we can test our code without reading smbios data from the machine that
+/// runs the test.
+pub(crate) fn table_load_from_device(
+ entry_file: &Path,
+ table_file: &Path,
+) -> Result {
+ let version = SMBiosEntryPoint64::try_load_from_file(entry_file)
+ .map(|entry_point| SMBiosVersion {
+ major: entry_point.major_version(),
+ minor: entry_point.minor_version(),
+ revision: 0,
+ })
+ .or_else(|err| {
+ if err.kind() != ErrorKind::InvalidData {
+ return Err(err);
+ }
+ SMBiosEntryPoint32::try_load_from_file(entry_file).map(|entry_point| SMBiosVersion {
+ major: entry_point.major_version(),
+ minor: entry_point.minor_version(),
+ revision: 0,
+ })
+ })?;
+
+ Ok(SMBiosData::try_load_from_file(
+ table_file.to_str().unwrap(),
+ Some(version),
+ )?)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::helpers::test_utils::get_test_filepath;
+
+ #[test]
+ fn test_load_smbios_data() {
+ let result = load_smbios_data(
+ &get_test_filepath("smbios_entry_point"),
+ &get_test_filepath("DMI"),
+ );
+ assert!(result.is_some());
+ }
+
+ #[test]
+ fn test_bios_from_smbios() {
+ let smbios_data = load_smbios_data(
+ &get_test_filepath("smbios_entry_point"),
+ &get_test_filepath("DMI"),
+ )
+ .unwrap();
+ let bios_info_vec = smbios_data.collect::();
+ let bios_info = bios_info_vec.first().unwrap();
+ let bios = Bios::try_from(bios_info).unwrap();
+ assert!(bios.firmware_revision.is_some());
+ assert!(bios.release_date.is_some());
+ assert!(bios.revision.is_some());
+ assert_eq!(bios.vendor, "American Megatrends International, LLC.");
+ assert_eq!(bios.version, "5.19");
+ assert_eq!(bios.release_date.unwrap(), "2021-05-14");
+ assert_eq!(bios.firmware_revision.unwrap(), "5.19");
+ assert_eq!(bios.revision.unwrap(), "5.19");
+ }
+
+ #[test]
+ fn test_processor_from_smbios() {
+ let smbios_data = load_smbios_data(
+ &get_test_filepath("smbios_entry_point"),
+ &get_test_filepath("DMI"),
+ )
+ .unwrap();
+ let processor_info_vec = smbios_data.collect::();
+ let processor_info = processor_info_vec.first().unwrap();
+ let processor = Processor::try_from((
+ processor_info,
+ get_test_filepath("cpuinfo_max_freq").as_path(),
+ ))
+ .unwrap();
+ assert_eq!(processor.codename, "Tiger Lake");
+ assert_eq!(processor.frequency, 1800);
+ assert_eq!(processor.manufacturer, "Intel(R) Corporation");
+ assert_eq!(processor.version, "Intel(R) Celeron(R) 6305E @ 1.80GHz");
+ }
+
+ #[test]
+ fn test_collect_chassis_info() {
+ let smbios_data = load_smbios_data(
+ &get_test_filepath("smbios_entry_point"),
+ &get_test_filepath("DMI"),
+ )
+ .unwrap();
+ let chassis_info_vec = smbios_data.collect::();
+ let chassis_info = chassis_info_vec.first().unwrap();
+ let chassis = Chassis::try_from(chassis_info).unwrap();
+ assert_eq!(chassis.chassis_type, "Desktop");
+ assert_eq!(chassis.manufacturer, "AAEON");
+ assert_eq!(chassis.sku, "Default string");
+ assert_eq!(chassis.version, "V1.0");
+ }
+
+ #[test]
+ fn test_collect_motherboard_info() {
+ let smbios_data = load_smbios_data(
+ &get_test_filepath("smbios_entry_point"),
+ &get_test_filepath("DMI"),
+ )
+ .unwrap();
+ let board_info_vec = smbios_data.collect::();
+ let board_info = board_info_vec.first().unwrap();
+ let board = Board::try_from(board_info).unwrap();
+ assert_eq!(board.manufacturer, "AAEON");
+ assert_eq!(board.product_name, "UPX-TGL01");
+ assert_eq!(board.version, "V1.0");
+ }
+
+ #[test]
+ fn test_collect_motherboard_info_from_device_tree() {
+ let board = Board::try_from(get_test_filepath("device-tree/").as_path()).unwrap();
+ assert_eq!(board.manufacturer, "Unknown");
+ assert_eq!(board.product_name, "Raspberry Pi 4 Model B Rev 1.5");
+ assert_eq!(board.version, "raspberrypi,4-model-bbrcm,bcm2711");
+ }
+
+ #[test]
+ fn test_get_system_info() {
+ let smbios_data = load_smbios_data(
+ &get_test_filepath("smbios_entry_point"),
+ &get_test_filepath("DMI"),
+ )
+ .unwrap();
+ let system_data_vec = smbios_data.collect::();
+ let system_data = system_data_vec.first().unwrap();
+ let system_info = SystemInfo::try_from_smbios(system_data).unwrap();
+ assert_eq!(system_info.product_name, "UPX-TGL01");
+ assert_eq!(system_info.manufacturer, "AAEON");
+ }
+}
diff --git a/client/hwlib/src/collectors/mod.rs b/client/hwlib/src/collectors/mod.rs
new file mode 100644
index 00000000..c7fe2519
--- /dev/null
+++ b/client/hwlib/src/collectors/mod.rs
@@ -0,0 +1,24 @@
+/* Copyright 2024 Canonical Ltd.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Written by:
+ * Nadzeya Hutsko
+ */
+
+mod cpuid;
+pub mod cpuinfo;
+pub mod hardware_info;
+pub mod os_info;
diff --git a/client/hwlib/src/collectors/os_info.rs b/client/hwlib/src/collectors/os_info.rs
new file mode 100644
index 00000000..0fd7fadb
--- /dev/null
+++ b/client/hwlib/src/collectors/os_info.rs
@@ -0,0 +1,114 @@
+/* Copyright 2024 Canonical Ltd.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Written by:
+ * Nadzeya Hutsko
+ */
+
+use anyhow::{anyhow, Context, Result};
+use lazy_static::lazy_static;
+use regex::Regex;
+use std::{fs::read_to_string, path::Path, process::Command};
+
+use crate::models::software::{KernelPackage, OS};
+
+impl TryFrom<&Path> for OS {
+ type Error = anyhow::Error;
+
+ fn try_from(proc_version_filepath: &Path) -> Result {
+ let codename = get_codename()?;
+ let distributor = get_distributor()?;
+ let version = get_version()?;
+ let kernel = KernelPackage::try_from(proc_version_filepath)?;
+ Ok(OS {
+ codename,
+ distributor,
+ version,
+ kernel,
+ })
+ }
+}
+
+impl TryFrom<&Path> for KernelPackage {
+ type Error = anyhow::Error;
+
+ fn try_from(proc_version_filepath: &Path) -> Result {
+ let kernel_version = read_to_string(proc_version_filepath)?;
+ let kernel_version = kernel_version
+ .split_whitespace()
+ .nth(2)
+ .unwrap_or_default()
+ .to_string();
+ let loaded_modules_output = Command::new("lsmod").output()?;
+ let loaded_modules_str = String::from_utf8(loaded_modules_output.stdout)?;
+ let loaded_modules: Vec = loaded_modules_str
+ .lines()
+ .skip(1) // skip the header
+ .map(|line| line.split_whitespace().next().unwrap_or("").to_string())
+ .collect();
+ Ok(KernelPackage {
+ name: Some("Linux".to_string()),
+ version: kernel_version,
+ signature: None, // Signature is not available easily, so we set it to None for now.
+ loaded_modules,
+ })
+ }
+}
+
+pub(crate) fn get_architecture() -> Result {
+ let arch = Command::new("dpkg")
+ .arg("--print-architecture")
+ .output()?
+ .stdout;
+ Ok(String::from_utf8(arch)?.trim().to_string())
+}
+
+pub(super) fn get_codename() -> Result {
+ lazy_static! {
+ static ref CODENAME_RE: Regex = Regex::new(r"Codename:\s*(\S+)").unwrap();
+ }
+ get_lsb_release_info("-c", &CODENAME_RE)
+}
+
+pub(super) fn get_distributor() -> Result {
+ lazy_static! {
+ static ref DISTRIBUTOR_RE: Regex = Regex::new(r"Distributor ID:\s*(\S+)").unwrap();
+ }
+ get_lsb_release_info("-i", &DISTRIBUTOR_RE)
+}
+
+pub(super) fn get_version() -> Result {
+ lazy_static! {
+ static ref VERSION_RE: Regex = Regex::new(r"Release:\s*(\S+)").unwrap();
+ }
+ get_lsb_release_info("-r", &VERSION_RE)
+}
+
+fn get_lsb_release_info(flag: &str, re: &Regex) -> Result {
+ let lsb_release_output = Command::new("lsb_release")
+ .arg(flag)
+ .output()
+ .context(format!(
+ "failed to execute lsb_release command with flag {}",
+ flag
+ ))?;
+ let output_str = String::from_utf8(lsb_release_output.stdout)
+ .context("failed to convert lsb_release output to UTF-8 string")?;
+ re.captures(&output_str)
+ .and_then(|caps| caps.get(1))
+ .map(|m| m.as_str().to_string())
+ .ok_or_else(|| anyhow!("failed to capture information using regex"))
+}
diff --git a/client/hwlib/src/constants.rs b/client/hwlib/src/constants.rs
new file mode 100644
index 00000000..bda5b820
--- /dev/null
+++ b/client/hwlib/src/constants.rs
@@ -0,0 +1,26 @@
+/* Copyright 2024 Canonical Ltd.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Written by:
+ * Nadzeya Hutsko
+ */
+
+pub const CERT_STATUS_ENDPOINT: &str = "/v1/certification/status";
+
+pub const CPU_MAX_FREQ_FILE_PATH: &str = "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
+pub const PROC_CPUINFO_FILE_PATH: &str = "/proc/cpuinfo";
+pub const PROC_DEVICE_TREE_DIR_PATH: &str = "/proc/device-tree/";
+pub const PROC_VERSION_FILE_PATH: &str = "/proc/version";
diff --git a/client/hwlib/src/helpers.rs b/client/hwlib/src/helpers.rs
new file mode 100644
index 00000000..cfbb4cf0
--- /dev/null
+++ b/client/hwlib/src/helpers.rs
@@ -0,0 +1,58 @@
+/* Copyright 2024 Canonical Ltd.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Written by:
+ * Nadzeya Hutsko
+ */
+
+use std::path::PathBuf;
+
+pub(crate) fn append_to_pathbuf(p: PathBuf, s: &str) -> PathBuf {
+ let mut p = p.into_os_string();
+ p.push(s);
+ p.into()
+}
+
+#[cfg(test)]
+pub(crate) mod test_utils {
+ use anyhow::{bail, Result};
+ use std::{env, fs::read_dir, path::PathBuf};
+
+ pub(crate) fn get_test_filepath(file_name: &str) -> PathBuf {
+ let mut path = get_project_root().unwrap();
+ if !path.to_str().unwrap().contains("hwlib") {
+ // If hwlib is not in the path, the project root is the monorepo root
+ path.extend(["client", "hwlib"]);
+ }
+ path.extend(["tests", "test_data", file_name]);
+ path
+ }
+
+ fn get_project_root() -> Result {
+ let path = env::current_dir()?;
+ let path_ancestors = path.as_path().ancestors();
+
+ for p in path_ancestors {
+ let has_cargo_lock = read_dir(p)?
+ .flat_map(|entry| entry.ok())
+ .any(|entry| entry.file_name() == "Cargo.lock");
+ if has_cargo_lock {
+ return Ok(PathBuf::from(p));
+ }
+ }
+ bail!("ran out of places to find Cargo.lock")
+ }
+}
diff --git a/client/hwlib/src/lib.rs b/client/hwlib/src/lib.rs
index e58f5fde..76125494 100644
--- a/client/hwlib/src/lib.rs
+++ b/client/hwlib/src/lib.rs
@@ -19,107 +19,31 @@
* Nadzeya Hutsko
*/
+pub mod collectors;
+mod constants;
+mod helpers;
pub mod models;
pub mod py_bindings;
-use models::devices;
-use models::rbody::{CertifiedResponse, RelatedCertifiedSystemExistsResponse};
-use models::software;
-fn get_certified_system_sample() -> CertifiedResponse {
- let kernel_package = software::KernelPackage {
- name: "Linux".to_string(),
- version: "5.4.0-42-generic".to_string(),
- signature: "Sample Signature".to_string(),
- };
+use anyhow::Result;
+use reqwest::Client;
- CertifiedResponse {
- status: "Certified".to_string(),
- os: software::OS {
- distributor: "Ubuntu".to_string(),
- description: "Ubuntu 20.04.1 LTS".to_string(),
- version: "20.04".to_string(),
- codename: "focal".to_string(),
- kernel: kernel_package,
- loaded_modules: vec!["module1".to_string(), "module2".to_string()],
- },
- bios: devices::Bios {
- firmware_revision: "1.0".to_string(),
- release_date: "2020-01-01".to_string(),
- revision: "rev1".to_string(),
- vendor: "BIOSVendor".to_string(),
- version: "v1.0".to_string(),
- },
- }
-}
-
-fn get_related_certified_system_exists_sample() -> RelatedCertifiedSystemExistsResponse {
- RelatedCertifiedSystemExistsResponse {
- status: "Partially Certified".to_string(),
- board: devices::Board {
- manufacturer: "Sample Manufacturer".to_string(),
- product_name: "Sample Product".to_string(),
- version: "v1.0".to_string(),
- },
- chassis: Some(devices::Chassis {
- chassis_type: "Sample Type".to_string(),
- manufacturer: "Sample Manufacturer".to_string(),
- sku: "Sample SKU".to_string(),
- version: "v1.0".to_string(),
- }),
- processor: Some(vec![devices::Processor {
- family: "Sample Family".to_string(),
- frequency: 3.5,
- manufacturer: "Sample Manufacturer".to_string(),
- version: "v1.0".to_string(),
- }]),
- gpu: Some(vec![devices::GPU {
- family: "Sample Family".to_string(),
- manufacturer: "Sample Manufacturer".to_string(),
- version: "v1.0".to_string(),
- }]),
- audio: Some(vec![devices::Audio {
- model: "Sample Model".to_string(),
- vendor: "Sample Vendor".to_string(),
- }]),
- video: Some(vec![devices::VideoCapture {
- model: "Sample Model".to_string(),
- vendor: "Sample Vendor".to_string(),
- }]),
- network: Some(vec![devices::NetworkAdapter {
- bus: "Sample Bus".to_string(),
- id: "Sample ID".to_string(),
- model: "Sample Model".to_string(),
- vendor: "Sample Vendor".to_string(),
- capacity: 1000,
- }]),
- wireless: Some(vec![devices::WirelessAdapter {
- model: "Sample Model".to_string(),
- vendor: "Sample Vendor".to_string(),
- }]),
- pci_peripherals: Some(vec![devices::PCIPeripheral {
- name: "Sample Name".to_string(),
- pci_id: "0000:0000".to_string(),
- vendor: "Sample Vendor".to_string(),
- }]),
- usb_peripherals: Some(vec![devices::USBPeripheral {
- name: "Sample Name".to_string(),
- usb_id: "0000:0000".to_string(),
- vendor: "Sample Vendor".to_string(),
- }]),
- }
-}
-
-pub async fn get_certification_status(_url: &str) -> Result {
- let response_type = std::env::var("CERTIFICATION_STATUS")
- .unwrap_or_else(|_| "0".to_string())
- .parse::()
- .unwrap_or(0);
+use constants::CERT_STATUS_ENDPOINT;
+use models::{
+ request_validators::CertificationStatusRequest,
+ response_validators::CertificationStatusResponse,
+};
- let response = match response_type {
- 1 => serde_json::json!(get_related_certified_system_exists_sample()),
- 2 => serde_json::json!(get_certified_system_sample()),
- _ => serde_json::json!({"status": "Not Seen"}),
- };
+pub async fn send_certification_status_request(
+ url: String,
+ request: &CertificationStatusRequest,
+) -> Result {
+ let client = Client::new();
+ let mut server_url = url.clone();
+ server_url.push_str(CERT_STATUS_ENDPOINT);
+ let response = client.post(server_url).json(request).send().await?;
+ let response_text = response.text().await?;
+ let typed_response: CertificationStatusResponse = serde_json::from_str(&response_text)?;
- Ok(response)
+ Ok(typed_response)
}
diff --git a/client/hwlib/src/models/devices.rs b/client/hwlib/src/models/devices.rs
index 3b8d53c3..59611c73 100644
--- a/client/hwlib/src/models/devices.rs
+++ b/client/hwlib/src/models/devices.rs
@@ -20,29 +20,30 @@
use serde::{Deserialize, Serialize};
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct Audio {
+ pub identifier: String,
pub model: String,
pub vendor: String,
}
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct Bios {
- pub firmware_revision: String,
- pub release_date: String,
- pub revision: String,
+ pub firmware_revision: Option,
+ pub release_date: Option,
+ pub revision: Option,
pub vendor: String,
pub version: String,
}
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct Board {
pub manufacturer: String,
pub product_name: String,
pub version: String,
}
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct Chassis {
pub chassis_type: String,
pub manufacturer: String,
@@ -50,53 +51,66 @@ pub struct Chassis {
pub version: String,
}
-#[derive(Serialize, Deserialize, Debug)]
-#[allow(clippy::upper_case_acronyms)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct GPU {
- pub family: String,
+ pub codename: Option,
+ pub identifier: String,
pub manufacturer: String,
+ pub status: Option,
pub version: String,
}
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct NetworkAdapter {
pub bus: String,
- pub id: String,
+ pub capacity: i32,
+ pub identifier: String,
pub model: String,
pub vendor: String,
- pub capacity: i32,
}
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct PCIPeripheral {
pub pci_id: String,
pub name: String,
+ pub status: Option,
pub vendor: String,
}
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct Processor {
- pub family: String,
- pub frequency: f64,
+ pub codename: String,
+ pub frequency: u64,
pub manufacturer: String,
pub version: String,
}
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct USBPeripheral {
pub usb_id: String,
pub name: String,
+ pub status: Option,
pub vendor: String,
}
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct VideoCapture {
+ pub identifier: String,
pub model: String,
+ pub status: Option,
pub vendor: String,
}
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct WirelessAdapter {
+ pub identifier: String,
pub model: String,
+ pub status: Option,
pub vendor: String,
}
+
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
+pub enum DeviceStatus {
+ Working,
+ Breaking,
+}
diff --git a/client/hwlib/src/models/mod.rs b/client/hwlib/src/models/mod.rs
index 7d550d45..1670abe5 100644
--- a/client/hwlib/src/models/mod.rs
+++ b/client/hwlib/src/models/mod.rs
@@ -19,5 +19,6 @@
*/
pub mod devices;
-pub mod rbody;
+pub mod request_validators;
+pub mod response_validators;
pub mod software;
diff --git a/client/hwlib/src/models/rbody.rs b/client/hwlib/src/models/rbody.rs
deleted file mode 100644
index 43960eda..00000000
--- a/client/hwlib/src/models/rbody.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Copyright 2024 Canonical Ltd.
- * All rights reserved.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- * Written by:
- * Nadzeya Hutsko
- */
-
-use serde::{Deserialize, Serialize};
-
-use crate::models::devices;
-use crate::models::software;
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct CertifiedResponse {
- pub status: String,
- pub os: software::OS,
- pub bios: devices::Bios,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct NotSeenResponse {
- pub status: String,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct RelatedCertifiedSystemExistsResponse {
- pub status: String,
- pub board: devices::Board,
- pub chassis: Option,
- pub processor: Option>,
- pub gpu: Option>,
- pub audio: Option>,
- pub video: Option>,
- pub network: Option>,
- pub wireless: Option>,
- pub pci_peripherals: Option>,
- pub usb_peripherals: Option>,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub enum CertificationStatusResponse {
- Certified(CertifiedResponse),
- NotSeen(NotSeenResponse),
- RelatedCertifiedSystemExists(RelatedCertifiedSystemExistsResponse),
-}
diff --git a/client/hwlib/src/models/request_validators.rs b/client/hwlib/src/models/request_validators.rs
new file mode 100644
index 00000000..6d539972
--- /dev/null
+++ b/client/hwlib/src/models/request_validators.rs
@@ -0,0 +1,167 @@
+/* Copyright 2024 Canonical Ltd.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Written by:
+ * Nadzeya Hutsko
+ */
+
+use anyhow::{anyhow, Result};
+use serde::{Deserialize, Serialize};
+use smbioslib::{
+ self, SMBiosBaseboardInformation, SMBiosInformation, SMBiosProcessorInformation,
+ SMBiosSystemChassisInformation, SMBiosSystemInformation,
+};
+use std::path::PathBuf;
+
+use crate::{
+ collectors::{
+ cpuinfo::CpuInfo,
+ hardware_info::{load_smbios_data, SystemInfo},
+ os_info::get_architecture,
+ },
+ constants,
+ models::{
+ devices::{Bios, Board, Chassis, PCIPeripheral, Processor, USBPeripheral},
+ software::OS,
+ },
+};
+
+#[derive(Debug, Clone)]
+pub struct Paths {
+ pub smbios_entry_filepath: PathBuf,
+ pub smbios_table_filepath: PathBuf,
+ pub cpuinfo_filepath: PathBuf,
+ pub max_cpu_frequency_filepath: PathBuf,
+ pub device_tree_dirpath: PathBuf,
+ pub proc_version_filepath: PathBuf,
+}
+
+impl Default for Paths {
+ fn default() -> Self {
+ Self {
+ smbios_entry_filepath: PathBuf::from(smbioslib::SYS_ENTRY_FILE),
+ smbios_table_filepath: PathBuf::from(smbioslib::SYS_TABLE_FILE),
+ cpuinfo_filepath: PathBuf::from(constants::PROC_CPUINFO_FILE_PATH),
+ max_cpu_frequency_filepath: PathBuf::from(constants::CPU_MAX_FREQ_FILE_PATH),
+ device_tree_dirpath: PathBuf::from(constants::PROC_DEVICE_TREE_DIR_PATH),
+ proc_version_filepath: PathBuf::from(constants::PROC_VERSION_FILE_PATH),
+ }
+ }
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct CertificationStatusRequest {
+ pub architecture: String,
+ pub bios: Option,
+ pub board: Board,
+ pub chassis: Option,
+ pub model: String,
+ pub os: OS,
+ pub pci_peripherals: Vec,
+ pub processor: Processor,
+ pub usb_peripherals: Vec,
+ pub vendor: String,
+}
+
+impl CertificationStatusRequest {
+ pub fn new(paths: Paths) -> Result {
+ let Paths {
+ smbios_entry_filepath,
+ smbios_table_filepath,
+ ..
+ } = &paths;
+ if let Some(smbios_data) = load_smbios_data(smbios_entry_filepath, smbios_table_filepath) {
+ Self::from_smbios_data(&smbios_data, paths)
+ } else {
+ Self::from_defaults(paths)
+ }
+ }
+
+ fn from_smbios_data(data: &smbioslib::SMBiosData, paths: Paths) -> Result {
+ let Paths {
+ max_cpu_frequency_filepath,
+ proc_version_filepath,
+ ..
+ } = paths;
+ let bios_info_vec = data.collect::();
+ let bios_info = bios_info_vec
+ .first()
+ .ok_or_else(|| anyhow!("failed to load BIOS data"))?;
+ let processor_info_vec = data.collect::();
+ let processor_info = processor_info_vec
+ .first()
+ .ok_or_else(|| anyhow!("failed to load processor data"))?;
+ let chassis_info_vec = data.collect::();
+ let chassis_info = chassis_info_vec
+ .first()
+ .ok_or_else(|| anyhow!("failed to load chassis data"))?;
+ let board_info_vec = data.collect::();
+ let board_info = board_info_vec
+ .first()
+ .ok_or_else(|| anyhow!("failed to load board data"))?;
+ let system_data_vec = data.collect::();
+ let system_data = system_data_vec.first().unwrap();
+ let system_info = SystemInfo::try_from_smbios(system_data)?;
+ Ok(Self {
+ architecture: get_architecture()?,
+ bios: Some(Bios::try_from(bios_info)?),
+ board: Board::try_from(board_info)?,
+ chassis: Some(Chassis::try_from(chassis_info)?),
+ model: system_info.product_name,
+ os: OS::try_from(proc_version_filepath.as_path())?,
+ pci_peripherals: Vec::new(),
+ processor: Processor::try_from((processor_info, max_cpu_frequency_filepath.as_path()))?,
+ usb_peripherals: Vec::new(),
+ vendor: system_info.manufacturer,
+ })
+ }
+
+ fn from_defaults(paths: Paths) -> Result {
+ let Paths {
+ cpuinfo_filepath,
+ max_cpu_frequency_filepath,
+ device_tree_dirpath,
+ proc_version_filepath,
+ ..
+ } = paths;
+ let cpu_info = CpuInfo::from_file(&cpuinfo_filepath.clone())?;
+ let architecture = get_architecture()?;
+ let bios = None;
+ let board = Board::try_from(device_tree_dirpath.as_path())?;
+ let chassis = None;
+ let model = cpu_info.model;
+ let os = OS::try_from(proc_version_filepath.as_path())?;
+ let pci_peripherals = Vec::new();
+ let processor = Processor::try_from((
+ cpuinfo_filepath.as_path(),
+ max_cpu_frequency_filepath.as_path(),
+ ))?;
+ let usb_peripherals = Vec::new();
+ let vendor = String::from("Unknown");
+ Ok(Self {
+ architecture,
+ bios,
+ board,
+ chassis,
+ model,
+ os,
+ pci_peripherals,
+ processor,
+ usb_peripherals,
+ vendor,
+ })
+ }
+}
diff --git a/client/hwlib/src/models/response_validators.rs b/client/hwlib/src/models/response_validators.rs
new file mode 100644
index 00000000..db2ca508
--- /dev/null
+++ b/client/hwlib/src/models/response_validators.rs
@@ -0,0 +1,66 @@
+/* Copyright 2024 Canonical Ltd.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Written by:
+ * Nadzeya Hutsko
+ */
+
+use serde::{Deserialize, Serialize};
+
+use crate::models::{
+ devices::{
+ Audio, Bios, Board, Chassis, NetworkAdapter, PCIPeripheral, USBPeripheral, VideoCapture,
+ WirelessAdapter, GPU,
+ },
+ software::OS,
+};
+
+#[derive(Serialize, Deserialize, Debug)]
+#[serde(tag = "status")]
+pub enum CertificationStatusResponse {
+ Certified {
+ architecture: String,
+ available_releases: Vec,
+ bios: Bios,
+ board: Board,
+ chassis: Option,
+ },
+ #[serde(rename = "Not Seen")]
+ NotSeen,
+ #[serde(rename = "Certified Image Exists")]
+ CertifiedImageExists {
+ architecture: String,
+ bios: Bios,
+ board: Board,
+ available_releases: Vec,
+ chassis: Option,
+ },
+ #[serde(rename = "Related Certified System Exists")]
+ RelatedCertifiedSystemExists {
+ architecture: String,
+ board: Board,
+ bios: Bios,
+ chassis: Option,
+ gpu: Option>,
+ audio: Option>,
+ video: Option>,
+ network: Option>,
+ wireless: Option>,
+ pci_peripherals: Vec,
+ usb_peripherals: Vec,
+ available_releases: Vec,
+ },
+}
diff --git a/client/hwlib/src/models/software.rs b/client/hwlib/src/models/software.rs
index fc1da7c0..805221f8 100644
--- a/client/hwlib/src/models/software.rs
+++ b/client/hwlib/src/models/software.rs
@@ -20,19 +20,18 @@
use serde::{Deserialize, Serialize};
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct KernelPackage {
- pub name: String,
+ pub name: Option,
pub version: String,
- pub signature: String,
+ pub signature: Option,
+ pub loaded_modules: Vec,
}
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct OS {
+ pub codename: String,
pub distributor: String,
- pub description: String,
pub version: String,
- pub codename: String,
pub kernel: KernelPackage,
- pub loaded_modules: Vec,
}
diff --git a/client/hwlib/src/py_bindings.rs b/client/hwlib/src/py_bindings.rs
index ff58a76b..86546e68 100644
--- a/client/hwlib/src/py_bindings.rs
+++ b/client/hwlib/src/py_bindings.rs
@@ -18,24 +18,37 @@
* Nadzeya Hutsko
*/
-use crate::get_certification_status as native_get_certification_status;
-use once_cell::sync::Lazy;
-use pyo3::exceptions::PyRuntimeError;
-use pyo3::prelude::*;
-use pyo3::types::PyString;
-use pyo3::wrap_pyfunction;
-use pyo3::{PyObject, PyResult, Python};
+use crate::{
+ models::request_validators::{CertificationStatusRequest, Paths},
+ send_certification_status_request as native_send_certification_status_request,
+};
+use lazy_static::lazy_static;
+use pyo3::{
+ exceptions::PyRuntimeError,
+ prelude::*,
+ types::PyString,
+ wrap_pyfunction, {PyObject, PyResult, Python},
+};
+use serde_json;
use tokio::runtime::Runtime;
-static RT: Lazy = Lazy::new(|| Runtime::new().expect("Failed to create Tokio runtime"));
+lazy_static! {
+ static ref RT: Runtime = Runtime::new().expect("failed to create Tokio runtime");
+}
+/// This function creates and sends the certification status request to the specified
+/// hardware-api server URL.
#[pyfunction]
-fn get_certification_status(py: Python, url: String) -> PyResult {
- let response = RT.block_on(async { native_get_certification_status(&url).await });
+fn send_certification_request(py: Python, url: String) -> PyResult {
+ let request_body = CertificationStatusRequest::new(Paths::default())
+ .map_err(|e| PyRuntimeError::new_err(format!("failed to create request: {}", e)))?;
+
+ let response =
+ RT.block_on(async { native_send_certification_status_request(url, &request_body).await });
match response {
Ok(response_value) => {
- let json_str = response_value.to_string();
+ let json_str = serde_json::json!(response_value).to_string();
let json: PyObject = PyString::new_bound(py, &json_str).into();
let json_module = py.import_bound("json")?;
let json_object: PyObject = json_module.call_method1("loads", (json,))?.into();
@@ -51,6 +64,6 @@ fn get_certification_status(py: Python, url: String) -> PyResult {
#[pymodule]
fn hwlib(m: &Bound<'_, PyModule>) -> PyResult<()> {
- m.add_function(wrap_pyfunction!(get_certification_status, m)?)?;
+ m.add_function(wrap_pyfunction!(send_certification_request, m)?)?;
Ok(())
}
diff --git a/client/hwlib/tests/test_cert_status.rs b/client/hwlib/tests/test_cert_status.rs
deleted file mode 100644
index de352d66..00000000
--- a/client/hwlib/tests/test_cert_status.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-use hwlib::get_certification_status;
-
-const SERVER_URL: &str = "https://example.com";
-
-#[tokio::test]
-async fn test_get_certification_status_not_seen() {
- std::env::set_var("CERTIFICATION_STATUS", "0");
- let response = get_certification_status(SERVER_URL).await.unwrap();
- assert_eq!(response.get("status").unwrap(), "Not Seen");
- std::env::remove_var("CERTIFICATION_STATUS");
-}
-
-#[tokio::test]
-async fn test_get_certification_status_partially_certified() {
- std::env::set_var("CERTIFICATION_STATUS", "1");
- let response = get_certification_status(SERVER_URL).await.unwrap();
- assert!(response.get("board").is_some());
- assert_eq!(response.get("status").unwrap(), "Partially Certified");
- std::env::remove_var("CERTIFICATION_STATUS");
-}
-
-#[tokio::test]
-async fn test_get_certification_status_certified() {
- std::env::set_var("CERTIFICATION_STATUS", "2");
- let response = get_certification_status(SERVER_URL).await.unwrap();
- assert_eq!(response.get("status").unwrap(), "Certified");
- assert!(response.get("os").is_some());
- assert!(response.get("bios").is_some());
- std::env::remove_var("CERTIFICATION_STATUS");
-}
diff --git a/client/hwlib/tests/test_data/DMI b/client/hwlib/tests/test_data/DMI
new file mode 100644
index 0000000000000000000000000000000000000000..12ad48a23b8a133b2a74b9748d15a38093fbec8f
GIT binary patch
literal 5821
zcmb_g-E&jN6+c(svM{!Rhy=s1F{I#WY$Vw-HZv{0`m$s!OVx)lY1$&&7h51p^hySt
zri}w3kbcsShd!p6X{OV@l~={wTy&JeqroW(gwF)W&x8V@AQTKLP-%A@`JDbZI6$A+X2>0S=e4UYr%P4i
znRzN^7e@*+bAcc&1V;lD)2@^@Dh*L@=;i9_6JE*IXOr5UUYDJE=>MS6?zfX-hrxCf
zH~>8$yR)VEe=yMFbwGiQsO_wZh}NoAZMjj?MUdjRqz89alE>?OtW3yCCPa{NJyvGS
zN@k2@+>ezRw~`rW8P8*7!d5b2BJY5gln^f|
zHaQud8iB$i{ufDEr7iQv5?c$kN~5%@iL72*(!>eLd+EtM^nI{TK*1_&fgUAVt5q-&
z#7e6NtZA_CWk8Q*NF769EU^YF9dP7P{|O6g3flU0O)oVz^lrhc{;AN;VVza#rPicpR%wwUcpS_yj1CO^fCWw3$=Txu=bdL_^GHc_Lx8LR8(ne
z@TsVX&f`a61xmABTOXW)xexR7Gs9Yt@E8V9V$_dDiK2)7A$A
zFOs0(6SC*7S@1lsbjm-mqy3lYWqQVB{+#Wf-O+xtIRvLZkIH9~rB}U^7#W50#aE9{
zQ7W;Bq+cXA(ccHi*3ZM%E`@Abgs{-1<`b!TUI3~8@HuKNHnvC2?u>Kr_}8RRvw5D*
z?af6*1FwG_z@z3gO+!sD;^W8`UF+c}kb185eB|Jt-2>Y)h6SB_cVnH}))UsLhF+^w
zG(9*HT`OVHs;DEeR5~4?h&q2k1gM~ws`X`T1+WviQq$MvPJjX^K@^&rJUbB{9}5M+
z%!h-abUryTzA%v#twOYY32X2E14KFPiiSPLvWC@iMH3?;nk~|X
zd|uG>IyM|sSS$!Nnieeqp6Nt^QyHEjQU+JFsk=@U?Gyqsfmy$q-Fe=aDZrR6mZAu||f5>Me4$?l#8f&bm41SxN0OsPkboKAk0
zqcs=mXqqAAHjj6|B)Ob!9((@JjTt0IAc_5LpY1O$jQhiXd}6=1LLRrrttxR!&<@76`}1vzR0$cl(;ip*$yG(Gx^)pYw%icYHO0{TMq+s|!(mJfHPXR*W)EahUSFX{IC
z{M1Tnee|(Y#xgykhc9qFmm&ObU{5ArXxMi_dKGV(>Qb$wuOO;3jWtatU~{D0VO{+l
zY@xiOL3Y@ehMW!9S_d=b=7I6a;i?6e0mk97Wr1aZIf31^z`g;DXYB(EEC-CI^7|H8
z9vE^z{l)?-0P_O-zyd1*WAFXW0$Tv)1NM6h>>{vz!2V!?EduKW_K^j4iIn%)*MI6@
zd9|Rjt6ziIen5ZiK&Gp|35qauTKe7RCvzQ$)A
zV+}6O$5Q4?JldV8XEIq6$<;Rb08rAenN`_JslmpX712tWx665~63ex!Sxv%+!81y)
zUu$jQj0(Qv#8=Go!S@O>i>q%>{hm6cH}
z*+^6#Z<#CIM`wd-C_?RA;_+UU)T6UwYKW4tbc&hRSm%L$DGuiaM}x>r!4nKste}vD
z)~%p1301A2aS7F|ps<9l?}V^zHO=_071T0AXUKP48s7Jv+nx>IjBN8Uw~n
zlM&j%2%3yrtiRcu4JJaQMnwu6bdPsaj!|3~Ch2UOG?^_|ceB3VW=$rzF;*p`#^~V>OeJH0
z^YWV=zSxy-2{UL7>RX1UxO7)G#i!_1y|ivxv}Gy_L)lz5Dw6ng6Kl;=ll8X2nqj9h
zE1!zR1?ZlXKf>D`9f*7086|7;uuUW(P0EvL^aY8=Xi>LtC+Pv&(aL+l25e6B60o
zK6|%~k4pt=VG*&{Eq||#2WPo?bs-%!+rDo=QZ%9#3L;{H@0lR?N`k5h{;&;(WHjOk
zBNC;miT=nCjd7FRz5Q_;8DHSeq_y=LHkIvfX2#I{#N=?p-JDLW|J2|_rBl>-k-eB}
zw}0RBvv!i(?|DGVr$i^yaipG4ul}4Ahf=iB1DA4lN`T~;ZZm0m3lqb-*umK{c-?p2
z?N}Bx7sN3v72?YnUogGzyM7_b`w#z;l@kzi(KU}V@U#=yYfa4H>0G5`QL00aR5
literal 0
HcmV?d00001
diff --git a/client/hwlib/tests/test_data/version b/client/hwlib/tests/test_data/version
new file mode 100644
index 00000000..b5fe12ae
--- /dev/null
+++ b/client/hwlib/tests/test_data/version
@@ -0,0 +1 @@
+Linux version 6.8.0-38-generic (buildd@lcy02-amd64-049) (x86_64-linux-gnu-gcc-13 (Ubuntu 13.2.0-23ubuntu4) 13.2.0, GNU ld (GNU Binutils for Ubuntu) 2.42) #38-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 7 15:25:01 UTC 2024
diff --git a/client/hwlib/tox.ini b/client/hwlib/tox.ini
index fd4ef39a..9eff8118 100644
--- a/client/hwlib/tox.ini
+++ b/client/hwlib/tox.ini
@@ -8,7 +8,6 @@ deps =
black
ruff
pytest
- pytest-asyncio
maturin
commands_pre =
{envbindir}/maturin develop
diff --git a/server/hwapi/data_models/repository.py b/server/hwapi/data_models/repository.py
index d185174c..01199f29 100644
--- a/server/hwapi/data_models/repository.py
+++ b/server/hwapi/data_models/repository.py
@@ -19,7 +19,7 @@
from typing import Any
-from sqlalchemy import select, and_
+from sqlalchemy import select, and_, null
from sqlalchemy.orm import Session, selectinload
from hwapi.data_models import models
@@ -86,7 +86,7 @@ def get_board(
.join(models.Vendor)
.where(
and_(
- models.Vendor.name.ilike(vendor_name),
+ models.Vendor.name.ilike(_clean_vendor_name(vendor_name)),
models.Device.name.ilike(product_name),
models.Device.version.ilike(version),
models.Device.category.in_(
@@ -107,12 +107,15 @@ def get_bios(
.join(models.Vendor)
.where(
and_(
- models.Vendor.name.ilike(vendor_name),
+ models.Vendor.name.ilike(_clean_vendor_name(vendor_name)),
models.Bios.version.ilike(version),
- models.Bios.firmware_revision == firmware_revision,
)
)
)
+
+ if firmware_revision:
+ stmt = stmt.filter(models.Bios.firmware_revision.ilike(firmware_revision))
+
return db.execute(stmt).scalars().first()
@@ -135,6 +138,8 @@ def get_machine_with_same_hardware_params(
stmt = stmt.join(models.Bios, models.Report.bios_id == models.Bios.id).filter(
models.Bios.id == bios.id
)
+ else:
+ stmt = stmt.filter(models.Report.bios_id.is_(null()))
machine = db.execute(stmt.distinct()).scalars().first()
return machine
diff --git a/server/hwapi/external/c3/client.py b/server/hwapi/external/c3/client.py
index f38eb55b..a74be2a8 100644
--- a/server/hwapi/external/c3/client.py
+++ b/server/hwapi/external/c3/client.py
@@ -112,8 +112,11 @@ def _import_from_c3(self, url: str, loader: Callable, resp_model: Type[BaseModel
# Without this the sqlalchemy.exc.PendingRollbackError exception occurs
self.db.rollback()
continue
- except ValueError as exc:
- logging.error("Value error occured: %s", str(exc))
+ except Exception as exc:
+ logging.error(
+ "An error occured while importing the data from C3: %s",
+ str(exc),
+ )
continue
def _load_certified_configurations_from_response(
diff --git a/server/poetry.lock b/server/poetry.lock
index 0713e9a0..102e71b9 100644
--- a/server/poetry.lock
+++ b/server/poetry.lock
@@ -35,33 +35,33 @@ trio = ["trio (>=0.23)"]
[[package]]
name = "black"
-version = "24.4.2"
+version = "24.8.0"
description = "The uncompromising code formatter."
optional = false
python-versions = ">=3.8"
files = [
- {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"},
- {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"},
- {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"},
- {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"},
- {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"},
- {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"},
- {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"},
- {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"},
- {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"},
- {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"},
- {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"},
- {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"},
- {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"},
- {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"},
- {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"},
- {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"},
- {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"},
- {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"},
- {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"},
- {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"},
- {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"},
- {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"},
+ {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"},
+ {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"},
+ {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"},
+ {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"},
+ {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"},
+ {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"},
+ {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"},
+ {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"},
+ {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"},
+ {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"},
+ {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"},
+ {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"},
+ {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"},
+ {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"},
+ {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"},
+ {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"},
+ {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"},
+ {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"},
+ {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"},
+ {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"},
+ {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"},
+ {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"},
]
[package.dependencies]
@@ -227,63 +227,83 @@ files = [
[[package]]
name = "coverage"
-version = "7.6.0"
+version = "7.6.1"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "coverage-7.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dff044f661f59dace805eedb4a7404c573b6ff0cdba4a524141bc63d7be5c7fd"},
- {file = "coverage-7.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a8659fd33ee9e6ca03950cfdcdf271d645cf681609153f218826dd9805ab585c"},
- {file = "coverage-7.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7792f0ab20df8071d669d929c75c97fecfa6bcab82c10ee4adb91c7a54055463"},
- {file = "coverage-7.6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4b3cd1ca7cd73d229487fa5caca9e4bc1f0bca96526b922d61053ea751fe791"},
- {file = "coverage-7.6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7e128f85c0b419907d1f38e616c4f1e9f1d1b37a7949f44df9a73d5da5cd53c"},
- {file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a94925102c89247530ae1dab7dc02c690942566f22e189cbd53579b0693c0783"},
- {file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dcd070b5b585b50e6617e8972f3fbbee786afca71b1936ac06257f7e178f00f6"},
- {file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d50a252b23b9b4dfeefc1f663c568a221092cbaded20a05a11665d0dbec9b8fb"},
- {file = "coverage-7.6.0-cp310-cp310-win32.whl", hash = "sha256:0e7b27d04131c46e6894f23a4ae186a6a2207209a05df5b6ad4caee6d54a222c"},
- {file = "coverage-7.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dece71673b3187c86226c3ca793c5f891f9fc3d8aa183f2e3653da18566169"},
- {file = "coverage-7.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7b525ab52ce18c57ae232ba6f7010297a87ced82a2383b1afd238849c1ff933"},
- {file = "coverage-7.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bea27c4269234e06f621f3fac3925f56ff34bc14521484b8f66a580aacc2e7d"},
- {file = "coverage-7.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed8d1d1821ba5fc88d4a4f45387b65de52382fa3ef1f0115a4f7a20cdfab0e94"},
- {file = "coverage-7.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01c322ef2bbe15057bc4bf132b525b7e3f7206f071799eb8aa6ad1940bcf5fb1"},
- {file = "coverage-7.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03cafe82c1b32b770a29fd6de923625ccac3185a54a5e66606da26d105f37dac"},
- {file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d1b923fc4a40c5832be4f35a5dab0e5ff89cddf83bb4174499e02ea089daf57"},
- {file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4b03741e70fb811d1a9a1d75355cf391f274ed85847f4b78e35459899f57af4d"},
- {file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a73d18625f6a8a1cbb11eadc1d03929f9510f4131879288e3f7922097a429f63"},
- {file = "coverage-7.6.0-cp311-cp311-win32.whl", hash = "sha256:65fa405b837060db569a61ec368b74688f429b32fa47a8929a7a2f9b47183713"},
- {file = "coverage-7.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:6379688fb4cfa921ae349c76eb1a9ab26b65f32b03d46bb0eed841fd4cb6afb1"},
- {file = "coverage-7.6.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f7db0b6ae1f96ae41afe626095149ecd1b212b424626175a6633c2999eaad45b"},
- {file = "coverage-7.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bbdf9a72403110a3bdae77948b8011f644571311c2fb35ee15f0f10a8fc082e8"},
- {file = "coverage-7.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc44bf0315268e253bf563f3560e6c004efe38f76db03a1558274a6e04bf5d5"},
- {file = "coverage-7.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da8549d17489cd52f85a9829d0e1d91059359b3c54a26f28bec2c5d369524807"},
- {file = "coverage-7.6.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0086cd4fc71b7d485ac93ca4239c8f75732c2ae3ba83f6be1c9be59d9e2c6382"},
- {file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1fad32ee9b27350687035cb5fdf9145bc9cf0a094a9577d43e909948ebcfa27b"},
- {file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:044a0985a4f25b335882b0966625270a8d9db3d3409ddc49a4eb00b0ef5e8cee"},
- {file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:76d5f82213aa78098b9b964ea89de4617e70e0d43e97900c2778a50856dac605"},
- {file = "coverage-7.6.0-cp312-cp312-win32.whl", hash = "sha256:3c59105f8d58ce500f348c5b56163a4113a440dad6daa2294b5052a10db866da"},
- {file = "coverage-7.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:ca5d79cfdae420a1d52bf177de4bc2289c321d6c961ae321503b2ca59c17ae67"},
- {file = "coverage-7.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d39bd10f0ae453554798b125d2f39884290c480f56e8a02ba7a6ed552005243b"},
- {file = "coverage-7.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:beb08e8508e53a568811016e59f3234d29c2583f6b6e28572f0954a6b4f7e03d"},
- {file = "coverage-7.6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2e16f4cd2bc4d88ba30ca2d3bbf2f21f00f382cf4e1ce3b1ddc96c634bc48ca"},
- {file = "coverage-7.6.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6616d1c9bf1e3faea78711ee42a8b972367d82ceae233ec0ac61cc7fec09fa6b"},
- {file = "coverage-7.6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad4567d6c334c46046d1c4c20024de2a1c3abc626817ae21ae3da600f5779b44"},
- {file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d17c6a415d68cfe1091d3296ba5749d3d8696e42c37fca5d4860c5bf7b729f03"},
- {file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9146579352d7b5f6412735d0f203bbd8d00113a680b66565e205bc605ef81bc6"},
- {file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cdab02a0a941af190df8782aafc591ef3ad08824f97850b015c8c6a8b3877b0b"},
- {file = "coverage-7.6.0-cp38-cp38-win32.whl", hash = "sha256:df423f351b162a702c053d5dddc0fc0ef9a9e27ea3f449781ace5f906b664428"},
- {file = "coverage-7.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:f2501d60d7497fd55e391f423f965bbe9e650e9ffc3c627d5f0ac516026000b8"},
- {file = "coverage-7.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7221f9ac9dad9492cecab6f676b3eaf9185141539d5c9689d13fd6b0d7de840c"},
- {file = "coverage-7.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ddaaa91bfc4477d2871442bbf30a125e8fe6b05da8a0015507bfbf4718228ab2"},
- {file = "coverage-7.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4cbe651f3904e28f3a55d6f371203049034b4ddbce65a54527a3f189ca3b390"},
- {file = "coverage-7.6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:831b476d79408ab6ccfadaaf199906c833f02fdb32c9ab907b1d4aa0713cfa3b"},
- {file = "coverage-7.6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46c3d091059ad0b9c59d1034de74a7f36dcfa7f6d3bde782c49deb42438f2450"},
- {file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4d5fae0a22dc86259dee66f2cc6c1d3e490c4a1214d7daa2a93d07491c5c04b6"},
- {file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:07ed352205574aad067482e53dd606926afebcb5590653121063fbf4e2175166"},
- {file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:49c76cdfa13015c4560702574bad67f0e15ca5a2872c6a125f6327ead2b731dd"},
- {file = "coverage-7.6.0-cp39-cp39-win32.whl", hash = "sha256:482855914928c8175735a2a59c8dc5806cf7d8f032e4820d52e845d1f731dca2"},
- {file = "coverage-7.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:543ef9179bc55edfd895154a51792b01c017c87af0ebaae092720152e19e42ca"},
- {file = "coverage-7.6.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:6fe885135c8a479d3e37a7aae61cbd3a0fb2deccb4dda3c25f92a49189f766d6"},
- {file = "coverage-7.6.0.tar.gz", hash = "sha256:289cc803fa1dc901f84701ac10c9ee873619320f2f9aff38794db4a4a0268d51"},
+ {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"},
+ {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"},
+ {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"},
+ {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"},
+ {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"},
+ {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"},
+ {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"},
+ {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"},
+ {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"},
+ {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"},
+ {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"},
+ {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"},
+ {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"},
+ {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"},
+ {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"},
+ {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"},
+ {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"},
+ {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"},
+ {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"},
+ {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"},
+ {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"},
+ {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"},
+ {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"},
+ {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"},
+ {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"},
+ {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"},
+ {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"},
+ {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"},
+ {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"},
+ {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"},
+ {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"},
+ {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"},
+ {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"},
+ {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"},
+ {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"},
+ {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"},
+ {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"},
+ {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"},
+ {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"},
+ {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"},
+ {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"},
+ {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"},
+ {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"},
+ {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"},
+ {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"},
+ {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"},
+ {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"},
+ {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"},
+ {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"},
+ {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"},
+ {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"},
+ {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"},
+ {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"},
+ {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"},
+ {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"},
+ {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"},
+ {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"},
+ {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"},
+ {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"},
+ {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"},
+ {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"},
+ {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"},
+ {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"},
+ {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"},
+ {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"},
+ {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"},
+ {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"},
+ {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"},
+ {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"},
+ {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"},
+ {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"},
+ {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"},
]
[package.dependencies]
@@ -379,20 +399,21 @@ all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)"
[[package]]
name = "fastapi-cli"
-version = "0.0.4"
+version = "0.0.5"
description = "Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀"
optional = false
python-versions = ">=3.8"
files = [
- {file = "fastapi_cli-0.0.4-py3-none-any.whl", hash = "sha256:a2552f3a7ae64058cdbb530be6fa6dbfc975dc165e4fa66d224c3d396e25e809"},
- {file = "fastapi_cli-0.0.4.tar.gz", hash = "sha256:e2e9ffaffc1f7767f488d6da34b6f5a377751c996f397902eb6abb99a67bde32"},
+ {file = "fastapi_cli-0.0.5-py3-none-any.whl", hash = "sha256:e94d847524648c748a5350673546bbf9bcaeb086b33c24f2e82e021436866a46"},
+ {file = "fastapi_cli-0.0.5.tar.gz", hash = "sha256:d30e1239c6f46fcb95e606f02cdda59a1e2fa778a54b64686b3ff27f6211ff9f"},
]
[package.dependencies]
typer = ">=0.12.3"
+uvicorn = {version = ">=0.15.0", extras = ["standard"]}
[package.extras]
-standard = ["fastapi", "uvicorn[standard] (>=0.15.0)"]
+standard = ["uvicorn[standard] (>=0.15.0)"]
[[package]]
name = "filelock"
@@ -744,44 +765,44 @@ files = [
[[package]]
name = "mypy"
-version = "1.10.1"
+version = "1.11.1"
description = "Optional static typing for Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "mypy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e36f229acfe250dc660790840916eb49726c928e8ce10fbdf90715090fe4ae02"},
- {file = "mypy-1.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:51a46974340baaa4145363b9e051812a2446cf583dfaeba124af966fa44593f7"},
- {file = "mypy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:901c89c2d67bba57aaaca91ccdb659aa3a312de67f23b9dfb059727cce2e2e0a"},
- {file = "mypy-1.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0cd62192a4a32b77ceb31272d9e74d23cd88c8060c34d1d3622db3267679a5d9"},
- {file = "mypy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a2cbc68cb9e943ac0814c13e2452d2046c2f2b23ff0278e26599224cf164e78d"},
- {file = "mypy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bd6f629b67bb43dc0d9211ee98b96d8dabc97b1ad38b9b25f5e4c4d7569a0c6a"},
- {file = "mypy-1.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1bbb3a6f5ff319d2b9d40b4080d46cd639abe3516d5a62c070cf0114a457d84"},
- {file = "mypy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8edd4e9bbbc9d7b79502eb9592cab808585516ae1bcc1446eb9122656c6066f"},
- {file = "mypy-1.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6166a88b15f1759f94a46fa474c7b1b05d134b1b61fca627dd7335454cc9aa6b"},
- {file = "mypy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bb9cd11c01c8606a9d0b83ffa91d0b236a0e91bc4126d9ba9ce62906ada868e"},
- {file = "mypy-1.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d8681909f7b44d0b7b86e653ca152d6dff0eb5eb41694e163c6092124f8246d7"},
- {file = "mypy-1.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:378c03f53f10bbdd55ca94e46ec3ba255279706a6aacaecac52ad248f98205d3"},
- {file = "mypy-1.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bacf8f3a3d7d849f40ca6caea5c055122efe70e81480c8328ad29c55c69e93e"},
- {file = "mypy-1.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:701b5f71413f1e9855566a34d6e9d12624e9e0a8818a5704d74d6b0402e66c04"},
- {file = "mypy-1.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c4c2992f6ea46ff7fce0072642cfb62af7a2484efe69017ed8b095f7b39ef31"},
- {file = "mypy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:604282c886497645ffb87b8f35a57ec773a4a2721161e709a4422c1636ddde5c"},
- {file = "mypy-1.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37fd87cab83f09842653f08de066ee68f1182b9b5282e4634cdb4b407266bade"},
- {file = "mypy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8addf6313777dbb92e9564c5d32ec122bf2c6c39d683ea64de6a1fd98b90fe37"},
- {file = "mypy-1.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cc3ca0a244eb9a5249c7c583ad9a7e881aa5d7b73c35652296ddcdb33b2b9c7"},
- {file = "mypy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:1b3a2ffce52cc4dbaeee4df762f20a2905aa171ef157b82192f2e2f368eec05d"},
- {file = "mypy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe85ed6836165d52ae8b88f99527d3d1b2362e0cb90b005409b8bed90e9059b3"},
- {file = "mypy-1.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2ae450d60d7d020d67ab440c6e3fae375809988119817214440033f26ddf7bf"},
- {file = "mypy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be84c06e6abd72f960ba9a71561c14137a583093ffcf9bbfaf5e613d63fa531"},
- {file = "mypy-1.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2189ff1e39db399f08205e22a797383613ce1cb0cb3b13d8bcf0170e45b96cc3"},
- {file = "mypy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:97a131ee36ac37ce9581f4220311247ab6cba896b4395b9c87af0675a13a755f"},
- {file = "mypy-1.10.1-py3-none-any.whl", hash = "sha256:71d8ac0b906354ebda8ef1673e5fde785936ac1f29ff6987c7483cfbd5a4235a"},
- {file = "mypy-1.10.1.tar.gz", hash = "sha256:1f8f492d7db9e3593ef42d4f115f04e556130f2819ad33ab84551403e97dd4c0"},
+ {file = "mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c"},
+ {file = "mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411"},
+ {file = "mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03"},
+ {file = "mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4"},
+ {file = "mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58"},
+ {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"},
+ {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"},
+ {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"},
+ {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"},
+ {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"},
+ {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"},
+ {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"},
+ {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"},
+ {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"},
+ {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"},
+ {file = "mypy-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2"},
+ {file = "mypy-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b"},
+ {file = "mypy-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0"},
+ {file = "mypy-1.11.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd"},
+ {file = "mypy-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb"},
+ {file = "mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe"},
+ {file = "mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c"},
+ {file = "mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69"},
+ {file = "mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74"},
+ {file = "mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b"},
+ {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"},
+ {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"},
]
[package.dependencies]
mypy-extensions = ">=1.0.0"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
-typing-extensions = ">=4.1.0"
+typing-extensions = ">=4.6.0"
[package.extras]
dmypy = ["psutil (>=4.0)"]
@@ -866,13 +887,13 @@ testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "pre-commit"
-version = "3.7.1"
+version = "3.8.0"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
optional = false
python-versions = ">=3.9"
files = [
- {file = "pre_commit-3.7.1-py2.py3-none-any.whl", hash = "sha256:fae36fd1d7ad7d6a5a1c0b0d5adb2ed1a3bda5a21bf6c3e5372073d7a11cd4c5"},
- {file = "pre_commit-3.7.1.tar.gz", hash = "sha256:8ca3ad567bc78a4972a3f1a477e94a79d4597e8140a6e0b651c5e33899c3654a"},
+ {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"},
+ {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"},
]
[package.dependencies]
@@ -1021,13 +1042,13 @@ windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pytest"
-version = "8.2.2"
+version = "8.3.2"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"},
- {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"},
+ {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"},
+ {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"},
]
[package.dependencies]
@@ -1035,7 +1056,7 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""}
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
iniconfig = "*"
packaging = "*"
-pluggy = ">=1.5,<2.0"
+pluggy = ">=1.5,<2"
tomli = {version = ">=1", markers = "python_version < \"3.11\""}
[package.extras]
@@ -1089,61 +1110,64 @@ dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatc
[[package]]
name = "pyyaml"
-version = "6.0.1"
+version = "6.0.2"
description = "YAML parser and emitter for Python"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.8"
files = [
- {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
- {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
- {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
- {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
- {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
- {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
- {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
- {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
- {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
- {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
- {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
- {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
- {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
- {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
- {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
- {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
- {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
- {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
- {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
- {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
- {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
- {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
- {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
- {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
- {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
- {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
- {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
- {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
- {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
- {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
- {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
- {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
- {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
- {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
- {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
- {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
- {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
- {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
- {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
- {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
- {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
- {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
- {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
- {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
- {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
- {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
- {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
- {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
- {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
- {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
+ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
+ {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
+ {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
+ {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
+ {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
+ {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
+ {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
+ {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
+ {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
+ {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
+ {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
+ {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
+ {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
+ {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
+ {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
+ {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
+ {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
+ {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
+ {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
+ {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
+ {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
+ {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
+ {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
+ {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
+ {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
+ {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
+ {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
+ {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
+ {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
+ {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
+ {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
+ {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
+ {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
+ {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
+ {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
+ {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
+ {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"},
+ {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"},
+ {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"},
+ {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"},
+ {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"},
+ {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"},
+ {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"},
+ {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"},
+ {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"},
+ {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"},
+ {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"},
+ {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"},
+ {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"},
+ {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"},
+ {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"},
+ {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"},
+ {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
]
[[package]]
@@ -1204,29 +1228,29 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]]
name = "ruff"
-version = "0.5.3"
+version = "0.5.7"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
- {file = "ruff-0.5.3-py3-none-linux_armv6l.whl", hash = "sha256:b12424d9db7347fa63c5ed9af010003338c63c629fb9c9c6adb2aa4f5699729b"},
- {file = "ruff-0.5.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8d72c5684bbd4ed304a9a955ee2e67f57b35f6193222ade910cca8a805490e3"},
- {file = "ruff-0.5.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d2fc2cdb85ccac1e816cc9d5d8cedefd93661bd957756d902543af32a6b04a71"},
- {file = "ruff-0.5.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf4bc751240b2fab5d19254571bcacb315c7b0b00bf3c912d52226a82bbec073"},
- {file = "ruff-0.5.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc697ec874fdd7c7ba0a85ec76ab38f8595224868d67f097c5ffc21136e72fcd"},
- {file = "ruff-0.5.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e791d34d3557a3819b3704bc1f087293c821083fa206812842fa363f6018a192"},
- {file = "ruff-0.5.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:76bb5a87fd397520b91a83eae8a2f7985236d42dd9459f09eef58e7f5c1d8316"},
- {file = "ruff-0.5.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8cfc7a26422c78e94f1ec78ec02501bbad2df5834907e75afe474cc6b83a8c1"},
- {file = "ruff-0.5.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96066c4328a49fce2dd40e80f7117987369feec30ab771516cf95f1cc2db923c"},
- {file = "ruff-0.5.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03bfe9ab5bdc0b08470c3b261643ad54ea86edc32b64d1e080892d7953add3ad"},
- {file = "ruff-0.5.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7704582a026fa02cca83efd76671a98ee6eb412c4230209efe5e2a006c06db62"},
- {file = "ruff-0.5.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:08058d077e21b856d32ebf483443390e29dc44d927608dc8f092ff6776519da9"},
- {file = "ruff-0.5.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:77d49484429ed7c7e6e2e75a753f153b7b58f875bdb4158ad85af166a1ec1822"},
- {file = "ruff-0.5.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:642cbff6cbfa38d2566d8db086508d6f472edb136cbfcc4ea65997745368c29e"},
- {file = "ruff-0.5.3-py3-none-win32.whl", hash = "sha256:eafc45dd8bdc37a00b28e68cc038daf3ca8c233d73fea276dcd09defb1352841"},
- {file = "ruff-0.5.3-py3-none-win_amd64.whl", hash = "sha256:cbaec2ddf4f78e5e9ecf5456ea0f496991358a1d883862ed0b9e947e2b6aea93"},
- {file = "ruff-0.5.3-py3-none-win_arm64.whl", hash = "sha256:05fbd2cb404775d6cd7f2ff49504e2d20e13ef95fa203bd1ab22413af70d420b"},
- {file = "ruff-0.5.3.tar.gz", hash = "sha256:2a3eb4f1841771fa5b67a56be9c2d16fd3cc88e378bd86aaeaec2f7e6bcdd0a2"},
+ {file = "ruff-0.5.7-py3-none-linux_armv6l.whl", hash = "sha256:548992d342fc404ee2e15a242cdbea4f8e39a52f2e7752d0e4cbe88d2d2f416a"},
+ {file = "ruff-0.5.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:00cc8872331055ee017c4f1071a8a31ca0809ccc0657da1d154a1d2abac5c0be"},
+ {file = "ruff-0.5.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eaf3d86a1fdac1aec8a3417a63587d93f906c678bb9ed0b796da7b59c1114a1e"},
+ {file = "ruff-0.5.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a01c34400097b06cf8a6e61b35d6d456d5bd1ae6961542de18ec81eaf33b4cb8"},
+ {file = "ruff-0.5.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcc8054f1a717e2213500edaddcf1dbb0abad40d98e1bd9d0ad364f75c763eea"},
+ {file = "ruff-0.5.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f70284e73f36558ef51602254451e50dd6cc479f8b6f8413a95fcb5db4a55fc"},
+ {file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a78ad870ae3c460394fc95437d43deb5c04b5c29297815a2a1de028903f19692"},
+ {file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ccd078c66a8e419475174bfe60a69adb36ce04f8d4e91b006f1329d5cd44bcf"},
+ {file = "ruff-0.5.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e31c9bad4ebf8fdb77b59cae75814440731060a09a0e0077d559a556453acbb"},
+ {file = "ruff-0.5.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d796327eed8e168164346b769dd9a27a70e0298d667b4ecee6877ce8095ec8e"},
+ {file = "ruff-0.5.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a09ea2c3f7778cc635e7f6edf57d566a8ee8f485f3c4454db7771efb692c499"},
+ {file = "ruff-0.5.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a36d8dcf55b3a3bc353270d544fb170d75d2dff41eba5df57b4e0b67a95bb64e"},
+ {file = "ruff-0.5.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9369c218f789eefbd1b8d82a8cf25017b523ac47d96b2f531eba73770971c9e5"},
+ {file = "ruff-0.5.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b88ca3db7eb377eb24fb7c82840546fb7acef75af4a74bd36e9ceb37a890257e"},
+ {file = "ruff-0.5.7-py3-none-win32.whl", hash = "sha256:33d61fc0e902198a3e55719f4be6b375b28f860b09c281e4bdbf783c0566576a"},
+ {file = "ruff-0.5.7-py3-none-win_amd64.whl", hash = "sha256:083bbcbe6fadb93cd86709037acc510f86eed5a314203079df174c40bbbca6b3"},
+ {file = "ruff-0.5.7-py3-none-win_arm64.whl", hash = "sha256:2dca26154ff9571995107221d0aeaad0e75a77b5a682d6236cf89a58c70b76f4"},
+ {file = "ruff-0.5.7.tar.gz", hash = "sha256:8dfc0a458797f5d9fb622dd0efc52d796f23f0a1493a9527f4e49a550ae9a7e5"},
]
[[package]]
@@ -1253,60 +1277,60 @@ files = [
[[package]]
name = "sqlalchemy"
-version = "2.0.31"
+version = "2.0.32"
description = "Database Abstraction Library"
optional = false
python-versions = ">=3.7"
files = [
- {file = "SQLAlchemy-2.0.31-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f2a213c1b699d3f5768a7272de720387ae0122f1becf0901ed6eaa1abd1baf6c"},
- {file = "SQLAlchemy-2.0.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9fea3d0884e82d1e33226935dac990b967bef21315cbcc894605db3441347443"},
- {file = "SQLAlchemy-2.0.31-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ad7f221d8a69d32d197e5968d798217a4feebe30144986af71ada8c548e9fa"},
- {file = "SQLAlchemy-2.0.31-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2bee229715b6366f86a95d497c347c22ddffa2c7c96143b59a2aa5cc9eebbc"},
- {file = "SQLAlchemy-2.0.31-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cd5b94d4819c0c89280b7c6109c7b788a576084bf0a480ae17c227b0bc41e109"},
- {file = "SQLAlchemy-2.0.31-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:750900a471d39a7eeba57580b11983030517a1f512c2cb287d5ad0fcf3aebd58"},
- {file = "SQLAlchemy-2.0.31-cp310-cp310-win32.whl", hash = "sha256:7bd112be780928c7f493c1a192cd8c5fc2a2a7b52b790bc5a84203fb4381c6be"},
- {file = "SQLAlchemy-2.0.31-cp310-cp310-win_amd64.whl", hash = "sha256:5a48ac4d359f058474fadc2115f78a5cdac9988d4f99eae44917f36aa1476327"},
- {file = "SQLAlchemy-2.0.31-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f68470edd70c3ac3b6cd5c2a22a8daf18415203ca1b036aaeb9b0fb6f54e8298"},
- {file = "SQLAlchemy-2.0.31-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e2c38c2a4c5c634fe6c3c58a789712719fa1bf9b9d6ff5ebfce9a9e5b89c1ca"},
- {file = "SQLAlchemy-2.0.31-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd15026f77420eb2b324dcb93551ad9c5f22fab2c150c286ef1dc1160f110203"},
- {file = "SQLAlchemy-2.0.31-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2196208432deebdfe3b22185d46b08f00ac9d7b01284e168c212919891289396"},
- {file = "SQLAlchemy-2.0.31-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:352b2770097f41bff6029b280c0e03b217c2dcaddc40726f8f53ed58d8a85da4"},
- {file = "SQLAlchemy-2.0.31-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:56d51ae825d20d604583f82c9527d285e9e6d14f9a5516463d9705dab20c3740"},
- {file = "SQLAlchemy-2.0.31-cp311-cp311-win32.whl", hash = "sha256:6e2622844551945db81c26a02f27d94145b561f9d4b0c39ce7bfd2fda5776dac"},
- {file = "SQLAlchemy-2.0.31-cp311-cp311-win_amd64.whl", hash = "sha256:ccaf1b0c90435b6e430f5dd30a5aede4764942a695552eb3a4ab74ed63c5b8d3"},
- {file = "SQLAlchemy-2.0.31-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3b74570d99126992d4b0f91fb87c586a574a5872651185de8297c6f90055ae42"},
- {file = "SQLAlchemy-2.0.31-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f77c4f042ad493cb8595e2f503c7a4fe44cd7bd59c7582fd6d78d7e7b8ec52c"},
- {file = "SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd1591329333daf94467e699e11015d9c944f44c94d2091f4ac493ced0119449"},
- {file = "SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74afabeeff415e35525bf7a4ecdab015f00e06456166a2eba7590e49f8db940e"},
- {file = "SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b9c01990d9015df2c6f818aa8f4297d42ee71c9502026bb074e713d496e26b67"},
- {file = "SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66f63278db425838b3c2b1c596654b31939427016ba030e951b292e32b99553e"},
- {file = "SQLAlchemy-2.0.31-cp312-cp312-win32.whl", hash = "sha256:0b0f658414ee4e4b8cbcd4a9bb0fd743c5eeb81fc858ca517217a8013d282c96"},
- {file = "SQLAlchemy-2.0.31-cp312-cp312-win_amd64.whl", hash = "sha256:fa4b1af3e619b5b0b435e333f3967612db06351217c58bfb50cee5f003db2a5a"},
- {file = "SQLAlchemy-2.0.31-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f43e93057cf52a227eda401251c72b6fbe4756f35fa6bfebb5d73b86881e59b0"},
- {file = "SQLAlchemy-2.0.31-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d337bf94052856d1b330d5fcad44582a30c532a2463776e1651bd3294ee7e58b"},
- {file = "SQLAlchemy-2.0.31-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c06fb43a51ccdff3b4006aafee9fcf15f63f23c580675f7734245ceb6b6a9e05"},
- {file = "SQLAlchemy-2.0.31-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:b6e22630e89f0e8c12332b2b4c282cb01cf4da0d26795b7eae16702a608e7ca1"},
- {file = "SQLAlchemy-2.0.31-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:79a40771363c5e9f3a77f0e28b3302801db08040928146e6808b5b7a40749c88"},
- {file = "SQLAlchemy-2.0.31-cp37-cp37m-win32.whl", hash = "sha256:501ff052229cb79dd4c49c402f6cb03b5a40ae4771efc8bb2bfac9f6c3d3508f"},
- {file = "SQLAlchemy-2.0.31-cp37-cp37m-win_amd64.whl", hash = "sha256:597fec37c382a5442ffd471f66ce12d07d91b281fd474289356b1a0041bdf31d"},
- {file = "SQLAlchemy-2.0.31-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dc6d69f8829712a4fd799d2ac8d79bdeff651c2301b081fd5d3fe697bd5b4ab9"},
- {file = "SQLAlchemy-2.0.31-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:23b9fbb2f5dd9e630db70fbe47d963c7779e9c81830869bd7d137c2dc1ad05fb"},
- {file = "SQLAlchemy-2.0.31-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a21c97efcbb9f255d5c12a96ae14da873233597dfd00a3a0c4ce5b3e5e79704"},
- {file = "SQLAlchemy-2.0.31-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26a6a9837589c42b16693cf7bf836f5d42218f44d198f9343dd71d3164ceeeac"},
- {file = "SQLAlchemy-2.0.31-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc251477eae03c20fae8db9c1c23ea2ebc47331bcd73927cdcaecd02af98d3c3"},
- {file = "SQLAlchemy-2.0.31-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2fd17e3bb8058359fa61248c52c7b09a97cf3c820e54207a50af529876451808"},
- {file = "SQLAlchemy-2.0.31-cp38-cp38-win32.whl", hash = "sha256:c76c81c52e1e08f12f4b6a07af2b96b9b15ea67ccdd40ae17019f1c373faa227"},
- {file = "SQLAlchemy-2.0.31-cp38-cp38-win_amd64.whl", hash = "sha256:4b600e9a212ed59355813becbcf282cfda5c93678e15c25a0ef896b354423238"},
- {file = "SQLAlchemy-2.0.31-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b6cf796d9fcc9b37011d3f9936189b3c8074a02a4ed0c0fbbc126772c31a6d4"},
- {file = "SQLAlchemy-2.0.31-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:78fe11dbe37d92667c2c6e74379f75746dc947ee505555a0197cfba9a6d4f1a4"},
- {file = "SQLAlchemy-2.0.31-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc47dc6185a83c8100b37acda27658fe4dbd33b7d5e7324111f6521008ab4fe"},
- {file = "SQLAlchemy-2.0.31-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a41514c1a779e2aa9a19f67aaadeb5cbddf0b2b508843fcd7bafdf4c6864005"},
- {file = "SQLAlchemy-2.0.31-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:afb6dde6c11ea4525318e279cd93c8734b795ac8bb5dda0eedd9ebaca7fa23f1"},
- {file = "SQLAlchemy-2.0.31-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3f9faef422cfbb8fd53716cd14ba95e2ef655400235c3dfad1b5f467ba179c8c"},
- {file = "SQLAlchemy-2.0.31-cp39-cp39-win32.whl", hash = "sha256:fc6b14e8602f59c6ba893980bea96571dd0ed83d8ebb9c4479d9ed5425d562e9"},
- {file = "SQLAlchemy-2.0.31-cp39-cp39-win_amd64.whl", hash = "sha256:3cb8a66b167b033ec72c3812ffc8441d4e9f5f78f5e31e54dcd4c90a4ca5bebc"},
- {file = "SQLAlchemy-2.0.31-py3-none-any.whl", hash = "sha256:69f3e3c08867a8e4856e92d7afb618b95cdee18e0bc1647b77599722c9a28911"},
- {file = "SQLAlchemy-2.0.31.tar.gz", hash = "sha256:b607489dd4a54de56984a0c7656247504bd5523d9d0ba799aef59d4add009484"},
+ {file = "SQLAlchemy-2.0.32-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0c9045ecc2e4db59bfc97b20516dfdf8e41d910ac6fb667ebd3a79ea54084619"},
+ {file = "SQLAlchemy-2.0.32-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1467940318e4a860afd546ef61fefb98a14d935cd6817ed07a228c7f7c62f389"},
+ {file = "SQLAlchemy-2.0.32-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5954463675cb15db8d4b521f3566a017c8789222b8316b1e6934c811018ee08b"},
+ {file = "SQLAlchemy-2.0.32-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167e7497035c303ae50651b351c28dc22a40bb98fbdb8468cdc971821b1ae533"},
+ {file = "SQLAlchemy-2.0.32-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b27dfb676ac02529fb6e343b3a482303f16e6bc3a4d868b73935b8792edb52d0"},
+ {file = "SQLAlchemy-2.0.32-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bf2360a5e0f7bd75fa80431bf8ebcfb920c9f885e7956c7efde89031695cafb8"},
+ {file = "SQLAlchemy-2.0.32-cp310-cp310-win32.whl", hash = "sha256:306fe44e754a91cd9d600a6b070c1f2fadbb4a1a257b8781ccf33c7067fd3e4d"},
+ {file = "SQLAlchemy-2.0.32-cp310-cp310-win_amd64.whl", hash = "sha256:99db65e6f3ab42e06c318f15c98f59a436f1c78179e6a6f40f529c8cc7100b22"},
+ {file = "SQLAlchemy-2.0.32-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21b053be28a8a414f2ddd401f1be8361e41032d2ef5884b2f31d31cb723e559f"},
+ {file = "SQLAlchemy-2.0.32-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b178e875a7a25b5938b53b006598ee7645172fccafe1c291a706e93f48499ff5"},
+ {file = "SQLAlchemy-2.0.32-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723a40ee2cc7ea653645bd4cf024326dea2076673fc9d3d33f20f6c81db83e1d"},
+ {file = "SQLAlchemy-2.0.32-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:295ff8689544f7ee7e819529633d058bd458c1fd7f7e3eebd0f9268ebc56c2a0"},
+ {file = "SQLAlchemy-2.0.32-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49496b68cd190a147118af585173ee624114dfb2e0297558c460ad7495f9dfe2"},
+ {file = "SQLAlchemy-2.0.32-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:acd9b73c5c15f0ec5ce18128b1fe9157ddd0044abc373e6ecd5ba376a7e5d961"},
+ {file = "SQLAlchemy-2.0.32-cp311-cp311-win32.whl", hash = "sha256:9365a3da32dabd3e69e06b972b1ffb0c89668994c7e8e75ce21d3e5e69ddef28"},
+ {file = "SQLAlchemy-2.0.32-cp311-cp311-win_amd64.whl", hash = "sha256:8bd63d051f4f313b102a2af1cbc8b80f061bf78f3d5bd0843ff70b5859e27924"},
+ {file = "SQLAlchemy-2.0.32-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6bab3db192a0c35e3c9d1560eb8332463e29e5507dbd822e29a0a3c48c0a8d92"},
+ {file = "SQLAlchemy-2.0.32-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:19d98f4f58b13900d8dec4ed09dd09ef292208ee44cc9c2fe01c1f0a2fe440e9"},
+ {file = "SQLAlchemy-2.0.32-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd33c61513cb1b7371fd40cf221256456d26a56284e7d19d1f0b9f1eb7dd7e8"},
+ {file = "SQLAlchemy-2.0.32-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d6ba0497c1d066dd004e0f02a92426ca2df20fac08728d03f67f6960271feec"},
+ {file = "SQLAlchemy-2.0.32-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2b6be53e4fde0065524f1a0a7929b10e9280987b320716c1509478b712a7688c"},
+ {file = "SQLAlchemy-2.0.32-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:916a798f62f410c0b80b63683c8061f5ebe237b0f4ad778739304253353bc1cb"},
+ {file = "SQLAlchemy-2.0.32-cp312-cp312-win32.whl", hash = "sha256:31983018b74908ebc6c996a16ad3690301a23befb643093fcfe85efd292e384d"},
+ {file = "SQLAlchemy-2.0.32-cp312-cp312-win_amd64.whl", hash = "sha256:4363ed245a6231f2e2957cccdda3c776265a75851f4753c60f3004b90e69bfeb"},
+ {file = "SQLAlchemy-2.0.32-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b8afd5b26570bf41c35c0121801479958b4446751a3971fb9a480c1afd85558e"},
+ {file = "SQLAlchemy-2.0.32-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c750987fc876813f27b60d619b987b057eb4896b81117f73bb8d9918c14f1cad"},
+ {file = "SQLAlchemy-2.0.32-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada0102afff4890f651ed91120c1120065663506b760da4e7823913ebd3258be"},
+ {file = "SQLAlchemy-2.0.32-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:78c03d0f8a5ab4f3034c0e8482cfcc415a3ec6193491cfa1c643ed707d476f16"},
+ {file = "SQLAlchemy-2.0.32-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:3bd1cae7519283ff525e64645ebd7a3e0283f3c038f461ecc1c7b040a0c932a1"},
+ {file = "SQLAlchemy-2.0.32-cp37-cp37m-win32.whl", hash = "sha256:01438ebcdc566d58c93af0171c74ec28efe6a29184b773e378a385e6215389da"},
+ {file = "SQLAlchemy-2.0.32-cp37-cp37m-win_amd64.whl", hash = "sha256:4979dc80fbbc9d2ef569e71e0896990bc94df2b9fdbd878290bd129b65ab579c"},
+ {file = "SQLAlchemy-2.0.32-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c742be912f57586ac43af38b3848f7688863a403dfb220193a882ea60e1ec3a"},
+ {file = "SQLAlchemy-2.0.32-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:62e23d0ac103bcf1c5555b6c88c114089587bc64d048fef5bbdb58dfd26f96da"},
+ {file = "SQLAlchemy-2.0.32-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:251f0d1108aab8ea7b9aadbd07fb47fb8e3a5838dde34aa95a3349876b5a1f1d"},
+ {file = "SQLAlchemy-2.0.32-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ef18a84e5116340e38eca3e7f9eeaaef62738891422e7c2a0b80feab165905f"},
+ {file = "SQLAlchemy-2.0.32-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3eb6a97a1d39976f360b10ff208c73afb6a4de86dd2a6212ddf65c4a6a2347d5"},
+ {file = "SQLAlchemy-2.0.32-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0c1c9b673d21477cec17ab10bc4decb1322843ba35b481585facd88203754fc5"},
+ {file = "SQLAlchemy-2.0.32-cp38-cp38-win32.whl", hash = "sha256:c41a2b9ca80ee555decc605bd3c4520cc6fef9abde8fd66b1cf65126a6922d65"},
+ {file = "SQLAlchemy-2.0.32-cp38-cp38-win_amd64.whl", hash = "sha256:8a37e4d265033c897892279e8adf505c8b6b4075f2b40d77afb31f7185cd6ecd"},
+ {file = "SQLAlchemy-2.0.32-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:52fec964fba2ef46476312a03ec8c425956b05c20220a1a03703537824b5e8e1"},
+ {file = "SQLAlchemy-2.0.32-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:328429aecaba2aee3d71e11f2477c14eec5990fb6d0e884107935f7fb6001632"},
+ {file = "SQLAlchemy-2.0.32-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85a01b5599e790e76ac3fe3aa2f26e1feba56270023d6afd5550ed63c68552b3"},
+ {file = "SQLAlchemy-2.0.32-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf04784797dcdf4c0aa952c8d234fa01974c4729db55c45732520ce12dd95b4"},
+ {file = "SQLAlchemy-2.0.32-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4488120becf9b71b3ac718f4138269a6be99a42fe023ec457896ba4f80749525"},
+ {file = "SQLAlchemy-2.0.32-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:14e09e083a5796d513918a66f3d6aedbc131e39e80875afe81d98a03312889e6"},
+ {file = "SQLAlchemy-2.0.32-cp39-cp39-win32.whl", hash = "sha256:0d322cc9c9b2154ba7e82f7bf25ecc7c36fbe2d82e2933b3642fc095a52cfc78"},
+ {file = "SQLAlchemy-2.0.32-cp39-cp39-win_amd64.whl", hash = "sha256:7dd8583df2f98dea28b5cd53a1beac963f4f9d087888d75f22fcc93a07cf8d84"},
+ {file = "SQLAlchemy-2.0.32-py3-none-any.whl", hash = "sha256:e567a8793a692451f706b363ccf3c45e056b67d90ead58c3bc9471af5d212202"},
+ {file = "SQLAlchemy-2.0.32.tar.gz", hash = "sha256:c1b88cc8b02b6a5f0efb0345a03672d4c897dc7d92585176f88c67346f565ea8"},
]
[package.dependencies]
@@ -1413,13 +1437,13 @@ typing-extensions = ">=3.7.4.3"
[[package]]
name = "types-pyyaml"
-version = "6.0.12.20240311"
+version = "6.0.12.20240808"
description = "Typing stubs for PyYAML"
optional = false
python-versions = ">=3.8"
files = [
- {file = "types-PyYAML-6.0.12.20240311.tar.gz", hash = "sha256:a9e0f0f88dc835739b0c1ca51ee90d04ca2a897a71af79de9aec5f38cb0a5342"},
- {file = "types_PyYAML-6.0.12.20240311-py3-none-any.whl", hash = "sha256:b845b06a1c7e54b8e5b4c683043de0d9caf205e7434b3edc678ff2411979b8f6"},
+ {file = "types-PyYAML-6.0.12.20240808.tar.gz", hash = "sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af"},
+ {file = "types_PyYAML-6.0.12.20240808-py3-none-any.whl", hash = "sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35"},
]
[[package]]
@@ -1466,13 +1490,13 @@ zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "uvicorn"
-version = "0.30.1"
+version = "0.30.6"
description = "The lightning-fast ASGI server."
optional = false
python-versions = ">=3.8"
files = [
- {file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"},
- {file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"},
+ {file = "uvicorn-0.30.6-py3-none-any.whl", hash = "sha256:65fd46fe3fda5bdc1b03b94eb634923ff18cd35b2f084813ea79d1f103f711b5"},
+ {file = "uvicorn-0.30.6.tar.gz", hash = "sha256:4b15decdda1e72be08209e860a1e10e92439ad5b97cf44cc945fcbee66fc5788"},
]
[package.dependencies]
@@ -1556,86 +1580,98 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess
[[package]]
name = "watchfiles"
-version = "0.22.0"
+version = "0.23.0"
description = "Simple, modern and high performance file watching and code reload in python."
optional = false
python-versions = ">=3.8"
files = [
- {file = "watchfiles-0.22.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:da1e0a8caebf17976e2ffd00fa15f258e14749db5e014660f53114b676e68538"},
- {file = "watchfiles-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61af9efa0733dc4ca462347becb82e8ef4945aba5135b1638bfc20fad64d4f0e"},
- {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d9188979a58a096b6f8090e816ccc3f255f137a009dd4bbec628e27696d67c1"},
- {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2bdadf6b90c099ca079d468f976fd50062905d61fae183f769637cb0f68ba59a"},
- {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:067dea90c43bf837d41e72e546196e674f68c23702d3ef80e4e816937b0a3ffd"},
- {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbf8a20266136507abf88b0df2328e6a9a7c7309e8daff124dda3803306a9fdb"},
- {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1235c11510ea557fe21be5d0e354bae2c655a8ee6519c94617fe63e05bca4171"},
- {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2444dc7cb9d8cc5ab88ebe792a8d75709d96eeef47f4c8fccb6df7c7bc5be71"},
- {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c5af2347d17ab0bd59366db8752d9e037982e259cacb2ba06f2c41c08af02c39"},
- {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9624a68b96c878c10437199d9a8b7d7e542feddda8d5ecff58fdc8e67b460848"},
- {file = "watchfiles-0.22.0-cp310-none-win32.whl", hash = "sha256:4b9f2a128a32a2c273d63eb1fdbf49ad64852fc38d15b34eaa3f7ca2f0d2b797"},
- {file = "watchfiles-0.22.0-cp310-none-win_amd64.whl", hash = "sha256:2627a91e8110b8de2406d8b2474427c86f5a62bf7d9ab3654f541f319ef22bcb"},
- {file = "watchfiles-0.22.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8c39987a1397a877217be1ac0fb1d8b9f662c6077b90ff3de2c05f235e6a8f96"},
- {file = "watchfiles-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a927b3034d0672f62fb2ef7ea3c9fc76d063c4b15ea852d1db2dc75fe2c09696"},
- {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052d668a167e9fc345c24203b104c313c86654dd6c0feb4b8a6dfc2462239249"},
- {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e45fb0d70dda1623a7045bd00c9e036e6f1f6a85e4ef2c8ae602b1dfadf7550"},
- {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c49b76a78c156979759d759339fb62eb0549515acfe4fd18bb151cc07366629c"},
- {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4a65474fd2b4c63e2c18ac67a0c6c66b82f4e73e2e4d940f837ed3d2fd9d4da"},
- {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc0cba54f47c660d9fa3218158b8963c517ed23bd9f45fe463f08262a4adae1"},
- {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ebe84a035993bb7668f58a0ebf998174fb723a39e4ef9fce95baabb42b787f"},
- {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e0f0a874231e2839abbf473256efffe577d6ee2e3bfa5b540479e892e47c172d"},
- {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:213792c2cd3150b903e6e7884d40660e0bcec4465e00563a5fc03f30ea9c166c"},
- {file = "watchfiles-0.22.0-cp311-none-win32.whl", hash = "sha256:b44b70850f0073b5fcc0b31ede8b4e736860d70e2dbf55701e05d3227a154a67"},
- {file = "watchfiles-0.22.0-cp311-none-win_amd64.whl", hash = "sha256:00f39592cdd124b4ec5ed0b1edfae091567c72c7da1487ae645426d1b0ffcad1"},
- {file = "watchfiles-0.22.0-cp311-none-win_arm64.whl", hash = "sha256:3218a6f908f6a276941422b035b511b6d0d8328edd89a53ae8c65be139073f84"},
- {file = "watchfiles-0.22.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c7b978c384e29d6c7372209cbf421d82286a807bbcdeb315427687f8371c340a"},
- {file = "watchfiles-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd4c06100bce70a20c4b81e599e5886cf504c9532951df65ad1133e508bf20be"},
- {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:425440e55cd735386ec7925f64d5dde392e69979d4c8459f6bb4e920210407f2"},
- {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68fe0c4d22332d7ce53ad094622b27e67440dacefbaedd29e0794d26e247280c"},
- {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8a31bfd98f846c3c284ba694c6365620b637debdd36e46e1859c897123aa232"},
- {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc2e8fe41f3cac0660197d95216c42910c2b7e9c70d48e6d84e22f577d106fc1"},
- {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b7cc10261c2786c41d9207193a85c1db1b725cf87936df40972aab466179b6"},
- {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28585744c931576e535860eaf3f2c0ec7deb68e3b9c5a85ca566d69d36d8dd27"},
- {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00095dd368f73f8f1c3a7982a9801190cc88a2f3582dd395b289294f8975172b"},
- {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:52fc9b0dbf54d43301a19b236b4a4614e610605f95e8c3f0f65c3a456ffd7d35"},
- {file = "watchfiles-0.22.0-cp312-none-win32.whl", hash = "sha256:581f0a051ba7bafd03e17127735d92f4d286af941dacf94bcf823b101366249e"},
- {file = "watchfiles-0.22.0-cp312-none-win_amd64.whl", hash = "sha256:aec83c3ba24c723eac14225194b862af176d52292d271c98820199110e31141e"},
- {file = "watchfiles-0.22.0-cp312-none-win_arm64.whl", hash = "sha256:c668228833c5619f6618699a2c12be057711b0ea6396aeaece4ded94184304ea"},
- {file = "watchfiles-0.22.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d47e9ef1a94cc7a536039e46738e17cce058ac1593b2eccdede8bf72e45f372a"},
- {file = "watchfiles-0.22.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28f393c1194b6eaadcdd8f941307fc9bbd7eb567995232c830f6aef38e8a6e88"},
- {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd64f3a4db121bc161644c9e10a9acdb836853155a108c2446db2f5ae1778c3d"},
- {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2abeb79209630da981f8ebca30a2c84b4c3516a214451bfc5f106723c5f45843"},
- {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cc382083afba7918e32d5ef12321421ef43d685b9a67cc452a6e6e18920890e"},
- {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d048ad5d25b363ba1d19f92dcf29023988524bee6f9d952130b316c5802069cb"},
- {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:103622865599f8082f03af4214eaff90e2426edff5e8522c8f9e93dc17caee13"},
- {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e1f3cf81f1f823e7874ae563457828e940d75573c8fbf0ee66818c8b6a9099"},
- {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8597b6f9dc410bdafc8bb362dac1cbc9b4684a8310e16b1ff5eee8725d13dcd6"},
- {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0b04a2cbc30e110303baa6d3ddce8ca3664bc3403be0f0ad513d1843a41c97d1"},
- {file = "watchfiles-0.22.0-cp38-none-win32.whl", hash = "sha256:b610fb5e27825b570554d01cec427b6620ce9bd21ff8ab775fc3a32f28bba63e"},
- {file = "watchfiles-0.22.0-cp38-none-win_amd64.whl", hash = "sha256:fe82d13461418ca5e5a808a9e40f79c1879351fcaeddbede094028e74d836e86"},
- {file = "watchfiles-0.22.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3973145235a38f73c61474d56ad6199124e7488822f3a4fc97c72009751ae3b0"},
- {file = "watchfiles-0.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:280a4afbc607cdfc9571b9904b03a478fc9f08bbeec382d648181c695648202f"},
- {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a0d883351a34c01bd53cfa75cd0292e3f7e268bacf2f9e33af4ecede7e21d1d"},
- {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9165bcab15f2b6d90eedc5c20a7f8a03156b3773e5fb06a790b54ccecdb73385"},
- {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc1b9b56f051209be458b87edb6856a449ad3f803315d87b2da4c93b43a6fe72"},
- {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc1fc25a1dedf2dd952909c8e5cb210791e5f2d9bc5e0e8ebc28dd42fed7562"},
- {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc92d2d2706d2b862ce0568b24987eba51e17e14b79a1abcd2edc39e48e743c8"},
- {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97b94e14b88409c58cdf4a8eaf0e67dfd3ece7e9ce7140ea6ff48b0407a593ec"},
- {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:96eec15e5ea7c0b6eb5bfffe990fc7c6bd833acf7e26704eb18387fb2f5fd087"},
- {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:28324d6b28bcb8d7c1041648d7b63be07a16db5510bea923fc80b91a2a6cbed6"},
- {file = "watchfiles-0.22.0-cp39-none-win32.whl", hash = "sha256:8c3e3675e6e39dc59b8fe5c914a19d30029e36e9f99468dddffd432d8a7b1c93"},
- {file = "watchfiles-0.22.0-cp39-none-win_amd64.whl", hash = "sha256:25c817ff2a86bc3de3ed2df1703e3d24ce03479b27bb4527c57e722f8554d971"},
- {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b810a2c7878cbdecca12feae2c2ae8af59bea016a78bc353c184fa1e09f76b68"},
- {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7e1f9c5d1160d03b93fc4b68a0aeb82fe25563e12fbcdc8507f8434ab6f823c"},
- {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:030bc4e68d14bcad2294ff68c1ed87215fbd9a10d9dea74e7cfe8a17869785ab"},
- {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace7d060432acde5532e26863e897ee684780337afb775107c0a90ae8dbccfd2"},
- {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5834e1f8b71476a26df97d121c0c0ed3549d869124ed2433e02491553cb468c2"},
- {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0bc3b2f93a140df6806c8467c7f51ed5e55a931b031b5c2d7ff6132292e803d6"},
- {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fdebb655bb1ba0122402352b0a4254812717a017d2dc49372a1d47e24073795"},
- {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8e0aa0e8cc2a43561e0184c0513e291ca891db13a269d8d47cb9841ced7c71"},
- {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2f350cbaa4bb812314af5dab0eb8d538481e2e2279472890864547f3fe2281ed"},
- {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7a74436c415843af2a769b36bf043b6ccbc0f8d784814ba3d42fc961cdb0a9dc"},
- {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00ad0bcd399503a84cc688590cdffbe7a991691314dde5b57b3ed50a41319a31"},
- {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a44e9481afc7a5ee3291b09c419abab93b7e9c306c9ef9108cb76728ca58d2"},
- {file = "watchfiles-0.22.0.tar.gz", hash = "sha256:988e981aaab4f3955209e7e28c7794acdb690be1efa7f16f8ea5aba7ffdadacb"},
+ {file = "watchfiles-0.23.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:bee8ce357a05c20db04f46c22be2d1a2c6a8ed365b325d08af94358e0688eeb4"},
+ {file = "watchfiles-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ccd3011cc7ee2f789af9ebe04745436371d36afe610028921cab9f24bb2987b"},
+ {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb02d41c33be667e6135e6686f1bb76104c88a312a18faa0ef0262b5bf7f1a0f"},
+ {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cf12ac34c444362f3261fb3ff548f0037ddd4c5bb85f66c4be30d2936beb3c5"},
+ {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0b2c25040a3c0ce0e66c7779cc045fdfbbb8d59e5aabfe033000b42fe44b53e"},
+ {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecf2be4b9eece4f3da8ba5f244b9e51932ebc441c0867bd6af46a3d97eb068d6"},
+ {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40cb8fa00028908211eb9f8d47744dca21a4be6766672e1ff3280bee320436f1"},
+ {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f48c917ffd36ff9a5212614c2d0d585fa8b064ca7e66206fb5c095015bc8207"},
+ {file = "watchfiles-0.23.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9d183e3888ada88185ab17064079c0db8c17e32023f5c278d7bf8014713b1b5b"},
+ {file = "watchfiles-0.23.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9837edf328b2805346f91209b7e660f65fb0e9ca18b7459d075d58db082bf981"},
+ {file = "watchfiles-0.23.0-cp310-none-win32.whl", hash = "sha256:296e0b29ab0276ca59d82d2da22cbbdb39a23eed94cca69aed274595fb3dfe42"},
+ {file = "watchfiles-0.23.0-cp310-none-win_amd64.whl", hash = "sha256:4ea756e425ab2dfc8ef2a0cb87af8aa7ef7dfc6fc46c6f89bcf382121d4fff75"},
+ {file = "watchfiles-0.23.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:e397b64f7aaf26915bf2ad0f1190f75c855d11eb111cc00f12f97430153c2eab"},
+ {file = "watchfiles-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b4ac73b02ca1824ec0a7351588241fd3953748d3774694aa7ddb5e8e46aef3e3"},
+ {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130a896d53b48a1cecccfa903f37a1d87dbb74295305f865a3e816452f6e49e4"},
+ {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c5e7803a65eb2d563c73230e9d693c6539e3c975ccfe62526cadde69f3fda0cf"},
+ {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1aa4cc85202956d1a65c88d18c7b687b8319dbe6b1aec8969784ef7a10e7d1a"},
+ {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87f889f6e58849ddb7c5d2cb19e2e074917ed1c6e3ceca50405775166492cca8"},
+ {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37fd826dac84c6441615aa3f04077adcc5cac7194a021c9f0d69af20fb9fa788"},
+ {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee7db6e36e7a2c15923072e41ea24d9a0cf39658cb0637ecc9307b09d28827e1"},
+ {file = "watchfiles-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2368c5371c17fdcb5a2ea71c5c9d49f9b128821bfee69503cc38eae00feb3220"},
+ {file = "watchfiles-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:857af85d445b9ba9178db95658c219dbd77b71b8264e66836a6eba4fbf49c320"},
+ {file = "watchfiles-0.23.0-cp311-none-win32.whl", hash = "sha256:1d636c8aeb28cdd04a4aa89030c4b48f8b2954d8483e5f989774fa441c0ed57b"},
+ {file = "watchfiles-0.23.0-cp311-none-win_amd64.whl", hash = "sha256:46f1d8069a95885ca529645cdbb05aea5837d799965676e1b2b1f95a4206313e"},
+ {file = "watchfiles-0.23.0-cp311-none-win_arm64.whl", hash = "sha256:e495ed2a7943503766c5d1ff05ae9212dc2ce1c0e30a80d4f0d84889298fa304"},
+ {file = "watchfiles-0.23.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1db691bad0243aed27c8354b12d60e8e266b75216ae99d33e927ff5238d270b5"},
+ {file = "watchfiles-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:62d2b18cb1edaba311fbbfe83fb5e53a858ba37cacb01e69bc20553bb70911b8"},
+ {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e087e8fdf1270d000913c12e6eca44edd02aad3559b3e6b8ef00f0ce76e0636f"},
+ {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd41d5c72417b87c00b1b635738f3c283e737d75c5fa5c3e1c60cd03eac3af77"},
+ {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e5f3ca0ff47940ce0a389457b35d6df601c317c1e1a9615981c474452f98de1"},
+ {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6991e3a78f642368b8b1b669327eb6751439f9f7eaaa625fae67dd6070ecfa0b"},
+ {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f7252f52a09f8fa5435dc82b6af79483118ce6bd51eb74e6269f05ee22a7b9f"},
+ {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e01bcb8d767c58865207a6c2f2792ad763a0fe1119fb0a430f444f5b02a5ea0"},
+ {file = "watchfiles-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8e56fbcdd27fce061854ddec99e015dd779cae186eb36b14471fc9ae713b118c"},
+ {file = "watchfiles-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bd3e2d64500a6cad28bcd710ee6269fbeb2e5320525acd0cfab5f269ade68581"},
+ {file = "watchfiles-0.23.0-cp312-none-win32.whl", hash = "sha256:eb99c954291b2fad0eff98b490aa641e128fbc4a03b11c8a0086de8b7077fb75"},
+ {file = "watchfiles-0.23.0-cp312-none-win_amd64.whl", hash = "sha256:dccc858372a56080332ea89b78cfb18efb945da858fabeb67f5a44fa0bcb4ebb"},
+ {file = "watchfiles-0.23.0-cp312-none-win_arm64.whl", hash = "sha256:6c21a5467f35c61eafb4e394303720893066897fca937bade5b4f5877d350ff8"},
+ {file = "watchfiles-0.23.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ba31c32f6b4dceeb2be04f717811565159617e28d61a60bb616b6442027fd4b9"},
+ {file = "watchfiles-0.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85042ab91814fca99cec4678fc063fb46df4cbb57b4835a1cc2cb7a51e10250e"},
+ {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24655e8c1c9c114005c3868a3d432c8aa595a786b8493500071e6a52f3d09217"},
+ {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b1a950ab299a4a78fd6369a97b8763732bfb154fdb433356ec55a5bce9515c1"},
+ {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8d3c5cd327dd6ce0edfc94374fb5883d254fe78a5e9d9dfc237a1897dc73cd1"},
+ {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ff785af8bacdf0be863ec0c428e3288b817e82f3d0c1d652cd9c6d509020dd0"},
+ {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02b7ba9d4557149410747353e7325010d48edcfe9d609a85cb450f17fd50dc3d"},
+ {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a1b05c0afb2cd2f48c1ed2ae5487b116e34b93b13074ed3c22ad5c743109f0"},
+ {file = "watchfiles-0.23.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:109a61763e7318d9f821b878589e71229f97366fa6a5c7720687d367f3ab9eef"},
+ {file = "watchfiles-0.23.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9f8e6bb5ac007d4a4027b25f09827ed78cbbd5b9700fd6c54429278dacce05d1"},
+ {file = "watchfiles-0.23.0-cp313-none-win32.whl", hash = "sha256:f46c6f0aec8d02a52d97a583782d9af38c19a29900747eb048af358a9c1d8e5b"},
+ {file = "watchfiles-0.23.0-cp313-none-win_amd64.whl", hash = "sha256:f449afbb971df5c6faeb0a27bca0427d7b600dd8f4a068492faec18023f0dcff"},
+ {file = "watchfiles-0.23.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:2dddc2487d33e92f8b6222b5fb74ae2cfde5e8e6c44e0248d24ec23befdc5366"},
+ {file = "watchfiles-0.23.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e75695cc952e825fa3e0684a7f4a302f9128721f13eedd8dbd3af2ba450932b8"},
+ {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2537ef60596511df79b91613a5bb499b63f46f01a11a81b0a2b0dedf645d0a9c"},
+ {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:20b423b58f5fdde704a226b598a2d78165fe29eb5621358fe57ea63f16f165c4"},
+ {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b98732ec893975455708d6fc9a6daab527fc8bbe65be354a3861f8c450a632a4"},
+ {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee1f5fcbf5bc33acc0be9dd31130bcba35d6d2302e4eceafafd7d9018c7755ab"},
+ {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f195338a5a7b50a058522b39517c50238358d9ad8284fd92943643144c0c03"},
+ {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:524fcb8d59b0dbee2c9b32207084b67b2420f6431ed02c18bd191e6c575f5c48"},
+ {file = "watchfiles-0.23.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0eff099a4df36afaa0eea7a913aa64dcf2cbd4e7a4f319a73012210af4d23810"},
+ {file = "watchfiles-0.23.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a8323daae27ea290ba3350c70c836c0d2b0fb47897fa3b0ca6a5375b952b90d3"},
+ {file = "watchfiles-0.23.0-cp38-none-win32.whl", hash = "sha256:aafea64a3ae698695975251f4254df2225e2624185a69534e7fe70581066bc1b"},
+ {file = "watchfiles-0.23.0-cp38-none-win_amd64.whl", hash = "sha256:c846884b2e690ba62a51048a097acb6b5cd263d8bd91062cd6137e2880578472"},
+ {file = "watchfiles-0.23.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a753993635eccf1ecb185dedcc69d220dab41804272f45e4aef0a67e790c3eb3"},
+ {file = "watchfiles-0.23.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6bb91fa4d0b392f0f7e27c40981e46dda9eb0fbc84162c7fb478fe115944f491"},
+ {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1f67312efa3902a8e8496bfa9824d3bec096ff83c4669ea555c6bdd213aa516"},
+ {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ca6b71dcc50d320c88fb2d88ecd63924934a8abc1673683a242a7ca7d39e781"},
+ {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aec5c29915caf08771d2507da3ac08e8de24a50f746eb1ed295584ba1820330"},
+ {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1733b9bc2c8098c6bdb0ff7a3d7cb211753fecb7bd99bdd6df995621ee1a574b"},
+ {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02ff5d7bd066c6a7673b17c8879cd8ee903078d184802a7ee851449c43521bdd"},
+ {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18e2de19801b0eaa4c5292a223effb7cfb43904cb742c5317a0ac686ed604765"},
+ {file = "watchfiles-0.23.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8ada449e22198c31fb013ae7e9add887e8d2bd2335401abd3cbc55f8c5083647"},
+ {file = "watchfiles-0.23.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3af1b05361e1cc497bf1be654a664750ae61f5739e4bb094a2be86ec8c6db9b6"},
+ {file = "watchfiles-0.23.0-cp39-none-win32.whl", hash = "sha256:486bda18be5d25ab5d932699ceed918f68eb91f45d018b0343e3502e52866e5e"},
+ {file = "watchfiles-0.23.0-cp39-none-win_amd64.whl", hash = "sha256:d2d42254b189a346249424fb9bb39182a19289a2409051ee432fb2926bad966a"},
+ {file = "watchfiles-0.23.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6a9265cf87a5b70147bfb2fec14770ed5b11a5bb83353f0eee1c25a81af5abfe"},
+ {file = "watchfiles-0.23.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f02a259fcbbb5fcfe7a0805b1097ead5ba7a043e318eef1db59f93067f0b49b"},
+ {file = "watchfiles-0.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ebaebb53b34690da0936c256c1cdb0914f24fb0e03da76d185806df9328abed"},
+ {file = "watchfiles-0.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd257f98cff9c6cb39eee1a83c7c3183970d8a8d23e8cf4f47d9a21329285cee"},
+ {file = "watchfiles-0.23.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aba037c1310dd108411d27b3d5815998ef0e83573e47d4219f45753c710f969f"},
+ {file = "watchfiles-0.23.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:a96ac14e184aa86dc43b8a22bb53854760a58b2966c2b41580de938e9bf26ed0"},
+ {file = "watchfiles-0.23.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11698bb2ea5e991d10f1f4f83a39a02f91e44e4bd05f01b5c1ec04c9342bf63c"},
+ {file = "watchfiles-0.23.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efadd40fca3a04063d40c4448c9303ce24dd6151dc162cfae4a2a060232ebdcb"},
+ {file = "watchfiles-0.23.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:556347b0abb4224c5ec688fc58214162e92a500323f50182f994f3ad33385dcb"},
+ {file = "watchfiles-0.23.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1cf7f486169986c4b9d34087f08ce56a35126600b6fef3028f19ca16d5889071"},
+ {file = "watchfiles-0.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f18de0f82c62c4197bea5ecf4389288ac755896aac734bd2cc44004c56e4ac47"},
+ {file = "watchfiles-0.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:532e1f2c491274d1333a814e4c5c2e8b92345d41b12dc806cf07aaff786beb66"},
+ {file = "watchfiles-0.23.0.tar.gz", hash = "sha256:9338ade39ff24f8086bb005d16c29f8e9f19e55b18dcb04dfa26fcbc09da497b"},
]
[package.dependencies]