Build debian package for SQLCipher4

This commit is contained in:
Miguel Jacq 2025-12-21 12:58:42 +11:00
parent 2f68084bdd
commit d1aa5762bc
Signed by: mig5
GPG key ID: 59B3F0C24135C6A9
28 changed files with 285904 additions and 168 deletions

78
Dockerfile.debbuild Normal file
View file

@ -0,0 +1,78 @@
# syntax=docker/dockerfile:1
ARG BASE_IMAGE=debian:bookworm
FROM ${BASE_IMAGE}
ENV DEBIAN_FRONTEND=noninteractive
# If Ubuntu, ensure Universe is enabled.
RUN set -eux; \
. /etc/os-release; \
if [ "${ID:-}" = "ubuntu" ]; then \
apt-get update; \
apt-get install -y --no-install-recommends software-properties-common ca-certificates; \
add-apt-repository -y universe; \
fi;
# Build deps
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
build-essential \
devscripts \
debhelper \
dh-python \
python3-all-dev \
python3-setuptools \
python3-wheel \
libssl-dev \
ca-certificates \
rsync \
; \
rm -rf /var/lib/apt/lists/*
# Build runner script
RUN set -eux; \
cat > /usr/local/bin/build-deb <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
SRC="${SRC:-/src}"
WORKROOT="${WORKROOT:-/work}"
WORK="${WORKROOT}/src"
OUT="${OUT:-/out}"
mkdir -p "$WORK" "$OUT"
rsync -a --delete \
--exclude '.git' \
--exclude '.venv' \
--exclude 'dist' \
--exclude 'build' \
--exclude '__pycache__' \
--exclude '.pytest_cache' \
--exclude '.mypy_cache' \
"${SRC}/" "${WORK}/"
cd "${WORK}"
if [ -n "${SUITE:-}" ]; then
export DEBEMAIL="mig@mig5.net"
export DEBFULLNAME="Miguel Jacq"
dch --distribution "$SUITE" --local "~${SUITE}" "CI build for $SUITE"
fi
dpkg-buildpackage -us -uc -b
shopt -s nullglob
cp -v "${WORKROOT}"/*.deb \
"${WORKROOT}"/*.changes \
"${WORKROOT}"/*.buildinfo \
"${WORKROOT}"/*.dsc \
"${WORKROOT}"/*.tar.* \
"${OUT}/" || true
echo "Artifacts copied to ${OUT}"
EOF
RUN chmod +x /usr/local/bin/build-deb
WORKDIR /work
ENTRYPOINT ["/usr/local/bin/build-deb"]

39
LICENSE
View file

@ -1,19 +1,24 @@
Copyright (c) 2004-2007 Gerhard Häring Copyright (c) 2025, ZETETIC LLC
All rights reserved.
This software is provided 'as-is', without any express or implied warranty. In Redistribution and use in source and binary forms, with or without
no event will the authors be held liable for any damages arising from the use modification, are permitted provided that the following conditions are met:
of this software. * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the ZETETIC LLC nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
Permission is granted to anyone to use this software for any purpose, THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY
including commercial applications, and to alter it and redistribute it freely, EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
subject to the following restrictions: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY
1. The origin of this software must not be misrepresented; you must not DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
claim that you wrote the original software. If you use this software in (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
a product, an acknowledgment in the product documentation would be LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
appreciated but is not required. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2. Altered source versions must be plainly marked as such, and must not be SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

View file

@ -1,92 +1,13 @@
sqlcipher3 bouquin-sqlcipher4
========== ==========
*NOTICE*: This is a fork of [sqlcipher3](https://github.com/coleifer/sqlcipher3) *NOTICE*: This is a fork of [sqlcipher3-wheels](https://github.com/laggykiller/sqlcipher3),
which adds github action for creating wheels for Windows, MacOS and Linux. itself a fork of https://github.com/coleifer/sqlcipher3.
The unofficial wheels from this fork are uploaded to `sqlcipher3-wheels` on pypi,
while the official wheels are `sqlcipher3-binary`.
To install openssl easily, conan is used. I made some reference with
[this fork of pysqlite3 by Dobatymo](https://github.com/Dobatymo/pysqlite3)
*NOTICE*: To build from this fork, copy `sqlite3.c` and `sqlite3.h` This one is intended for my Bouquin tool and is up to date with latest SQLCipher 4 (it also
to `src/sqlcipher`, then run `pip wheel .` or `python -m build .` reuses the SQLCipher 4 version number to be aligned with it at all times).
*NOTICE*: The wheels are built with sqlcipher version 4. You have to execute The main motivation of the 'fork' is just to bring SQLCipher up to date and also to package
`PRAGMA cipher_compatibility = 3` before doing any operations on a database it for Debian in my apt repo. This unblocks me to package Bouquin for Debian in my apt repo
encrypted with SQLCipher version 3 when a newer version is installed. too.
Keep in mind, you have to add `PRAGMA cipher_compatibility` after `PRAGMA key`:
```python
from sqlcipher3 import dbapi2 as sqlite
conn = sqlite.connect('test.db')
c = conn.cursor()
c.execute("PRAGMA key='password'")
c.execute("PRAGMA cipher_compatibility = 3")
c.execute('''create table stocks (date text, trans text, symbol text, qty real, price real)''')
c.execute("""insert into stocks values ('2006-01-05','BUY','RHAT',100,35.14)""")
conn.commit()
c.close()
```
This library takes [pysqlite3](https://github.com/coleifer/pysqlite3) and makes
some small modifications so it is suitable for use with
[sqlcipher](https://github.com/sqlcipher/sqlcipher) (sqlite with encryption).
Additional features:
* User-defined window functions (requires SQLite >= 3.25)
* Flags and VFS an be specified when opening connection
* Incremental BLOB I/O, [bpo-24905](https://github.com/python/cpython/pull/271)
* Improved error messages, [bpo-16379](https://github.com/python/cpython/pull/1108)
* Simplified detection of DML statements via `sqlite3_stmt_readonly`.
* Sqlite native backup API (also present in standard library 3.7 and newer).
A completely self-contained binary package (wheel) is available for versions
0.4.0 and newer as `sqlcipher3-binary`. This package contains the latest
release of sqlcipher compiled with numerous extensions, and requires no
external dependencies.
Building with System SQLCipher
------------------------------
To build `sqlcipher3` linked against the system SQLCipher, run:
```
$ python setup.py build
```
Building a statically-linked library
------------------------------------
To build `sqlcipher3` statically-linked against a particular version of
SQLCipher, you need to obtain the SQLCipher source code and copy `sqlite3.c`
and `sqlite3.h` into the source tree.
```
# Download the latest version of SQLCipher source code and build the source
# amalgamation files (sqlite3.c and sqlite3.h).
$ git clone https://github.com/sqlcipher/sqlcipher
$ cd sqlcipher/
$ ./configure
$ make sqlite3.c
# Copy the sqlcipher amalgamation files into the root of the sqlcipher3
# checkout and run build_static + build:
$ cp sqlcipher/sqlite3.[ch] sqlcipher3/
$ cd sqlcipher3
$ python setup.py build_static build
```
You now have a statically-linked, completely self-contained `sqlcipher3`.
Using the binary package
------------------------
A binary package (wheel) is available for linux with a completely
self-contained `sqlcipher3`, statically-linked against the most recent release
of sqlcipher.
```
$ pip install sqlcipher3-binary
```

6
debian/changelog vendored Normal file
View file

@ -0,0 +1,6 @@
bouquin-sqlcipher4 (4.12.0-1~bouquin1) unstable; urgency=medium
* Initial internal package.
-- Miguel Jacq <mig@mig5.nte> Sun, 21 Dec 2025 00:00:00 +0000

23
debian/control vendored Normal file
View file

@ -0,0 +1,23 @@
Source: bouquin-sqlcipher4
Section: python
Priority: optional
Maintainer: Miguel Jacq <mig@mig5.net>
Rules-Requires-Root: no
Build-Depends:
debhelper-compat (= 13),
dh-python,
python3-all-dev,
python3-setuptools,
python3-wheel,
libssl-dev
Standards-Version: 4.6.2
Homepage: https://git.mig5.net/mig5/bouquin-sqlcipher4
Package: bouquin-sqlcipher4
Architecture: any
Multi-Arch: same
Depends: ${python3:Depends}, ${shlibs:Depends}, ${misc:Depends}
Provides: python3-sqlcipher4
Description: SQLCipher 4-backed sqlcipher module (Bouquin fork)
Provides the `sqlcipher4` Python module compiled against a bundled SQLCipher 4
amalgamation, suitable for use by Bouquin. Built for internal distribution.

48
debian/copyright vendored Normal file
View file

@ -0,0 +1,48 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: bouquin-sqlcipher4
Source: https://git.mig5.net/mig5/bouquin-sqlcipher4
Files: *
Copyright:
Charles Leifer and contributors
laggykiller and contributors
License: Zlib
License: Zlib
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use
of this software.
Permission is granted to anyone to use this software for any purpose, including
commercial applications, and to alter it and redistribute it freely, subject to
the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim
that you wrote the original software.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Files: sqlcipher/*
Copyright (c) 2025, ZETETIC LLC
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the ZETETIC LLC nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

8
debian/rules vendored Normal file
View file

@ -0,0 +1,8 @@
#!/usr/bin/make -f
export PYBUILD_DISABLE_PEP517=1
export BOUQUIN_SYSTEM_OPENSSL=1
%:
dh $@ --with python3 --buildsystem=pybuild

1
debian/source/format vendored Normal file
View file

@ -0,0 +1 @@
3.0 (quilt)

295
poetry.lock generated Normal file
View file

@ -0,0 +1,295 @@
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "coverage"
version = "7.13.0"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.10"
files = [
{file = "coverage-7.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:02d9fb9eccd48f6843c98a37bd6817462f130b86da8660461e8f5e54d4c06070"},
{file = "coverage-7.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:367449cf07d33dc216c083f2036bb7d976c6e4903ab31be400ad74ad9f85ce98"},
{file = "coverage-7.13.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cdb3c9f8fef0a954c632f64328a3935988d33a6604ce4bf67ec3e39670f12ae5"},
{file = "coverage-7.13.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d10fd186aac2316f9bbb46ef91977f9d394ded67050ad6d84d94ed6ea2e8e54e"},
{file = "coverage-7.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f88ae3e69df2ab62fb0bc5219a597cb890ba5c438190ffa87490b315190bb33"},
{file = "coverage-7.13.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4be718e51e86f553bcf515305a158a1cd180d23b72f07ae76d6017c3cc5d791"},
{file = "coverage-7.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a00d3a393207ae12f7c49bb1c113190883b500f48979abb118d8b72b8c95c032"},
{file = "coverage-7.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a7b1cd820e1b6116f92c6128f1188e7afe421c7e1b35fa9836b11444e53ebd9"},
{file = "coverage-7.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:37eee4e552a65866f15dedd917d5e5f3d59805994260720821e2c1b51ac3248f"},
{file = "coverage-7.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:62d7c4f13102148c78d7353c6052af6d899a7f6df66a32bddcc0c0eb7c5326f8"},
{file = "coverage-7.13.0-cp310-cp310-win32.whl", hash = "sha256:24e4e56304fdb56f96f80eabf840eab043b3afea9348b88be680ec5986780a0f"},
{file = "coverage-7.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:74c136e4093627cf04b26a35dab8cbfc9b37c647f0502fc313376e11726ba303"},
{file = "coverage-7.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0dfa3855031070058add1a59fdfda0192fd3e8f97e7c81de0596c145dea51820"},
{file = "coverage-7.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fdb6f54f38e334db97f72fa0c701e66d8479af0bc3f9bfb5b90f1c30f54500f"},
{file = "coverage-7.13.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7e442c013447d1d8d195be62852270b78b6e255b79b8675bad8479641e21fd96"},
{file = "coverage-7.13.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ed5630d946859de835a85e9a43b721123a8a44ec26e2830b296d478c7fd4259"},
{file = "coverage-7.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f15a931a668e58087bc39d05d2b4bf4b14ff2875b49c994bbdb1c2217a8daeb"},
{file = "coverage-7.13.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30a3a201a127ea57f7e14ba43c93c9c4be8b7d17a26e03bb49e6966d019eede9"},
{file = "coverage-7.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a485ff48fbd231efa32d58f479befce52dcb6bfb2a88bb7bf9a0b89b1bc8030"},
{file = "coverage-7.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:22486cdafba4f9e471c816a2a5745337742a617fef68e890d8baf9f3036d7833"},
{file = "coverage-7.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:263c3dbccc78e2e331e59e90115941b5f53e85cfcc6b3b2fbff1fd4e3d2c6ea8"},
{file = "coverage-7.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e5330fa0cc1f5c3c4c3bb8e101b742025933e7848989370a1d4c8c5e401ea753"},
{file = "coverage-7.13.0-cp311-cp311-win32.whl", hash = "sha256:0f4872f5d6c54419c94c25dd6ae1d015deeb337d06e448cd890a1e89a8ee7f3b"},
{file = "coverage-7.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51a202e0f80f241ccb68e3e26e19ab5b3bf0f813314f2c967642f13ebcf1ddfe"},
{file = "coverage-7.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:d2a9d7f1c11487b1c69367ab3ac2d81b9b3721f097aa409a3191c3e90f8f3dd7"},
{file = "coverage-7.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0b3d67d31383c4c68e19a88e28fc4c2e29517580f1b0ebec4a069d502ce1e0bf"},
{file = "coverage-7.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:581f086833d24a22c89ae0fe2142cfaa1c92c930adf637ddf122d55083fb5a0f"},
{file = "coverage-7.13.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0a3a30f0e257df382f5f9534d4ce3d4cf06eafaf5192beb1a7bd066cb10e78fb"},
{file = "coverage-7.13.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:583221913fbc8f53b88c42e8dbb8fca1d0f2e597cb190ce45916662b8b9d9621"},
{file = "coverage-7.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f5d9bd30756fff3e7216491a0d6d520c448d5124d3d8e8f56446d6412499e74"},
{file = "coverage-7.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a23e5a1f8b982d56fa64f8e442e037f6ce29322f1f9e6c2344cd9e9f4407ee57"},
{file = "coverage-7.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b01c22bc74a7fb44066aaf765224c0d933ddf1f5047d6cdfe4795504a4493f8"},
{file = "coverage-7.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:898cce66d0836973f48dda4e3514d863d70142bdf6dfab932b9b6a90ea5b222d"},
{file = "coverage-7.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:3ab483ea0e251b5790c2aac03acde31bff0c736bf8a86829b89382b407cd1c3b"},
{file = "coverage-7.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d84e91521c5e4cb6602fe11ece3e1de03b2760e14ae4fcf1a4b56fa3c801fcd"},
{file = "coverage-7.13.0-cp312-cp312-win32.whl", hash = "sha256:193c3887285eec1dbdb3f2bd7fbc351d570ca9c02ca756c3afbc71b3c98af6ef"},
{file = "coverage-7.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:4f3e223b2b2db5e0db0c2b97286aba0036ca000f06aca9b12112eaa9af3d92ae"},
{file = "coverage-7.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:086cede306d96202e15a4b77ace8472e39d9f4e5f9fd92dd4fecdfb2313b2080"},
{file = "coverage-7.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:28ee1c96109974af104028a8ef57cec21447d42d0e937c0275329272e370ebcf"},
{file = "coverage-7.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e97353dcc5587b85986cda4ff3ec98081d7e84dd95e8b2a6d59820f0545f8a"},
{file = "coverage-7.13.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:99acd4dfdfeb58e1937629eb1ab6ab0899b131f183ee5f23e0b5da5cba2fec74"},
{file = "coverage-7.13.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ff45e0cd8451e293b63ced93161e189780baf444119391b3e7d25315060368a6"},
{file = "coverage-7.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f4f72a85316d8e13234cafe0a9f81b40418ad7a082792fa4165bd7d45d96066b"},
{file = "coverage-7.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:11c21557d0e0a5a38632cbbaca5f008723b26a89d70db6315523df6df77d6232"},
{file = "coverage-7.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76541dc8d53715fb4f7a3a06b34b0dc6846e3c69bc6204c55653a85dd6220971"},
{file = "coverage-7.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6e9e451dee940a86789134b6b0ffbe31c454ade3b849bb8a9d2cca2541a8e91d"},
{file = "coverage-7.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5c67dace46f361125e6b9cace8fe0b729ed8479f47e70c89b838d319375c8137"},
{file = "coverage-7.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f59883c643cb19630500f57016f76cfdcd6845ca8c5b5ea1f6e17f74c8e5f511"},
{file = "coverage-7.13.0-cp313-cp313-win32.whl", hash = "sha256:58632b187be6f0be500f553be41e277712baa278147ecb7559983c6d9faf7ae1"},
{file = "coverage-7.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:73419b89f812f498aca53f757dd834919b48ce4799f9d5cad33ca0ae442bdb1a"},
{file = "coverage-7.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:eb76670874fdd6091eedcc856128ee48c41a9bbbb9c3f1c7c3cf169290e3ffd6"},
{file = "coverage-7.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6e63ccc6e0ad8986386461c3c4b737540f20426e7ec932f42e030320896c311a"},
{file = "coverage-7.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:494f5459ffa1bd45e18558cd98710c36c0b8fbfa82a5eabcbe671d80ecffbfe8"},
{file = "coverage-7.13.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:06cac81bf10f74034e055e903f5f946e3e26fc51c09fc9f584e4a1605d977053"},
{file = "coverage-7.13.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f2ffc92b46ed6e6760f1d47a71e56b5664781bc68986dbd1836b2b70c0ce2071"},
{file = "coverage-7.13.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0602f701057c6823e5db1b74530ce85f17c3c5be5c85fc042ac939cbd909426e"},
{file = "coverage-7.13.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:25dc33618d45456ccb1d37bce44bc78cf269909aa14c4db2e03d63146a8a1493"},
{file = "coverage-7.13.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:71936a8b3b977ddd0b694c28c6a34f4fff2e9dd201969a4ff5d5fc7742d614b0"},
{file = "coverage-7.13.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:936bc20503ce24770c71938d1369461f0c5320830800933bc3956e2a4ded930e"},
{file = "coverage-7.13.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:af0a583efaacc52ae2521f8d7910aff65cdb093091d76291ac5820d5e947fc1c"},
{file = "coverage-7.13.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f1c23e24a7000da892a312fb17e33c5f94f8b001de44b7cf8ba2e36fbd15859e"},
{file = "coverage-7.13.0-cp313-cp313t-win32.whl", hash = "sha256:5f8a0297355e652001015e93be345ee54393e45dc3050af4a0475c5a2b767d46"},
{file = "coverage-7.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6abb3a4c52f05e08460bd9acf04fec027f8718ecaa0d09c40ffbc3fbd70ecc39"},
{file = "coverage-7.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:3ad968d1e3aa6ce5be295ab5fe3ae1bf5bb4769d0f98a80a0252d543a2ef2e9e"},
{file = "coverage-7.13.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:453b7ec753cf5e4356e14fe858064e5520c460d3bbbcb9c35e55c0d21155c256"},
{file = "coverage-7.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:af827b7cbb303e1befa6c4f94fd2bf72f108089cfa0f8abab8f4ca553cf5ca5a"},
{file = "coverage-7.13.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9987a9e4f8197a1000280f7cc089e3ea2c8b3c0a64d750537809879a7b4ceaf9"},
{file = "coverage-7.13.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3188936845cd0cb114fa6a51842a304cdbac2958145d03be2377ec41eb285d19"},
{file = "coverage-7.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2bdb3babb74079f021696cb46b8bb5f5661165c385d3a238712b031a12355be"},
{file = "coverage-7.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7464663eaca6adba4175f6c19354feea61ebbdd735563a03d1e472c7072d27bb"},
{file = "coverage-7.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8069e831f205d2ff1f3d355e82f511eb7c5522d7d413f5db5756b772ec8697f8"},
{file = "coverage-7.13.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6fb2d5d272341565f08e962cce14cdf843a08ac43bd621783527adb06b089c4b"},
{file = "coverage-7.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5e70f92ef89bac1ac8a99b3324923b4749f008fdbd7aa9cb35e01d7a284a04f9"},
{file = "coverage-7.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4b5de7d4583e60d5fd246dd57fcd3a8aa23c6e118a8c72b38adf666ba8e7e927"},
{file = "coverage-7.13.0-cp314-cp314-win32.whl", hash = "sha256:a6c6e16b663be828a8f0b6c5027d36471d4a9f90d28444aa4ced4d48d7d6ae8f"},
{file = "coverage-7.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:0900872f2fdb3ee5646b557918d02279dc3af3dfb39029ac4e945458b13f73bc"},
{file = "coverage-7.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:3a10260e6a152e5f03f26db4a407c4c62d3830b9af9b7c0450b183615f05d43b"},
{file = "coverage-7.13.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9097818b6cc1cfb5f174e3263eba4a62a17683bcfe5c4b5d07f4c97fa51fbf28"},
{file = "coverage-7.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0018f73dfb4301a89292c73be6ba5f58722ff79f51593352759c1790ded1cabe"},
{file = "coverage-7.13.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:166ad2a22ee770f5656e1257703139d3533b4a0b6909af67c6b4a3adc1c98657"},
{file = "coverage-7.13.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f6aaef16d65d1787280943f1c8718dc32e9cf141014e4634d64446702d26e0ff"},
{file = "coverage-7.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e999e2dcc094002d6e2c7bbc1fb85b58ba4f465a760a8014d97619330cdbbbf3"},
{file = "coverage-7.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:00c3d22cf6fb1cf3bf662aaaa4e563be8243a5ed2630339069799835a9cc7f9b"},
{file = "coverage-7.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22ccfe8d9bb0d6134892cbe1262493a8c70d736b9df930f3f3afae0fe3ac924d"},
{file = "coverage-7.13.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9372dff5ea15930fea0445eaf37bbbafbc771a49e70c0aeed8b4e2c2614cc00e"},
{file = "coverage-7.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:69ac2c492918c2461bc6ace42d0479638e60719f2a4ef3f0815fa2df88e9f940"},
{file = "coverage-7.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:739c6c051a7540608d097b8e13c76cfa85263ced467168dc6b477bae3df7d0e2"},
{file = "coverage-7.13.0-cp314-cp314t-win32.whl", hash = "sha256:fe81055d8c6c9de76d60c94ddea73c290b416e061d40d542b24a5871bad498b7"},
{file = "coverage-7.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:445badb539005283825959ac9fa4a28f712c214b65af3a2c464f1adc90f5fcbc"},
{file = "coverage-7.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:de7f6748b890708578fc4b7bb967d810aeb6fcc9bff4bb77dbca77dab2f9df6a"},
{file = "coverage-7.13.0-py3-none-any.whl", hash = "sha256:850d2998f380b1e266459ca5b47bc9e7daf9af1d070f66317972f382d46f1904"},
{file = "coverage-7.13.0.tar.gz", hash = "sha256:a394aa27f2d7ff9bc04cf703817773a59ad6dfbd577032e690f961d2460ee936"},
]
[package.dependencies]
tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
[package.extras]
toml = ["tomli"]
[[package]]
name = "exceptiongroup"
version = "1.3.1"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
files = [
{file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"},
{file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"},
]
[package.dependencies]
typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""}
[package.extras]
test = ["pytest (>=6)"]
[[package]]
name = "iniconfig"
version = "2.3.0"
description = "brain-dead simple config-ini parsing"
optional = false
python-versions = ">=3.10"
files = [
{file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"},
{file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"},
]
[[package]]
name = "packaging"
version = "25.0"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"},
{file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"},
]
[[package]]
name = "pluggy"
version = "1.6.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.9"
files = [
{file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"},
{file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"},
]
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["coverage", "pytest", "pytest-benchmark"]
[[package]]
name = "pygments"
version = "2.19.2"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.8"
files = [
{file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"},
{file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"},
]
[package.extras]
windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pytest"
version = "8.4.2"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.9"
files = [
{file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"},
{file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"},
]
[package.dependencies]
colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""}
exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""}
iniconfig = ">=1"
packaging = ">=20"
pluggy = ">=1.5,<2"
pygments = ">=2.7.2"
tomli = {version = ">=1", markers = "python_version < \"3.11\""}
[package.extras]
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"]
[[package]]
name = "pytest-cov"
version = "5.0.0"
description = "Pytest plugin for measuring coverage."
optional = false
python-versions = ">=3.8"
files = [
{file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"},
{file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"},
]
[package.dependencies]
coverage = {version = ">=5.2.1", extras = ["toml"]}
pytest = ">=4.6"
[package.extras]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
[[package]]
name = "tomli"
version = "2.3.0"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.8"
files = [
{file = "tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45"},
{file = "tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba"},
{file = "tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf"},
{file = "tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441"},
{file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845"},
{file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c"},
{file = "tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456"},
{file = "tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be"},
{file = "tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac"},
{file = "tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22"},
{file = "tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f"},
{file = "tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52"},
{file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8"},
{file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6"},
{file = "tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876"},
{file = "tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878"},
{file = "tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b"},
{file = "tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae"},
{file = "tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b"},
{file = "tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf"},
{file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f"},
{file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05"},
{file = "tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606"},
{file = "tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999"},
{file = "tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e"},
{file = "tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3"},
{file = "tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc"},
{file = "tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0"},
{file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879"},
{file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005"},
{file = "tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463"},
{file = "tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8"},
{file = "tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77"},
{file = "tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf"},
{file = "tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530"},
{file = "tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b"},
{file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67"},
{file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f"},
{file = "tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0"},
{file = "tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba"},
{file = "tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b"},
{file = "tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549"},
]
[[package]]
name = "typing-extensions"
version = "4.15.0"
description = "Backported and Experimental Type Hints for Python 3.9+"
optional = false
python-versions = ">=3.9"
files = [
{file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
{file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
]
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "c315d07822f37ae50f83eb2d3f7e2dc941011291657f93f36780442a339022ca"

View file

@ -1,36 +1,50 @@
[project] [tool.poetry]
name = "sqlcipher3-wheels" name = "bouquin-sqlcipher4"
version = "0.5.6" version = "4.12.0"
description = "DB-API 2.0 interface for SQLCipher 3.x" description = "DB-API 2.0 interface for SQLCipher 4.x, for use with Bouquin"
readme = { content-type = "text/markdown", file = "README.md" }
authors = [ authors = [
{ name = "Charles Leifer", email = "coleifer@gmail.com" }, "Miguel Jacq <mig@mig5.net>",
{ name = "laggykiller", email = "chaudominic2@gmail.com" } "laggykiller <chaudominic2@gmail.com>",
"Charles Leifer <coleifer@gmail.com>",
] ]
license = { text = "zlib/libpng" } license = "MIT" # Poetry expects SPDX-ish strings; "MIT" is the usual one.
classifiers = [ readme = "README.md"
"Development Status :: 4 - Beta",
"Intended Audience :: Developers", # If your package dir is at repo root:
"Operating System :: MacOS :: MacOS X", packages = [{ include = "sqlcipher4" }]
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX", homepage = "https://git.mig5.net/mig5/bouquin-sqlcipher4"
"Programming Language :: C", repository = "https://git.mig5.net/mig5/bouquin-sqlcipher4"
"Programming Language :: Python",
"Topic :: Database :: Database Engines/Servers", # IMPORTANT: include native build outputs in wheel, and C sources/headers in sdist
"Topic :: Software Development :: Libraries :: Python Modules", include = [
{ path = "sqlcipher4/**/*.so", format = "wheel" },
{ path = "sqlcipher4/**/*.pyd", format = "wheel" },
{ path = "src/**/*.c", format = "sdist" },
{ path = "src/**/*.h", format = "sdist" },
{ path = "src/sqlcipher/sqlite3.c", format = "sdist" },
{ path = "src/sqlcipher/sqlite3.h", format = "sdist" },
] ]
[project.urls] [tool.poetry.dependencies]
homepage = "https://github.com/laggykiller/sqlcipher3" python = "^3.10"
[tool.poetry.group.dev.dependencies]
pytest = "^8"
pytest-cov = "^5"
[tool.poetry.build]
script = "scripts/build-extension.py"
[build-system] [build-system]
requires = [ requires = [
"conan>=2.0", "poetry-core>=1.0.0",
"setuptools>=45", "setuptools>=45",
"wheel",
"conan>=2.0",
"lipomerge>=0.1.1", "lipomerge>=0.1.1",
"urllib3<2.0" # urllib3>=2.0 not work for building python3.7 wheels (https://github.com/urllib3/urllib3/issues/2168) "urllib3<2.0",
] ]
build-backend = "setuptools.build_meta" build-backend = "poetry.core.masonry.api"
[tool.setuptools]
packages = ["sqlcipher3"]

43
release.sh Executable file
View file

@ -0,0 +1,43 @@
#!/bin/bash
set -eo pipefail
# Clean caches etc
filedust -y .
mkdir -p src/sqlcipher
cd sqlcipher && ./configure && make sqlite3.c && cp sqlite3.[ch] ../src/sqlcipher/
# Publish to Pypi
poetry build
poetry publish
# Sign packages
for file in `ls -1 dist/`; do qubes-gpg-client --batch --armor --detach-sign dist/$file > dist/$file.asc; done
# Deb stuff
DISTS=(
debian:bookworm
debian:trixie
ubuntu:jammy
ubuntu:noble
)
for dist in ${DISTS[@]}; do
release=$(echo ${dist} | cut -d: -f2)
mkdir -p dist/${release}
docker build -f Dockerfile.debbuild -t bouquin-sqlcipher4-deb:${release} \
--no-cache \
--progress=plain \
--build-arg BASE_IMAGE=${dist} .
docker run --rm \
-e SUITE="${release}" \
-v "$PWD":/src \
-v "$PWD/dist/${release}":/out \
bouquin-sqlcipher4-deb:${release}
debfile=$(ls -1 dist/${release}/*.deb)
#reprepro -b /home/user/git/repo includedeb "${release}" "${debfile}"
done

28
scripts/build-extension.py Executable file
View file

@ -0,0 +1,28 @@
from __future__ import annotations
import os
import subprocess
import sys
from pathlib import Path
def main() -> None:
root = Path(__file__).resolve().parents[1]
env = os.environ.copy()
env.setdefault("BOUQUIN_SYSTEM_OPENSSL", "1")
# Build the extension in-place so the .so/.pyd lands under sqlcipher4/
subprocess.check_call([sys.executable, "setup.py", "build_ext", "--inplace"], cwd=root, env=env)
# Sanity check: did we actually produce the native module?
pkg = root / "sqlcipher4"
built = list(pkg.glob("_sqlite3*.so")) + list(pkg.glob("_sqlite3*.pyd"))
if not built:
raise SystemExit("Extension build produced no sqlcipher4/_sqlite3*.so or .pyd. Check setup.py output.")
print("Built:", ", ".join(p.name for p in built))
if __name__ == "__main__":
main()

View file

@ -26,6 +26,7 @@ sources = glob("src/*.c") + ["src/sqlcipher/sqlite3.c"]
library_dirs = [] library_dirs = []
include_dirs = ["./src"] include_dirs = ["./src"]
def get_native_arch() -> str: def get_native_arch() -> str:
for k, v in CONAN_ARCHS.items(): for k, v in CONAN_ARCHS.items():
if platform.machine().lower() in v: if platform.machine().lower() in v:
@ -36,7 +37,7 @@ def get_native_arch() -> str:
def get_arch() -> str: def get_arch() -> str:
arch_env = os.getenv("SQLCIPHER3_COMPILE_TARGET") arch_env = os.getenv("SQLCIPHER4_COMPILE_TARGET")
if isinstance(arch_env, str): if isinstance(arch_env, str):
arch = arch_env arch = arch_env
else: else:
@ -46,8 +47,7 @@ def get_arch() -> str:
def install_openssl(arch: str) -> "dict[Any, Any]": def install_openssl(arch: str) -> "dict[Any, Any]":
"""Install openssl using Conan. """Install openssl using Conan."""
"""
settings: list[str] = [] settings: list[str] = []
options: list[str] = [] options: list[str] = []
@ -67,29 +67,41 @@ def install_openssl(arch: str) -> "dict[Any, Any]":
options.append("openssl/*:no_zlib=True") options.append("openssl/*:no_zlib=True")
build = ["missing"] build = ["missing"]
if os.path.isdir("/lib") and any(e.startswith("libc.musl") for e in os.listdir("/lib")): if os.path.isdir("/lib") and any(
e.startswith("libc.musl") for e in os.listdir("/lib")
):
# Need to compile openssl if musllinux # Need to compile openssl if musllinux
build.append("openssl*") build.append("openssl*")
subprocess.run(["conan", "profile", "detect", "-f"]) subprocess.run(["conan", "profile", "detect", "-f"])
# Latest openssl need center2.conan.io instead of center.conan.io # Latest openssl need center2.conan.io instead of center.conan.io
subprocess.run(["conan", "remote", "update", "conancenter", "--url=https://center2.conan.io"]) subprocess.run(
["conan", "remote", "update", "conancenter", "--url=https://center2.conan.io"]
)
conan_output = os.path.join("conan_output", arch) conan_output = os.path.join("conan_output", arch)
result = subprocess.run([ result = subprocess.run(
"conan", "install", [
"conan",
"install",
*[x for s in settings for x in ("-s", s)], *[x for s in settings for x in ("-s", s)],
*[x for b in build for x in ("-b", b)], *[x for b in build for x in ("-b", b)],
*[x for o in options for x in ("-o", o)], *[x for o in options for x in ("-o", o)],
"-of", conan_output, "--deployer=direct_deploy", "--format=json", "." "-of",
], stdout=subprocess.PIPE).stdout.decode() conan_output,
"--deployer=direct_deploy",
"--format=json",
".",
],
stdout=subprocess.PIPE,
).stdout.decode()
conan_info = json.loads(result) conan_info = json.loads(result)
return conan_info return conan_info
def add_deps(conan_info: "dict[Any, Any]") -> "tuple[list[str], list[str]]": def add_deps(conan_info: "dict[Any, Any]") -> "tuple[list[str], list[str]]":
"""Find directories of dependencies. """Find directories of dependencies."""
"""
library_dirs: list[str] = [] library_dirs: list[str] = []
include_dirs: list[str] = [] include_dirs: list[str] = []
for dep in conan_info["graph"]["nodes"].values(): for dep in conan_info["graph"]["nodes"].values():
@ -102,6 +114,7 @@ def add_deps(conan_info: "dict[Any, Any]") -> "tuple[list[str], list[str]]":
return library_dirs, include_dirs return library_dirs, include_dirs
def quote_argument(arg: str) -> str: def quote_argument(arg: str) -> str:
is_cibuildwheel = os.environ.get("CIBUILDWHEEL", "0") == "1" is_cibuildwheel = os.environ.get("CIBUILDWHEEL", "0") == "1"
@ -115,9 +128,10 @@ def quote_argument(arg: str) -> str:
return q + arg + q return q + arg + q
if __name__ == "__main__": if __name__ == "__main__":
define_macros: "list[tuple[str, Optional[str]]]" = [ define_macros: "list[tuple[str, Optional[str]]]" = [
("MODULE_NAME", quote_argument("sqlcipher3.dbapi2")), ("MODULE_NAME", quote_argument("sqlcipher4.dbapi2")),
("SQLITE_ENABLE_FTS3", "1"), ("SQLITE_ENABLE_FTS3", "1"),
("SQLITE_ENABLE_FTS3_PARENTHESIS", "1"), ("SQLITE_ENABLE_FTS3_PARENTHESIS", "1"),
("SQLITE_ENABLE_FTS4", "1"), ("SQLITE_ENABLE_FTS4", "1"),
@ -144,14 +158,24 @@ if __name__ == "__main__":
("inline", "__inline"), ("inline", "__inline"),
] ]
# Debian builds (and similar) should be able to use system OpenSSL
# instead of downloading OpenSSL via Conan.
use_system_openssl = os.environ.get("BOUQUIN_SYSTEM_OPENSSL", "0") == "1"
if use_system_openssl and sys.platform == "win32":
raise RuntimeError("BOUQUIN_SYSTEM_OPENSSL=1 is not supported on Windows")
# Configure the compiler # Configure the compiler
arch = get_arch() arch = get_arch()
if arch == "universal2": if (not use_system_openssl) and arch == "universal2":
conan_info_x64 = install_openssl("x86_64") conan_info_x64 = install_openssl("x86_64")
conan_build_folder_x64: str = conan_info_x64["graph"]["nodes"]["0"]["build_folder"] conan_build_folder_x64: str = conan_info_x64["graph"]["nodes"]["0"][
"build_folder"
]
library_dirs_x64, include_dirs_x64 = add_deps(conan_info_x64) library_dirs_x64, include_dirs_x64 = add_deps(conan_info_x64)
conan_info_arm = install_openssl("armv8") conan_info_arm = install_openssl("armv8")
conan_build_folder_arm: str = conan_info_arm["graph"]["nodes"]["0"]["build_folder"] conan_build_folder_arm: str = conan_info_arm["graph"]["nodes"]["0"][
"build_folder"
]
library_dirs_arm, include_dirs_arm = add_deps(conan_info_arm) library_dirs_arm, include_dirs_arm = add_deps(conan_info_arm)
if get_native_arch() == "x86_64": if get_native_arch() == "x86_64":
@ -183,11 +207,13 @@ if __name__ == "__main__":
shutil.rmtree(lipo_dir_merge_src) shutil.rmtree(lipo_dir_merge_src)
shutil.move(lipo_dir_merge_result, lipo_dir_merge_src) shutil.move(lipo_dir_merge_result, lipo_dir_merge_src)
else: elif not use_system_openssl:
conan_info = install_openssl(arch) conan_info = install_openssl(arch)
library_dirs, include_dirs = add_deps(conan_info) library_dirs, include_dirs = add_deps(conan_info)
extra_compile_args: "list[str]" = ["-Qunused-arguments"] if sys.platform == "darwin" else [] extra_compile_args: "list[str]" = (
["-Qunused-arguments"] if sys.platform == "darwin" else []
)
# Configure the linker # Configure the linker
extra_link_args: "list[str]" = [] extra_link_args: "list[str]" = []
@ -204,7 +230,7 @@ if __name__ == "__main__":
extra_link_args.extend(["-lm", "-lcrypto"]) extra_link_args.extend(["-lm", "-lcrypto"])
module = Extension( module = Extension(
name="sqlcipher3._sqlite3", name="sqlcipher4._sqlite3",
sources=sources, sources=sources,
define_macros=define_macros, define_macros=define_macros,
library_dirs=library_dirs, library_dirs=library_dirs,
@ -217,9 +243,9 @@ if __name__ == "__main__":
setup( setup(
# With pyproject.toml, all are not necessary except ext_modules # With pyproject.toml, all are not necessary except ext_modules
# However, they are kept for building python 3.6 wheels # However, they are kept for building python 3.6 wheels
name="sqlcipher3-wheels", name="sqlcipher4",
version="0.5.6", version="0.5.6",
package_dir={"sqlcipher3": "sqlcipher3"}, package_dir={"sqlcipher4": "sqlcipher4"},
packages=["sqlcipher3"], packages=["sqlcipher4"],
ext_modules=[module], ext_modules=[module],
) )

View file

@ -20,4 +20,4 @@
# misrepresented as being the original software. # misrepresented as being the original software.
# 3. This notice may not be removed or altered from any source distribution. # 3. This notice may not be removed or altered from any source distribution.
from sqlcipher3.dbapi2 import * from sqlcipher4.dbapi2 import *

View file

@ -1,2 +1,2 @@
# Adopted from https://github.com/python/typeshed # Adopted from https://github.com/python/typeshed
from sqlcipher3.dbapi2 import * from sqlcipher4.dbapi2 import *

View file

@ -1,5 +1,5 @@
#-*- coding: ISO-8859-1 -*- #-*- coding: ISO-8859-1 -*-
# sqlcipher3/dbapi2.py: the DB-API 2.0 interface # sqlcipher4/dbapi2.py: the DB-API 2.0 interface
# #
# Copyright (C) 2004-2005 Gerhard Häring <gh@ghaering.de> # Copyright (C) 2004-2005 Gerhard Häring <gh@ghaering.de>
# #
@ -25,7 +25,7 @@ import datetime
import time import time
import collections.abc import collections.abc
from sqlcipher3._sqlite3 import * from sqlcipher4._sqlite3 import *
paramstyle = "qmark" paramstyle = "qmark"

271212
sqlcipher4/sqlite3.c Normal file

File diff suppressed because it is too large Load diff

14028
sqlcipher4/sqlite3.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
from sqlcipher3 import dbapi2 as sqlite from sqlcipher4 import dbapi2 as sqlite
import unittest import unittest

View file

@ -23,7 +23,7 @@
import threading import threading
import unittest import unittest
from sqlcipher3 import dbapi2 as sqlite from sqlcipher4 import dbapi2 as sqlite
#from test.support import TESTFN, unlink #from test.support import TESTFN, unlink
TESTFN = 'pysqlite3_test' TESTFN = 'pysqlite3_test'

View file

@ -22,7 +22,7 @@
# 3. This notice may not be removed or altered from any source distribution. # 3. This notice may not be removed or altered from any source distribution.
import unittest import unittest
from sqlcipher3 import dbapi2 as sqlite from sqlcipher4 import dbapi2 as sqlite
from collections.abc import Sequence from collections.abc import Sequence
class MyConnection(sqlite.Connection): class MyConnection(sqlite.Connection):

View file

@ -24,7 +24,7 @@
import os import os
import unittest import unittest
from sqlcipher3 import dbapi2 as sqlite from sqlcipher4 import dbapi2 as sqlite
class CollationTests(unittest.TestCase): class CollationTests(unittest.TestCase):

View file

@ -24,7 +24,7 @@
import datetime import datetime
import functools import functools
import unittest import unittest
from sqlcipher3 import dbapi2 as sqlite from sqlcipher4 import dbapi2 as sqlite
import weakref import weakref
#from test import support #from test import support

View file

@ -22,7 +22,7 @@
# 3. This notice may not be removed or altered from any source distribution. # 3. This notice may not be removed or altered from any source distribution.
import glob, os, unittest import glob, os, unittest
from sqlcipher3 import dbapi2 as sqlite from sqlcipher4 import dbapi2 as sqlite
def get_db_path(): def get_db_path():
return "sqlite_testdb" return "sqlite_testdb"

View file

@ -23,7 +23,7 @@
import datetime import datetime
import unittest import unittest
from sqlcipher3 import dbapi2 as sqlite from sqlcipher4 import dbapi2 as sqlite
try: try:
import zlib import zlib
except ImportError: except ImportError:

View file

@ -24,7 +24,7 @@
import unittest import unittest
import unittest.mock import unittest.mock
from sqlcipher3 import dbapi2 as sqlite from sqlcipher4 import dbapi2 as sqlite
def func_returntext(): def func_returntext():
return "foo" return "foo"