diff --git a/.github/workflows/lint-testing.yml b/.github/workflows/lint-testing.yml
index e47bf27..e756e93 100644
--- a/.github/workflows/lint-testing.yml
+++ b/.github/workflows/lint-testing.yml
@@ -1,4 +1,3 @@
-
name: Lints-Tests
on:
@@ -13,47 +12,21 @@ permissions:
contents: read
env:
- RUSTFLAGS: -C debuginfo=0 # Do not produce debug symbols to keep memory usage down
+ RUSTFLAGS: -C debuginfo=0 # Do not produce debug symbols to keep memory usage down
jobs:
- rust-lints-test:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
-
- - name: Set up Rust
- run: rustup override set stable && rustup update
-
- - name: Install clippy
- run: rustup component add clippy
-
- - name: Cache Rust
- uses: Swatinem/rust-cache@v2
-
- - name: Run cargo clippy
- run: cargo clippy --workspace --all-targets -- -D warnings
-
- - run: cargo test
- env:
- RUST_BACKTRACE: 1
-
-
- test-os:
+ test-python:
name: test ${{ matrix.os }}-${{ matrix.python-version }}
strategy:
fail-fast: false
matrix:
- os: [ubuntu, macos, windows]
- poetry-version: ["1.5.1"]
- python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
+ os: [ubuntu, macos]
+ python-version: ["3.10", "3.11", "3.12"]
runs-on: ${{ matrix.os }}-latest
- needs: [rust-lints-test]
steps:
- uses: actions/checkout@v4
- - name: Install poetry
- run: pipx install poetry==${{ matrix.poetry-version }}
- name: install rust stable
uses: dtolnay/rust-toolchain@stable
@@ -63,77 +36,30 @@ jobs:
with:
key: ${{ matrix.os }}-${{ matrix.python-version }}-v1
- - name: set up python
- uses: actions/setup-python@v5
- with:
- python-version: ${{ matrix.python-version }}
- cache: 'poetry'
-
- - name: Create virtual environment
- run: |
- python -m venv .venv
- echo "$GITHUB_WORKSPACE/rusty-logger/.venv/bin" >> $GITHUB_PATH
-
- - name: Install dependencies
- run : make setup.project
-
- - name: Lints
- run : make lints.ci
-
- - name: Run tests and report coverage
- run: make test.unit
-
- - name: Upload coverage reports to Codecov
- uses: codecov/codecov-action@v4
- env:
- CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
-
- test-ubuntu-pypy:
- name: test ubuntu-${{ matrix.python-version }}
-
- strategy:
- fail-fast: false
- matrix:
- poetry-version: ["1.5.1"]
- python-version: ["pypy3.8", "pypy3.9", "pypy3.10"]
-
- runs-on: ubuntu-latest
- needs: [rust-lints-test]
- steps:
- - uses: actions/checkout@v4
- - name: Install poetry
- run: pipx install poetry==${{ matrix.poetry-version }}
-
- - name: install rust stable
- uses: dtolnay/rust-toolchain@stable
-
- - name: cache rust
- uses: Swatinem/rust-cache@v2
+ - name: Install uv
+ uses: astral-sh/setup-uv@v4
with:
- key: ubuntu-${{ matrix.python-version }}-v1
+ enable-cache: true
- name: set up python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- cache: 'poetry'
- name: Create virtual environment
- run: |
- python -m venv .venv
- echo "$GITHUB_WORKSPACE/rusty-logger/.venv/bin" >> $GITHUB_PATH
-
- - name: Install dependencies
- run : make setup.project
+ working-directory: ./py-logger
+ run: make setup.project
- name: Lints
- run : make lints.ci
-
+ working-directory: ./py-logger
+ run: make lints.ci
+
- name: Run tests and report coverage
+ working-directory: ./py-logger
run: make test.unit
test-build:
- needs: [test-os]
+ needs: [test-python]
name: build - ubuntu - ${{ matrix.manylinux }} - ${{ matrix.target }}
strategy:
fail-fast: false
@@ -160,8 +86,8 @@ jobs:
args: --release --out dist --interpreter 3.11
rust-toolchain: stable
docker-options: -e CI
+ working-directory: ./py-logger
- - run: ls -lh dist/
+ - run: ls -lh ./py-logger/dist/
- - run: twine check --strict dist/*
-
+ - run: twine check --strict ./py-logger/dist/*
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index eaff065..3e072e1 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,7 +1,6 @@
name: Release
on:
-
release:
types: [published]
@@ -10,399 +9,319 @@ permissions:
env:
PACKAGE_NAME: rusty_logger
- INTERPRETER: '3.8 3.9 3.10 3.11 3.12 pypy3.8 pypy3.9 pypy3.10'
+ INTERPRETER: "3.10 3.11 3.12 3.13 pypy3.10"
RUSTFLAGS: -C debuginfo=0
jobs:
- rust-lints-test:
+ rust-release:
+ environment: crates.io
+ name: Release-Rust
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
-
- - name: Set up Rust
- run: rustup override set stable && rustup update
-
- - name: Install clippy
- run: rustup component add clippy
-
- - name: Cache Rust
- uses: Swatinem/rust-cache@v2
-
- - name: Run cargo clippy
- run: cargo clippy --workspace --all-targets -- -D warnings
-
- - run: cargo test
- env:
- RUST_BACKTRACE: 1
-
- test-os:
- name: test ${{ matrix.os }}-${{ matrix.python-version }}
-
- strategy:
- fail-fast: false
- matrix:
- os: [ubuntu, macos, windows]
- poetry-version: ["1.5.1"]
- python-version: ["3.8", "3.9", "3.10","3.11", "3.12"]
-
- runs-on: ${{ matrix.os }}-latest
- needs: [rust-lints-test]
- steps:
- - uses: actions/checkout@v4
- - name: Install poetry
- run: pipx install poetry==${{ matrix.poetry-version }}
-
- - name: install rust stable
- uses: dtolnay/rust-toolchain@stable
-
+ with:
+ fetch-depth: 0
+ - uses: dtolnay/rust-toolchain@stable
- name: cache rust
+ id: cargo_release_cache
uses: Swatinem/rust-cache@v2
with:
- key: ${{ matrix.os }}-${{ matrix.python-version }}-v1
-
- - name: set up python
- uses: actions/setup-python@v5
- with:
- python-version: ${{ matrix.python-version }}
- cache: 'poetry'
-
- - name: Create virtual environment
- run: |
- python -m venv .venv
- echo "$GITHUB_WORKSPACE/rusty-logger/.venv/bin" >> $GITHUB_PATH
-
- - name: Install dependencies
- run : make setup.project
-
- - name: Lints
- run : make lints.ci
-
- - name: Run tests and report coverage
- run: make test.unit
-
- - name: Upload coverage reports to Codecov
- uses: codecov/codecov-action@v4
- env:
- CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
-
- build-sdist:
- needs: [test-os]
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - name: Build sdist
- uses: PyO3/maturin-action@v1
- with:
- command: sdist
- args: --out dist
- rust-toolchain: stable
-
- - name: Upload sdist
- uses: actions/upload-artifact@v3
- with:
- name: wheels
- path: dist
-
- build-ubuntu:
- needs: [test-os]
- name: build - ubuntu - ${{ matrix.manylinux }} - ${{ matrix.target }}
- strategy:
- fail-fast: false
- matrix:
- target: [x86_64, x86, aarch64, armv7, s390x, ppc64le]
- manylinux: [auto]
-
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
-
- - name: set up python
- uses: actions/setup-python@v5
- with:
- python-version: "3.11"
-
- - run: pip install -U twine 'black>=22.3.0,<23' typing_extensions
-
- - name: build wheels
- uses: PyO3/maturin-action@v1
- with:
- target: ${{ matrix.target }}
- manylinux: ${{ matrix.manylinux }}
- args: --release --out dist --interpreter ${{ env.INTERPRETER }}
- rust-toolchain: stable
- docker-options: -e CI
-
- - run: ls -lh dist/
-
- - run: twine check --strict dist/*
-
- - uses: actions/upload-artifact@v3
+ key: ${{ runner.os }}-cargo-release
+ - name: Switch to main branch
+ run: git checkout main
+ - name: Run release-plz
+ uses: MarcoIeni/release-plz-action@v0.5
with:
- name: pypi_files
- path: dist
-
- build-ubuntu-musllinux:
- needs: [test-os]
- name: build - ubuntu - ${{ matrix.manylinux }} - ${{ matrix.target }}
- strategy:
- fail-fast: false
- matrix:
- target: [x86_64, aarch64]
- manylinux: ["musllinux_1_1"]
-
-
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
-
- - name: set up python
- uses: actions/setup-python@v5
- with:
- python-version: '3.11'
-
- - run: pip install -U twine 'black>=22.3.0,<23' typing_extensions
-
- - name: build wheels
- uses: PyO3/maturin-action@v1
- with:
- target: ${{ matrix.target }}
- manylinux: ${{ matrix.manylinux }}
- args: --release --out dist --interpreter ${{ env.INTERPRETER }}
- rust-toolchain: stable
- docker-options: -e CI
-
- - run: ls -lh dist/
-
- - run: twine check --strict dist/*
-
- - uses: actions/upload-artifact@v3
- with:
- name: pypi_files
- path: dist
-
-
- build-macos:
- needs: [test-os]
- name: build - macos - (${{ matrix.target }} - auto
- strategy:
- fail-fast: false
- matrix:
- target: [x86_64, aarch64]
-
- runs-on: macos-latest
- steps:
- - uses: actions/checkout@v4
-
- - name: set up python
- uses: actions/setup-python@v5
- with:
- python-version: '3.11'
-
- - run: pip install -U twine 'black>=22.3.0,<23' typing_extensions
-
- - name: build wheels
- uses: PyO3/maturin-action@v1
- with:
- target: ${{ matrix.target }}
- args: --release --out dist --interpreter ${{ env.INTERPRETER }}
- rust-toolchain: stable
- docker-options: -e CI
-
- - run: ls -lh dist/
-
- - run: twine check --strict dist/*
-
- - uses: actions/upload-artifact@v3
- with:
- name: pypi_files
- path: dist
-
- build-windows:
- needs: [test-os]
- name: build - windows - (${{ matrix.target }} - auto
- strategy:
- fail-fast: false
- matrix:
- target: [x64, x86]
- include:
- - target: i686
- python-architecture: x86
- interpreter: 3.8 3.9 3.10 3.11 3.12
- - target: x64
- python-architecture: x64
- interpreter: 3.8 3.9 3.10 3.11 3.12
- - target: x86
- python-architecture: x86
- interpreter: 3.8 3.9 3.10 3.11 3.12
- exclude:
- - target: x86
- - target: x64
-
-
- runs-on: windows-latest
- steps:
- - uses: actions/checkout@v4
-
- - name: set up python
- uses: actions/setup-python@v5
- with:
- python-version: '3.11'
- architecture: ${{ matrix.python-architecture }}
-
- - run: pip install -U twine 'black>=22.3.0,<23' typing_extensions
-
- - name: build wheels
- uses: PyO3/maturin-action@v1
- with:
- target: ${{ matrix.target }}
- args: --release --out dist --interpreter ${{ matrix.interpreter || env.INTERPRETER }}
- rust-toolchain: stable
-
- - run: dir dist/
-
- - run: twine check --strict dist/*
-
- - uses: actions/upload-artifact@v3
- with:
- name: pypi_files
- path: dist
-
- inspect-pypi-assets:
- needs: [build-ubuntu, build-ubuntu-musllinux, build-macos, build-windows, build-sdist]
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v4
-
- - name: get dist artifacts
- uses: actions/download-artifact@v3
- with:
- name: pypi_files
- path: dist
-
- - name: list dist files
- run: |
- ls -lh dist/
- ls -l dist/
- echo "`ls dist | wc -l` files"
-
-
- test-builds-arch:
- name: test build on ${{ matrix.target }}-${{ matrix.distro }}
- needs: [build-ubuntu, build-ubuntu-musllinux, build-macos, build-windows, build-sdist]
- runs-on: ubuntu-latest
-
- strategy:
- fail-fast: false
- matrix:
- target: [aarch64, armv7, s390x, ppc64le]
- distro: ['ubuntu22.04']
- include:
- - target: aarch64
- distro: alpine_latest
-
- steps:
- - uses: actions/checkout@v4
-
- - name: get dist artifacts
- uses: actions/download-artifact@v3
- with:
- name: pypi_files
- path: dist
-
- - uses: uraimo/run-on-arch-action@v2.5.1
- name: install & test
- with:
- arch: ${{ matrix.target }}
- distro: ${{ matrix.distro }}
- githubToken: ${{ github.token }}
- install: |
- set -x
- if command -v apt-get &> /dev/null; then
- echo "installing python & pip with apt-get..."
- apt-get update
- apt-get install -y --no-install-recommends python3 python3-pip python3-venv
- else
- echo "installing python & pip with apk..."
- apk update
- apk add python3 py3-pip
- fi
- run: |
- set -x
- python3 -m venv venv
- source venv/bin/activate
- python3 -m pip install -r tests/requirements.txt
- python3 -m pip install rusty_logger --no-index --no-deps --find-links dist --force-reinstall
- python3 -m pytest
- python3 -c 'import rusty_logger; print(rusty_logger.__version__)'
-
- test-builds-os:
- name: test build on ${{ matrix.os }}
- needs: [build-ubuntu, build-ubuntu-musllinux, build-macos, build-windows, build-sdist]
-
- strategy:
- fail-fast: false
- matrix:
- os: [ubuntu, macos, windows]
-
- runs-on: ${{ matrix.os }}-latest
- steps:
- - uses: actions/checkout@v4
-
- - name: set up python
- uses: actions/setup-python@v5
- with:
- python-version: '3.11'
-
- - name: Create virtual environment
- run: |
- python -m venv .venv
- echo "$GITHUB_WORKSPACE/rusty-logger/.venv/bin" >> $GITHUB_PATH
-
- - name: get dist artifacts
- uses: actions/download-artifact@v3
- with:
- name: pypi_files
- path: dist
-
- - run: pip install -r tests/requirements.txt
- - run: pip install rusty_logger --no-index --no-deps --find-links dist --force-reinstall
- - run: pytest
-
-
- release:
- name: Release
- runs-on: ubuntu-latest
- needs: [test-builds-os, test-builds-arch]
-
- steps:
- - uses: actions/checkout@v4
-
- - name: install rust stable
- uses: dtolnay/rust-toolchain@stable
- - run: |
- export VERSION=$(cargo pkgid | cut -d "#" -f2)
- echo "version: $VERSION"
- echo "tag: $GITHUB_REF_NAME"
- test "v$VERSION" "=" "$GITHUB_REF_NAME"
-
- - name: set up python
- uses: actions/setup-python@v5
- with:
- python-version: '3.10'
-
- - run: pip install -U twine
-
- - name: get dist artifacts
- uses: actions/download-artifact@v3
- with:
- name: pypi_files
- path: dist
-
- - run: twine check --strict dist/*
-
- - name: upload to pypi
- run: twine upload dist/*
+ command: release
env:
- TWINE_USERNAME: __token__
- TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
\ No newline at end of file
+ GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }}
+ CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_REGISTRY_TOKEN }}
+ #est-python:
+ # name: test ${{ matrix.os }}-${{ matrix.python-version }}
+
+ # strategy:
+ # fail-fast: false
+ # matrix:
+ # os: [ubuntu, macos]
+ # python-version: ["3.10", "3.11", "3.12"]
+
+ # runs-on: ${{ matrix.os }}-latest
+ # steps:
+ # - uses: actions/checkout@v4
+
+ # - name: install rust stable
+ # uses: dtolnay/rust-toolchain@stable
+
+ # - name: cache rust
+ # uses: Swatinem/rust-cache@v2
+ # with:
+ # key: ${{ matrix.os }}-${{ matrix.python-version }}-v1
+
+ # - name: Install uv
+ # uses: astral-sh/setup-uv@v4
+ # with:
+ # enable-cache: true
+
+ # - name: set up python
+ # uses: actions/setup-python@v5
+ # with:
+ # python-version: ${{ matrix.python-version }}
+
+ # - name: Create virtual environment
+ # working-directory: ./py-logger
+ # run: make setup.project
+
+ # - name: Lints
+ # working-directory: ./py-logger
+ # run: make lints.ci
+
+ # - name: Run tests and report coverage
+ # working-directory: ./py-logger
+ # run: make test.unit
+
+ #uild-sdist:
+ # needs: [test-python]
+ # runs-on: ubuntu-latest
+ # steps:
+ # - uses: actions/checkout@v4
+ # - name: Build sdist
+ # uses: PyO3/maturin-action@v1
+ # with:
+ # command: sdist
+ # args: --out dist
+ # rust-toolchain: stable
+ # working-directory: ./py-logger
+
+ # - name: Upload sdist
+ # uses: actions/upload-artifact@v4
+ # with:
+ # name: wheels
+ # path: ./py-logger/dist
+
+ #uild-ubuntu:
+ # needs: [test-python]
+ # name: build - ubuntu - ${{ matrix.manylinux }} - ${{ matrix.target }}
+ # strategy:
+ # fail-fast: false
+ # matrix:
+ # target: [x86_64, x86, aarch64, armv7, s390x, ppc64le]
+ # manylinux: [auto]
+
+ # runs-on: ubuntu-latest
+ # steps:
+ # - uses: actions/checkout@v4
+
+ # - name: set up python
+ # uses: actions/setup-python@v5
+ # with:
+ # python-version: "3.12"
+
+ # - run: pip install -U twine 'black>=22.3.0,<23' typing_extensions
+
+ # - name: build wheels
+ # uses: PyO3/maturin-action@v1
+ # with:
+ # target: ${{ matrix.target }}
+ # manylinux: ${{ matrix.manylinux }}
+ # args: --release --out dist --interpreter ${{ env.INTERPRETER }}
+ # rust-toolchain: stable
+ # docker-options: -e CI
+ # working-directory: ./py-logger
+
+ # - run: ls -lh dist/
+
+ # - run: twine check --strict dist/*
+
+ # - uses: actions/upload-artifact@v3
+ # with:
+ # name: pypi_files
+ # path: ./py-logger/dist
+
+ #uild-ubuntu-musllinux:
+ # needs: [test-python]
+ # name: build - ubuntu - ${{ matrix.manylinux }} - ${{ matrix.target }}
+ # strategy:
+ # fail-fast: false
+ # matrix:
+ # target: [x86_64, aarch64]
+ # manylinux: ["musllinux_1_1"]
+
+ # runs-on: ubuntu-latest
+ # steps:
+ # - uses: actions/checkout@v4
+
+ # - name: set up python
+ # uses: actions/setup-python@v5
+ # with:
+ # python-version: "3.12"
+
+ # - run: pip install -U twine 'black>=22.3.0,<23' typing_extensions
+
+ # - name: build wheels
+ # uses: PyO3/maturin-action@v1
+ # with:
+ # target: ${{ matrix.target }}
+ # manylinux: ${{ matrix.manylinux }}
+ # args: --release --out dist --interpreter ${{ env.INTERPRETER }}
+ # rust-toolchain: stable
+ # docker-options: -e CI
+ # working-directory: ./py-logger
+
+ # - run: ls -lh dist/
+
+ # - run: twine check --strict dist/*
+
+ # - uses: actions/upload-artifact@v3
+ # with:
+ # name: pypi_files
+ # path: ./py-logger/dist
+
+ #uild-macos:
+ # needs: [test-python]
+ # name: build - macos - (${{ matrix.target }} - auto
+ # strategy:
+ # fail-fast: false
+ # matrix:
+ # target: [x86_64, aarch64]
+
+ # runs-on: macos-latest
+ # steps:
+ # - uses: actions/checkout@v4
+
+ # - name: set up python
+ # uses: actions/setup-python@v5
+ # with:
+ # python-version: "3.12"
+
+ # - run: pip install -U twine 'black>=22.3.0,<23' typing_extensions
+
+ # - name: build wheels
+ # uses: PyO3/maturin-action@v1
+ # with:
+ # target: ${{ matrix.target }}
+ # args: --release --out dist --interpreter ${{ env.INTERPRETER }}
+ # rust-toolchain: stable
+ # docker-options: -e CI
+ # working-directory: ./py-logger
+
+ # - run: ls -lh ./py-logger/dist/
+
+ # - run: twine check --strict ./py-logger/dist/*
+
+ # - uses: actions/upload-artifact@v3
+ # with:
+ # name: pypi_files
+ # path: ./py-logger/dist
+
+ #uild-windows:
+ # needs: [test-python]
+ # name: build - windows - (${{ matrix.target }} - auto
+ # strategy:
+ # fail-fast: false
+ # matrix:
+ # target: [x64, x86]
+ # include:
+ # - target: i686
+ # python-architecture: x86
+ # interpreter: 3.10 3.11 3.12 3.13
+ # - target: x64
+ # python-architecture: x64
+ # interpreter: 3.10 3.11 3.12 3.13
+ # - target: x86
+ # python-architecture: x86
+ # interpreter: 3.10 3.11 3.12 3.13
+ # exclude:
+ # - target: x86
+ # - target: x64
+
+ # runs-on: windows-latest
+ # steps:
+ # - uses: actions/checkout@v4
+
+ # - name: set up python
+ # uses: actions/setup-python@v5
+ # with:
+ # python-version: "3.12"
+ # architecture: ${{ matrix.python-architecture }}
+
+ # - run: pip install -U twine 'black>=22.3.0,<23' typing_extensions
+
+ # - name: build wheels
+ # uses: PyO3/maturin-action@v1
+ # with:
+ # target: ${{ matrix.target }}
+ # args: --release --out dist --interpreter ${{ matrix.interpreter || env.INTERPRETER }}
+ # rust-toolchain: stable
+ # working-directory: ./py-logger
+
+ # - run: dir dist/
+
+ # - run: twine check --strict ./py-logger/dist/*
+
+ # - uses: actions/upload-artifact@v3
+ # with:
+ # name: pypi_files
+ # path: ./py-logger/dist
+
+ #nspect-pypi-assets:
+ # needs:
+ # [
+ # build-ubuntu,
+ # build-ubuntu-musllinux,
+ # build-macos,
+ # build-windows,
+ # build-sdist,
+ # ]
+ # runs-on: ubuntu-latest
+
+ # steps:
+ # - uses: actions/checkout@v4
+
+ # - name: get dist artifacts
+ # uses: actions/download-artifact@v3
+ # with:
+ # name: pypi_files
+ # path: dist
+
+ # - name: list dist files
+ # run: |
+ # ls -lh dist/
+ # ls -l dist/
+ # echo "`ls dist | wc -l` files"
+
+ #elease:
+ # name: Release
+ # runs-on: ubuntu-latest
+ # needs: [inspect-pypi-assets]
+
+ # steps:
+ # - uses: actions/checkout@v4
+
+ # - name: install rust stable
+ # uses: dtolnay/rust-toolchain@stable
+ # - run: |
+ # export VERSION=$(cargo pkgid | cut -d "#" -f2)
+ # echo "version: $VERSION"
+ # echo "tag: $GITHUB_REF_NAME"
+ # test "v$VERSION" "=" "$GITHUB_REF_NAME"
+
+ # - name: set up python
+ # uses: actions/setup-python@v5
+ # with:
+ # python-version: "3.10"
+
+ # - run: pip install -U twine
+
+ # - name: get dist artifacts
+ # uses: actions/download-artifact@v3
+ # with:
+ # name: pypi_files
+ # path: dist
+
+ # - run: twine check --strict dist/*
+
+ # - name: upload to pypi
+ # run: twine upload dist/*
+ # env:
+ # TWINE_USERNAME: __token__
+ # TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
diff --git a/Cargo.lock b/Cargo.lock
index 7e736e3..6311e7f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
-version = 3
+version = 4
[[package]]
name = "aho-corasick"
@@ -12,16 +12,16 @@ dependencies = [
]
[[package]]
-name = "autocfg"
-version = "1.3.0"
+name = "anyhow"
+version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
[[package]]
-name = "bitflags"
-version = "2.6.0"
+name = "autocfg"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "cfg-if"
@@ -29,20 +29,29 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+[[package]]
+name = "chrono"
+version = "0.4.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
+dependencies = [
+ "num-traits",
+]
+
[[package]]
name = "crossbeam-channel"
-version = "0.5.13"
+version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
+checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
-version = "0.8.20"
+version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "deranged"
@@ -64,7 +73,7 @@ dependencies = [
"regex",
"serde",
"serde_json",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -76,17 +85,23 @@ dependencies = [
"serde",
]
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
[[package]]
name = "indoc"
-version = "1.0.9"
+version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306"
+checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
[[package]]
name = "itoa"
-version = "1.0.11"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "lazy_static"
@@ -96,25 +111,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
-version = "0.2.155"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
-
-[[package]]
-name = "lock_api"
-version = "0.4.12"
+version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
-dependencies = [
- "autocfg",
- "scopeguard",
-]
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "log"
-version = "0.4.22"
+version = "0.4.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
[[package]]
name = "memchr"
@@ -147,11 +152,20 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
[[package]]
name = "once_cell"
-version = "1.19.0"
+version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "overload"
@@ -161,38 +175,21 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "owo-colors"
-version = "4.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f"
-
-[[package]]
-name = "parking_lot"
-version = "0.12.3"
+version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
-dependencies = [
- "lock_api",
- "parking_lot_core",
-]
+checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56"
[[package]]
-name = "parking_lot_core"
-version = "0.9.10"
+name = "pin-project-lite"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
-dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall",
- "smallvec",
- "windows-targets",
-]
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
-name = "pin-project-lite"
-version = "0.2.14"
+name = "portable-atomic"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
[[package]]
name = "powerfmt"
@@ -202,35 +199,47 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "proc-macro2"
-version = "1.0.86"
+version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [
"unicode-ident",
]
+[[package]]
+name = "py-rusty-logger"
+version = "0.1.0"
+dependencies = [
+ "pyo3",
+ "rusty-logger",
+]
+
[[package]]
name = "pyo3"
-version = "0.19.2"
+version = "0.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e681a6cfdc4adcc93b4d3cf993749a4552018ee0a9b65fc0ccfad74352c72a38"
+checksum = "57fe09249128b3173d092de9523eaa75136bf7ba85e0d69eca241c7939c933cc"
dependencies = [
+ "anyhow",
"cfg-if",
+ "chrono",
"indoc",
"libc",
"memoffset",
- "parking_lot",
+ "once_cell",
+ "portable-atomic",
"pyo3-build-config",
"pyo3-ffi",
"pyo3-macros",
+ "serde",
"unindent",
]
[[package]]
name = "pyo3-build-config"
-version = "0.19.2"
+version = "0.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "076c73d0bc438f7a4ef6fdd0c3bb4732149136abd952b110ac93e4edb13a6ba5"
+checksum = "1cd3927b5a78757a0d71aa9dff669f903b1eb64b54142a9bd9f757f8fde65fd7"
dependencies = [
"once_cell",
"target-lexicon",
@@ -238,9 +247,9 @@ dependencies = [
[[package]]
name = "pyo3-ffi"
-version = "0.19.2"
+version = "0.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e53cee42e77ebe256066ba8aa77eff722b3bb91f3419177cf4cd0f304d3284d9"
+checksum = "dab6bb2102bd8f991e7749f130a70d05dd557613e39ed2deeee8e9ca0c4d548d"
dependencies = [
"libc",
"pyo3-build-config",
@@ -248,50 +257,43 @@ dependencies = [
[[package]]
name = "pyo3-macros"
-version = "0.19.2"
+version = "0.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfeb4c99597e136528c6dd7d5e3de5434d1ceaf487436a3f03b2d56b6fc9efd1"
+checksum = "91871864b353fd5ffcb3f91f2f703a22a9797c91b9ab497b1acac7b07ae509c7"
dependencies = [
"proc-macro2",
"pyo3-macros-backend",
"quote",
- "syn 1.0.109",
+ "syn",
]
[[package]]
name = "pyo3-macros-backend"
-version = "0.19.2"
+version = "0.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "947dc12175c254889edc0c02e399476c2f652b4b9ebd123aa655c224de259536"
+checksum = "43abc3b80bc20f3facd86cd3c60beed58c3e2aa26213f3cda368de39c60a27e4"
dependencies = [
+ "heck",
"proc-macro2",
+ "pyo3-build-config",
"quote",
- "syn 1.0.109",
+ "syn",
]
[[package]]
name = "quote"
-version = "1.0.36"
+version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
-[[package]]
-name = "redox_syscall"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
-dependencies = [
- "bitflags",
-]
-
[[package]]
name = "regex"
-version = "1.10.5"
+version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
@@ -301,9 +303,9 @@ dependencies = [
[[package]]
name = "regex-automata"
-version = "0.4.7"
+version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
@@ -312,19 +314,20 @@ dependencies = [
[[package]]
name = "regex-syntax"
-version = "0.8.4"
+version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "rusty-logger"
-version = "0.3.2"
+version = "0.1.0"
dependencies = [
"dynfmt",
"owo-colors",
"pyo3",
"serde",
"serde_json",
+ "thiserror 2.0.11",
"time",
"tracing",
"tracing-appender",
@@ -338,37 +341,31 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
-[[package]]
-name = "scopeguard"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
-
[[package]]
name = "serde"
-version = "1.0.204"
+version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
+checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.204"
+version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
+checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn",
]
[[package]]
name = "serde_json"
-version = "1.0.122"
+version = "1.0.135"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
+checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9"
dependencies = [
"itoa",
"memchr",
@@ -393,9 +390,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "syn"
-version = "1.0.109"
+version = "2.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
dependencies = [
"proc-macro2",
"quote",
@@ -403,40 +400,49 @@ dependencies = [
]
[[package]]
-name = "syn"
-version = "2.0.72"
+name = "target-lexicon"
+version = "0.12.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
+checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
+ "thiserror-impl 1.0.69",
]
[[package]]
-name = "target-lexicon"
-version = "0.12.16"
+name = "thiserror"
+version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
+checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
+dependencies = [
+ "thiserror-impl 2.0.11",
+]
[[package]]
-name = "thiserror"
-version = "1.0.63"
+name = "thiserror-impl"
+version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
- "thiserror-impl",
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.63"
+version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
+checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn",
]
[[package]]
@@ -451,9 +457,9 @@ dependencies = [
[[package]]
name = "time"
-version = "0.3.36"
+version = "0.3.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
+checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
dependencies = [
"deranged",
"itoa",
@@ -472,9 +478,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
-version = "0.2.18"
+version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
+checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
dependencies = [
"num-conv",
"time-core",
@@ -482,9 +488,9 @@ dependencies = [
[[package]]
name = "tracing"
-version = "0.1.40"
+version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"pin-project-lite",
"tracing-attributes",
@@ -498,27 +504,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf"
dependencies = [
"crossbeam-channel",
- "thiserror",
+ "thiserror 1.0.69",
"time",
"tracing-subscriber",
]
[[package]]
name = "tracing-attributes"
-version = "0.1.27"
+version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn",
]
[[package]]
name = "tracing-core"
-version = "0.1.32"
+version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
dependencies = [
"once_cell",
"valuable",
@@ -537,9 +543,9 @@ dependencies = [
[[package]]
name = "tracing-serde"
-version = "0.1.3"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1"
+checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1"
dependencies = [
"serde",
"tracing-core",
@@ -547,9 +553,9 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
-version = "0.3.18"
+version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
+checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
dependencies = [
"nu-ansi-term",
"serde",
@@ -565,15 +571,15 @@ dependencies = [
[[package]]
name = "unicode-ident"
-version = "1.0.12"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]]
name = "unindent"
-version = "0.1.11"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c"
+checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce"
[[package]]
name = "valuable"
@@ -602,67 +608,3 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-
-[[package]]
-name = "windows-targets"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
-dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_gnullvm",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
-]
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
-
-[[package]]
-name = "windows_i686_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/Cargo.toml b/Cargo.toml
index 5a9e726..6c96001 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,26 +1,41 @@
-[package]
-name = "rusty-logger"
-version = "0.3.2"
-edition = "2021"
+[workspace]
+resolver = "2"
+members = [
+ "crates/*",
+ "py-logger",
+]
+default-members = [
+ "crates/*",
+]
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-[lib]
-name = "_rusty_logger"
-crate-type = ["cdylib", "rlib"]
+[workspace.package]
+version = "0.1.0"
+authors = ["Steven Forrester "]
+edition = "2021"
+license = "MIT"
+repository = "https://github.com/demml/opsml"
-[dependencies]
-pyo3 = "0.19.0"
-tracing = "0.1.37"
-tracing-appender = "0.2.2"
-tracing-subscriber = {version = "0.3.17", features = ["json", "time"]}
-serde = { version = "1.0.163", features = ["derive"] }
-tracing-core = "0.1.31"
-serde_json = "1.0.107"
-time = "0.3.28"
-owo-colors = "4.0.0"
-dynfmt = { version = "0.1.5", features = ["curly"] }
+[workspace.dependencies]
+rusty-logger = { path = "crates/rusty_logger" }
+dynfmt = { version = "0.*", features = ["curly"] }
+owo-colors = "4.*"
+pyo3 = { version = "0.23", features = ["extension-module", "anyhow", "serde", "chrono"] }
+serde = { version = "1.*", features = ["derive"] }
+serde_json = "1.*"
+thiserror = "2.*"
+time = "0.*"
+tracing = "0.*"
+tracing-appender = "0.*"
+tracing-core = "0.*"
+tracing-subscriber = {version = "0.*", features = ["json", "time"]}
[profile.release]
lto = "fat"
codegen-units = 1
strip = true
+opt-level = 3
+
+[profile.release-no-lto]
+inherits = "release"
+lto = false
+
diff --git a/README.md b/README.md
index 7dbd91d..61d6391 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,10 @@
+# Attention
+
+## This crate/library is currently undergoing re-writes.
+
[](https://github.com/thorrester/rusty-logger/actions/workflows/lint-testing.yml)
[](https://codecov.io/gh/thorrester/rusty-logger)
@@ -13,13 +17,20 @@ Simple, opinionated and fast python logging. `Rusty-Logger` is a thin python wra
## Table of contents
-- [Supported configuration](#supported-configuration)
-- [Constraints](#constraints)
-- [Additional metadata](#additional-metadata)
-- [Code examples](#show-me-the-code)
-- [Additional examples](#additional-examples)
-- [Performance](#performance)
-- [Contributing](#contributing)
+- [Attention](#attention)
+ - [This crate/library is currently undergoing re-writes.](#this-cratelibrary-is-currently-undergoing-re-writes)
+ - [Rusty Logger](#rusty-logger)
+ - [Table of contents](#table-of-contents)
+ - [Supported Configuration](#supported-configuration)
+ - [Constraints](#constraints)
+ - [Show Me The Code!](#show-me-the-code)
+ - [Basic Usage](#basic-usage)
+ - [JSON](#json)
+ - [Log to file](#log-to-file)
+ - [Record multiple places at once](#record-multiple-places-at-once)
+ - [Additional examples](#additional-examples)
+ - [Performance](#performance)
+ - [Contributing](#contributing)
## Supported Configuration
diff --git a/crates/rusty_logger/Cargo.toml b/crates/rusty_logger/Cargo.toml
new file mode 100644
index 0000000..c1de506
--- /dev/null
+++ b/crates/rusty_logger/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "rusty-logger"
+version = { workspace = true }
+edition = { workspace = true }
+repository = { workspace = true }
+description = "Logging for OpsML"
+
+[lib]
+doctest = false
+
+[dependencies]
+dynfmt = { workspace = true }
+pyo3 = { workspace = true }
+serde = { workspace = true }
+serde_json = { workspace = true }
+owo-colors = { workspace = true }
+thiserror = { workspace = true }
+time = { workspace = true }
+tracing = { workspace = true }
+tracing-appender = { workspace = true }
+tracing-core = { workspace = true }
+tracing-subscriber = { workspace = true }
\ No newline at end of file
diff --git a/crates/rusty_logger/src/error.rs b/crates/rusty_logger/src/error.rs
new file mode 100644
index 0000000..761dcfc
--- /dev/null
+++ b/crates/rusty_logger/src/error.rs
@@ -0,0 +1,14 @@
+use pyo3::PyErr;
+use thiserror::Error;
+
+#[derive(Error, Debug)]
+pub enum LoggingError {
+ #[error("Logging Error: {0}")]
+ Error(String),
+}
+
+impl From for PyErr {
+ fn from(err: LoggingError) -> PyErr {
+ PyErr::new::(err.to_string())
+ }
+}
diff --git a/crates/rusty_logger/src/lib.rs b/crates/rusty_logger/src/lib.rs
new file mode 100644
index 0000000..f83f3f3
--- /dev/null
+++ b/crates/rusty_logger/src/lib.rs
@@ -0,0 +1,2 @@
+pub mod error;
+pub mod logger;
diff --git a/crates/rusty_logger/src/logger.rs b/crates/rusty_logger/src/logger.rs
new file mode 100644
index 0000000..59b3402
--- /dev/null
+++ b/crates/rusty_logger/src/logger.rs
@@ -0,0 +1,256 @@
+use crate::error::LoggingError;
+use dynfmt::{Format, SimpleCurlyFormat};
+use pyo3::prelude::*;
+use pyo3::types::PyTuple;
+use pyo3::types::PyTupleMethods;
+use serde::{Deserialize, Serialize};
+use std::io;
+use std::str::FromStr;
+use tracing_subscriber;
+use tracing_subscriber::fmt::time::UtcTime;
+
+#[pyclass(eq)]
+#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)]
+pub enum LogLevel {
+ Debug,
+ #[default]
+ Info,
+ Warn,
+ Error,
+ Trace,
+}
+
+impl FromStr for LogLevel {
+ type Err = LoggingError;
+
+ fn from_str(s: &str) -> Result {
+ match s {
+ "debug" => Ok(LogLevel::Debug),
+ "info" => Ok(LogLevel::Info),
+ "warn" => Ok(LogLevel::Warn),
+ "error" => Ok(LogLevel::Error),
+ "trace" => Ok(LogLevel::Trace),
+ _ => Ok(LogLevel::Info),
+ }
+ }
+}
+
+#[pyclass(eq)]
+#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)]
+pub enum WriteLevel {
+ #[default]
+ Stdout,
+ Stderror,
+}
+
+#[allow(clippy::len_zero)] // len tends to be faster than is_empty in tests
+fn format_string(message: &str, args: &Vec) -> String {
+ if args.len() > 0 {
+ SimpleCurlyFormat
+ .format(message, args)
+ .unwrap_or_else(|_| message.into())
+ .to_string()
+ } else {
+ message.to_string()
+ }
+}
+
+pub fn parse_args(args: &Bound<'_, PyTuple>) -> Option> {
+ if args.is_empty() {
+ None
+ } else {
+ Some(args.iter().map(|x| x.to_string()).collect())
+ }
+}
+
+const DEFAULT_TIME_PATTERN: &str =
+ "[year]-[month]-[day]T[hour repr:24]:[minute]:[second]::[subsecond digits:4]";
+
+fn build_json_subscriber(
+ log_level: tracing::Level,
+ config: &LoggingConfig,
+) -> Result<(), LoggingError> {
+ let sub = tracing_subscriber::fmt()
+ .with_max_level(log_level)
+ .json()
+ .with_target(false)
+ .flatten_event(true)
+ .with_thread_ids(config.show_threads.clone())
+ .with_timer(config.time_format()?);
+
+ if config.write_level == WriteLevel::Stderror {
+ sub.with_writer(io::stderr).try_init().map_err(|e| {
+ LoggingError::Error(format!("Failed to setup logging with error: {}", e))
+ })?;
+ } else {
+ sub.with_writer(io::stdout).try_init().map_err(|e| {
+ LoggingError::Error(format!("Failed to setup logging with error: {}", e))
+ })?;
+ }
+ Ok(())
+}
+
+fn build_subscriber(log_level: tracing::Level, config: &LoggingConfig) -> Result<(), LoggingError> {
+ let sub = tracing_subscriber::fmt()
+ .with_max_level(log_level)
+ .with_target(false)
+ .with_thread_ids(config.show_threads.clone())
+ .with_timer(config.time_format()?);
+
+ if config.write_level == WriteLevel::Stderror {
+ sub.with_writer(io::stderr).try_init().map_err(|e| {
+ LoggingError::Error(format!("Failed to setup logging with error: {}", e))
+ })?;
+ } else {
+ sub.with_writer(io::stdout).try_init().map_err(|e| {
+ LoggingError::Error(format!("Failed to setup logging with error: {}", e))
+ })?;
+ }
+ Ok(())
+}
+
+pub fn setup_logging(config: &LoggingConfig) -> Result<(), LoggingError> {
+ let display_level = match &config.log_level {
+ LogLevel::Debug => tracing::Level::DEBUG,
+ LogLevel::Info => tracing::Level::INFO,
+ LogLevel::Warn => tracing::Level::WARN,
+ LogLevel::Error => tracing::Level::ERROR,
+ LogLevel::Trace => tracing::Level::TRACE,
+ };
+
+ if config.use_json {
+ return build_json_subscriber(display_level, config);
+ } else {
+ return build_subscriber(display_level, config);
+ }
+}
+
+#[pyclass]
+#[derive(Clone, Default)]
+pub struct LoggingConfig {
+ #[pyo3(get, set)]
+ show_threads: bool,
+
+ #[pyo3(get, set)]
+ log_level: LogLevel,
+
+ #[pyo3(get, set)]
+ write_level: WriteLevel,
+
+ #[pyo3(get, set)]
+ use_json: bool,
+}
+
+#[pymethods]
+impl LoggingConfig {
+ #[new]
+ #[pyo3(signature = (show_threads=None, log_level=None, write_level=WriteLevel::Stdout, use_json=false))]
+ pub fn new(
+ show_threads: Option,
+ log_level: Option,
+ write_level: Option,
+ use_json: Option,
+ ) -> Self {
+ let show_threads = show_threads.unwrap_or(true);
+ let log_level = log_level.unwrap_or(LogLevel::Info);
+ let write_level = write_level.unwrap_or(WriteLevel::Stdout);
+ let use_json = use_json.unwrap_or(false);
+ LoggingConfig {
+ show_threads,
+ log_level,
+ write_level,
+ use_json,
+ }
+ }
+}
+
+impl LoggingConfig {
+ fn time_format(
+ &self,
+ ) -> Result>>, LoggingError> {
+ let formatter = UtcTime::new(
+ time::format_description::parse(DEFAULT_TIME_PATTERN).map_err(|e| {
+ LoggingError::Error(format!(
+ "Failed to parse time format: {} with error: {}",
+ DEFAULT_TIME_PATTERN, e
+ ))
+ })?,
+ );
+
+ Ok(formatter)
+ }
+}
+
+#[pyclass]
+pub struct RustyLogger {}
+
+#[pymethods]
+impl RustyLogger {
+ #[staticmethod]
+ #[pyo3(signature = (config=None))]
+ pub fn setup_logging(config: Option) -> Result<(), LoggingError> {
+ let config = config.unwrap_or(LoggingConfig::default());
+ let _ = setup_logging(&config).is_ok();
+
+ Ok(())
+ }
+
+ #[staticmethod]
+ #[pyo3(signature = (config=None))]
+ pub fn get_logger(config: Option) -> Result {
+ let config = config.unwrap_or(LoggingConfig::default());
+ let _ = setup_logging(&config).is_ok();
+
+ Ok(RustyLogger {})
+ }
+
+ #[pyo3(signature = (message, *args))]
+ pub fn info(&self, message: &str, args: &Bound<'_, PyTuple>) {
+ let args = parse_args(args);
+ let msg = match args {
+ Some(val) => format_string(message, &val),
+ None => message.to_string(),
+ };
+ tracing::info!(msg);
+ }
+
+ #[pyo3(signature = (message, *args))]
+ pub fn debug(&self, message: &str, args: &Bound<'_, PyTuple>) {
+ let args = parse_args(args);
+ let msg = match args {
+ Some(val) => format_string(message, &val),
+ None => message.to_string(),
+ };
+ tracing::debug!(msg);
+ }
+
+ #[pyo3(signature = (message, *args))]
+ pub fn warn(&self, message: &str, args: &Bound<'_, PyTuple>) {
+ let args = parse_args(args);
+ let msg = match args {
+ Some(val) => format_string(message, &val),
+ None => message.to_string(),
+ };
+ tracing::warn!(msg);
+ }
+
+ #[pyo3(signature = (message, *args))]
+ pub fn error(&self, message: &str, args: &Bound<'_, PyTuple>) {
+ let args = parse_args(args);
+ let msg = match args {
+ Some(val) => format_string(message, &val),
+ None => message.to_string(),
+ };
+ tracing::error!(msg);
+ }
+
+ #[pyo3(signature = (message, *args))]
+ pub fn trace(&self, message: &str, args: &Bound<'_, PyTuple>) {
+ let args = parse_args(args);
+ let msg = match args {
+ Some(val) => format_string(message, &val),
+ None => message.to_string(),
+ };
+ tracing::trace!(msg);
+ }
+}
diff --git a/poetry.lock b/poetry.lock
deleted file mode 100644
index 192a5c2..0000000
--- a/poetry.lock
+++ /dev/null
@@ -1,1036 +0,0 @@
-# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
-
-[[package]]
-name = "annotated-types"
-version = "0.7.0"
-description = "Reusable constraint types to use with typing.Annotated"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
- {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
-]
-
-[package.dependencies]
-typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""}
-
-[[package]]
-name = "anyio"
-version = "4.4.0"
-description = "High level compatibility layer for multiple asynchronous event loop implementations"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"},
- {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"},
-]
-
-[package.dependencies]
-exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""}
-idna = ">=2.8"
-sniffio = ">=1.1"
-typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""}
-
-[package.extras]
-doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
-test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
-trio = ["trio (>=0.23)"]
-
-[[package]]
-name = "astroid"
-version = "2.15.8"
-description = "An abstract syntax tree for Python with inference support."
-optional = false
-python-versions = ">=3.7.2"
-files = [
- {file = "astroid-2.15.8-py3-none-any.whl", hash = "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c"},
- {file = "astroid-2.15.8.tar.gz", hash = "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a"},
-]
-
-[package.dependencies]
-lazy-object-proxy = ">=1.4.0"
-typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""}
-wrapt = [
- {version = ">=1.11,<2", markers = "python_version < \"3.11\""},
- {version = ">=1.14,<2", markers = "python_version >= \"3.11\""},
-]
-
-[[package]]
-name = "black"
-version = "22.3.0"
-description = "The uncompromising code formatter."
-optional = false
-python-versions = ">=3.6.2"
-files = [
- {file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"},
- {file = "black-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb"},
- {file = "black-22.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a"},
- {file = "black-22.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968"},
- {file = "black-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"},
- {file = "black-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce"},
- {file = "black-22.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82"},
- {file = "black-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b"},
- {file = "black-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015"},
- {file = "black-22.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b"},
- {file = "black-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a"},
- {file = "black-22.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163"},
- {file = "black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464"},
- {file = "black-22.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0"},
- {file = "black-22.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176"},
- {file = "black-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0"},
- {file = "black-22.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20"},
- {file = "black-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a"},
- {file = "black-22.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad"},
- {file = "black-22.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21"},
- {file = "black-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265"},
- {file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"},
- {file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"},
-]
-
-[package.dependencies]
-click = ">=8.0.0"
-mypy-extensions = ">=0.4.3"
-pathspec = ">=0.9.0"
-platformdirs = ">=2"
-tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
-typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
-
-[package.extras]
-colorama = ["colorama (>=0.4.3)"]
-d = ["aiohttp (>=3.7.4)"]
-jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
-uvloop = ["uvloop (>=0.15.2)"]
-
-[[package]]
-name = "certifi"
-version = "2024.7.4"
-description = "Python package for providing Mozilla's CA Bundle."
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"},
- {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
-]
-
-[[package]]
-name = "click"
-version = "8.1.7"
-description = "Composable command line interface toolkit"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
- {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
-]
-
-[package.dependencies]
-colorama = {version = "*", markers = "platform_system == \"Windows\""}
-
-[[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.6.0"
-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"},
-]
-
-[package.dependencies]
-tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
-
-[package.extras]
-toml = ["tomli"]
-
-[[package]]
-name = "dill"
-version = "0.3.8"
-description = "serialize all of Python"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"},
- {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"},
-]
-
-[package.extras]
-graph = ["objgraph (>=1.7.2)"]
-profile = ["gprof2dot (>=2022.7.29)"]
-
-[[package]]
-name = "exceptiongroup"
-version = "1.2.2"
-description = "Backport of PEP 654 (exception groups)"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
- {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
-]
-
-[package.extras]
-test = ["pytest (>=6)"]
-
-[[package]]
-name = "fastapi"
-version = "0.109.2"
-description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "fastapi-0.109.2-py3-none-any.whl", hash = "sha256:2c9bab24667293b501cad8dd388c05240c850b58ec5876ee3283c47d6e1e3a4d"},
- {file = "fastapi-0.109.2.tar.gz", hash = "sha256:f3817eac96fe4f65a2ebb4baa000f394e55f5fccdaf7f75250804bc58f354f73"},
-]
-
-[package.dependencies]
-pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
-starlette = ">=0.36.3,<0.37.0"
-typing-extensions = ">=4.8.0"
-
-[package.extras]
-all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
-
-[[package]]
-name = "gunicorn"
-version = "22.0.0"
-description = "WSGI HTTP Server for UNIX"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "gunicorn-22.0.0-py3-none-any.whl", hash = "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9"},
- {file = "gunicorn-22.0.0.tar.gz", hash = "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63"},
-]
-
-[package.dependencies]
-packaging = "*"
-
-[package.extras]
-eventlet = ["eventlet (>=0.24.1,!=0.36.0)"]
-gevent = ["gevent (>=1.4.0)"]
-setproctitle = ["setproctitle"]
-testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"]
-tornado = ["tornado (>=0.2)"]
-
-[[package]]
-name = "h11"
-version = "0.14.0"
-description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
- {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
-]
-
-[[package]]
-name = "httpcore"
-version = "1.0.5"
-description = "A minimal low-level HTTP client."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"},
- {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"},
-]
-
-[package.dependencies]
-certifi = "*"
-h11 = ">=0.13,<0.15"
-
-[package.extras]
-asyncio = ["anyio (>=4.0,<5.0)"]
-http2 = ["h2 (>=3,<5)"]
-socks = ["socksio (==1.*)"]
-trio = ["trio (>=0.22.0,<0.26.0)"]
-
-[[package]]
-name = "httpx"
-version = "0.27.0"
-description = "The next generation HTTP client."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"},
- {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"},
-]
-
-[package.dependencies]
-anyio = "*"
-certifi = "*"
-httpcore = "==1.*"
-idna = "*"
-sniffio = "*"
-
-[package.extras]
-brotli = ["brotli", "brotlicffi"]
-cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
-http2 = ["h2 (>=3,<5)"]
-socks = ["socksio (==1.*)"]
-
-[[package]]
-name = "idna"
-version = "3.7"
-description = "Internationalized Domain Names in Applications (IDNA)"
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
- {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
-]
-
-[[package]]
-name = "iniconfig"
-version = "2.0.0"
-description = "brain-dead simple config-ini parsing"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
- {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
-]
-
-[[package]]
-name = "isort"
-version = "5.13.2"
-description = "A Python utility / library to sort Python imports."
-optional = false
-python-versions = ">=3.8.0"
-files = [
- {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
- {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
-]
-
-[package.extras]
-colors = ["colorama (>=0.4.6)"]
-
-[[package]]
-name = "lazy-object-proxy"
-version = "1.10.0"
-description = "A fast and thorough lazy object proxy."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"},
- {file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"},
- {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3"},
- {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05"},
- {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895"},
- {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83"},
- {file = "lazy_object_proxy-1.10.0-cp310-cp310-win32.whl", hash = "sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9"},
- {file = "lazy_object_proxy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4"},
- {file = "lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c"},
- {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4"},
- {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56"},
- {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9"},
- {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f"},
- {file = "lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03"},
- {file = "lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6"},
- {file = "lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba"},
- {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43"},
- {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9"},
- {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3"},
- {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b"},
- {file = "lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074"},
- {file = "lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282"},
- {file = "lazy_object_proxy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a"},
- {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c"},
- {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255"},
- {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8"},
- {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee"},
- {file = "lazy_object_proxy-1.10.0-cp38-cp38-win32.whl", hash = "sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4"},
- {file = "lazy_object_proxy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696"},
- {file = "lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94"},
- {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b"},
- {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757"},
- {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424"},
- {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658"},
- {file = "lazy_object_proxy-1.10.0-cp39-cp39-win32.whl", hash = "sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70"},
- {file = "lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd"},
- {file = "lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d"},
-]
-
-[[package]]
-name = "markdown-it-py"
-version = "3.0.0"
-description = "Python port of markdown-it. Markdown parsing, done right!"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
- {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"},
-]
-
-[package.dependencies]
-mdurl = ">=0.1,<1.0"
-
-[package.extras]
-benchmarking = ["psutil", "pytest", "pytest-benchmark"]
-code-style = ["pre-commit (>=3.0,<4.0)"]
-compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
-linkify = ["linkify-it-py (>=1,<3)"]
-plugins = ["mdit-py-plugins"]
-profiling = ["gprof2dot"]
-rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
-testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
-
-[[package]]
-name = "mccabe"
-version = "0.7.0"
-description = "McCabe checker, plugin for flake8"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
- {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
-]
-
-[[package]]
-name = "mdurl"
-version = "0.1.2"
-description = "Markdown URL utilities"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
- {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
-]
-
-[[package]]
-name = "mypy"
-version = "1.11.1"
-description = "Optional static typing for Python"
-optional = false
-python-versions = ">=3.8"
-files = [
- {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.6.0"
-
-[package.extras]
-dmypy = ["psutil (>=4.0)"]
-install-types = ["pip"]
-mypyc = ["setuptools (>=50)"]
-reports = ["lxml"]
-
-[[package]]
-name = "mypy-extensions"
-version = "1.0.0"
-description = "Type system extensions for programs checked with the mypy type checker."
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
- {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
-]
-
-[[package]]
-name = "packaging"
-version = "24.1"
-description = "Core utilities for Python packages"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
- {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
-]
-
-[[package]]
-name = "pathspec"
-version = "0.12.1"
-description = "Utility library for gitignore style pattern matching of file paths."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
- {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
-]
-
-[[package]]
-name = "platformdirs"
-version = "4.2.2"
-description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"},
- {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"},
-]
-
-[package.extras]
-docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
-test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
-type = ["mypy (>=1.8)"]
-
-[[package]]
-name = "pluggy"
-version = "1.5.0"
-description = "plugin and hook calling mechanisms for python"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
- {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
-]
-
-[package.extras]
-dev = ["pre-commit", "tox"]
-testing = ["pytest", "pytest-benchmark"]
-
-[[package]]
-name = "pydantic"
-version = "2.8.2"
-description = "Data validation using Python type hints"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"},
- {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"},
-]
-
-[package.dependencies]
-annotated-types = ">=0.4.0"
-pydantic-core = "2.20.1"
-typing-extensions = {version = ">=4.6.1", markers = "python_version < \"3.13\""}
-
-[package.extras]
-email = ["email-validator (>=2.0.0)"]
-
-[[package]]
-name = "pydantic-core"
-version = "2.20.1"
-description = "Core functionality for Pydantic validation and serialization"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"},
- {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"},
- {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"},
- {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"},
- {file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"},
- {file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"},
- {file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"},
- {file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"},
- {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"},
- {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"},
- {file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"},
- {file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"},
- {file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"},
- {file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"},
- {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"},
- {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"},
- {file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"},
- {file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"},
- {file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"},
- {file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"},
- {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"},
- {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"},
- {file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"},
- {file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"},
- {file = "pydantic_core-2.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91"},
- {file = "pydantic_core-2.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd"},
- {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa"},
- {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987"},
- {file = "pydantic_core-2.20.1-cp38-none-win32.whl", hash = "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a"},
- {file = "pydantic_core-2.20.1-cp38-none-win_amd64.whl", hash = "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434"},
- {file = "pydantic_core-2.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c"},
- {file = "pydantic_core-2.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1"},
- {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09"},
- {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab"},
- {file = "pydantic_core-2.20.1-cp39-none-win32.whl", hash = "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2"},
- {file = "pydantic_core-2.20.1-cp39-none-win_amd64.whl", hash = "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"},
- {file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"},
-]
-
-[package.dependencies]
-typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
-
-[[package]]
-name = "pygments"
-version = "2.18.0"
-description = "Pygments is a syntax highlighting package written in Python."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
- {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
-]
-
-[package.extras]
-windows-terminal = ["colorama (>=0.4.6)"]
-
-[[package]]
-name = "pylint"
-version = "2.17.7"
-description = "python code static checker"
-optional = false
-python-versions = ">=3.7.2"
-files = [
- {file = "pylint-2.17.7-py3-none-any.whl", hash = "sha256:27a8d4c7ddc8c2f8c18aa0050148f89ffc09838142193fdbe98f172781a3ff87"},
- {file = "pylint-2.17.7.tar.gz", hash = "sha256:f4fcac7ae74cfe36bc8451e931d8438e4a476c20314b1101c458ad0f05191fad"},
-]
-
-[package.dependencies]
-astroid = ">=2.15.8,<=2.17.0-dev0"
-colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
-dill = [
- {version = ">=0.2", markers = "python_version < \"3.11\""},
- {version = ">=0.3.6", markers = "python_version >= \"3.11\""},
-]
-isort = ">=4.2.5,<6"
-mccabe = ">=0.6,<0.8"
-platformdirs = ">=2.2.0"
-tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
-tomlkit = ">=0.10.1"
-typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
-
-[package.extras]
-spelling = ["pyenchant (>=3.2,<4.0)"]
-testutils = ["gitpython (>3)"]
-
-[[package]]
-name = "pytest"
-version = "7.4.4"
-description = "pytest: simple powerful testing with Python"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
- {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
-]
-
-[package.dependencies]
-colorama = {version = "*", markers = "sys_platform == \"win32\""}
-exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
-iniconfig = "*"
-packaging = "*"
-pluggy = ">=0.12,<2.0"
-tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
-
-[package.extras]
-testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
-
-[[package]]
-name = "pytest-cov"
-version = "3.0.0"
-description = "Pytest plugin for measuring coverage."
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"},
- {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"},
-]
-
-[package.dependencies]
-coverage = {version = ">=5.2.1", extras = ["toml"]}
-pytest = ">=4.6"
-
-[package.extras]
-testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
-
-[[package]]
-name = "python-json-logger"
-version = "2.0.7"
-description = "A python library adding a json log formatter"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "python-json-logger-2.0.7.tar.gz", hash = "sha256:23e7ec02d34237c5aa1e29a070193a4ea87583bb4e7f8fd06d3de8264c4b2e1c"},
- {file = "python_json_logger-2.0.7-py3-none-any.whl", hash = "sha256:f380b826a991ebbe3de4d897aeec42760035ac760345e57b812938dc8b35e2bd"},
-]
-
-[[package]]
-name = "rich"
-version = "13.7.1"
-description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
-optional = false
-python-versions = ">=3.7.0"
-files = [
- {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"},
- {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"},
-]
-
-[package.dependencies]
-markdown-it-py = ">=2.2.0"
-pygments = ">=2.13.0,<3.0.0"
-typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""}
-
-[package.extras]
-jupyter = ["ipywidgets (>=7.5.1,<9)"]
-
-[[package]]
-name = "ruff"
-version = "0.1.15"
-description = "An extremely fast Python linter and code formatter, written in Rust."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:5fe8d54df166ecc24106db7dd6a68d44852d14eb0729ea4672bb4d96c320b7df"},
- {file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6f0bfbb53c4b4de117ac4d6ddfd33aa5fc31beeaa21d23c45c6dd249faf9126f"},
- {file = "ruff-0.1.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0d432aec35bfc0d800d4f70eba26e23a352386be3a6cf157083d18f6f5881c8"},
- {file = "ruff-0.1.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9405fa9ac0e97f35aaddf185a1be194a589424b8713e3b97b762336ec79ff807"},
- {file = "ruff-0.1.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c66ec24fe36841636e814b8f90f572a8c0cb0e54d8b5c2d0e300d28a0d7bffec"},
- {file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:6f8ad828f01e8dd32cc58bc28375150171d198491fc901f6f98d2a39ba8e3ff5"},
- {file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86811954eec63e9ea162af0ffa9f8d09088bab51b7438e8b6488b9401863c25e"},
- {file = "ruff-0.1.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd4025ac5e87d9b80e1f300207eb2fd099ff8200fa2320d7dc066a3f4622dc6b"},
- {file = "ruff-0.1.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b17b93c02cdb6aeb696effecea1095ac93f3884a49a554a9afa76bb125c114c1"},
- {file = "ruff-0.1.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ddb87643be40f034e97e97f5bc2ef7ce39de20e34608f3f829db727a93fb82c5"},
- {file = "ruff-0.1.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:abf4822129ed3a5ce54383d5f0e964e7fef74a41e48eb1dfad404151efc130a2"},
- {file = "ruff-0.1.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6c629cf64bacfd136c07c78ac10a54578ec9d1bd2a9d395efbee0935868bf852"},
- {file = "ruff-0.1.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1bab866aafb53da39c2cadfb8e1c4550ac5340bb40300083eb8967ba25481447"},
- {file = "ruff-0.1.15-py3-none-win32.whl", hash = "sha256:2417e1cb6e2068389b07e6fa74c306b2810fe3ee3476d5b8a96616633f40d14f"},
- {file = "ruff-0.1.15-py3-none-win_amd64.whl", hash = "sha256:3837ac73d869efc4182d9036b1405ef4c73d9b1f88da2413875e34e0d6919587"},
- {file = "ruff-0.1.15-py3-none-win_arm64.whl", hash = "sha256:9a933dfb1c14ec7a33cceb1e49ec4a16b51ce3c20fd42663198746efc0427360"},
- {file = "ruff-0.1.15.tar.gz", hash = "sha256:f6dfa8c1b21c913c326919056c390966648b680966febcb796cc9d1aaab8564e"},
-]
-
-[[package]]
-name = "shellingham"
-version = "1.5.4"
-description = "Tool to Detect Surrounding Shell"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"},
- {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"},
-]
-
-[[package]]
-name = "smokeshow"
-version = "0.4.0"
-description = "Deploy ephemeral websites."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "smokeshow-0.4.0-py3-none-any.whl", hash = "sha256:472d975eeeae140d80cc7f289f6ed2e89190984fa12e2cdad67e929e2db48da6"},
- {file = "smokeshow-0.4.0.tar.gz", hash = "sha256:5c767c7a4753187aee065b090d6739cfabbbd04a9cd6648c23e450737d8a6191"},
-]
-
-[package.dependencies]
-httpx = ">=0.24"
-typer = ">=0.8.0"
-
-[[package]]
-name = "sniffio"
-version = "1.3.1"
-description = "Sniff out which async library your code is running under"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
- {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
-]
-
-[[package]]
-name = "starlette"
-version = "0.36.3"
-description = "The little ASGI library that shines."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "starlette-0.36.3-py3-none-any.whl", hash = "sha256:13d429aa93a61dc40bf503e8c801db1f1bca3dc706b10ef2434a36123568f044"},
- {file = "starlette-0.36.3.tar.gz", hash = "sha256:90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080"},
-]
-
-[package.dependencies]
-anyio = ">=3.4.0,<5"
-typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
-
-[package.extras]
-full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"]
-
-[[package]]
-name = "tomli"
-version = "2.0.1"
-description = "A lil' TOML parser"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
- {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
-]
-
-[[package]]
-name = "tomlkit"
-version = "0.13.0"
-description = "Style preserving TOML library"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "tomlkit-0.13.0-py3-none-any.whl", hash = "sha256:7075d3042d03b80f603482d69bf0c8f345c2b30e41699fd8883227f89972b264"},
- {file = "tomlkit-0.13.0.tar.gz", hash = "sha256:08ad192699734149f5b97b45f1f18dad7eb1b6d16bc72ad0c2335772650d7b72"},
-]
-
-[[package]]
-name = "typer"
-version = "0.12.3"
-description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"},
- {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"},
-]
-
-[package.dependencies]
-click = ">=8.0.0"
-rich = ">=10.11.0"
-shellingham = ">=1.3.0"
-typing-extensions = ">=3.7.4.3"
-
-[[package]]
-name = "typing-extensions"
-version = "4.12.2"
-description = "Backported and Experimental Type Hints for Python 3.8+"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
- {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
-]
-
-[[package]]
-name = "uvicorn"
-version = "0.23.2"
-description = "The lightning-fast ASGI server."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "uvicorn-0.23.2-py3-none-any.whl", hash = "sha256:1f9be6558f01239d4fdf22ef8126c39cb1ad0addf76c40e760549d2c2f43ab53"},
- {file = "uvicorn-0.23.2.tar.gz", hash = "sha256:4d3cc12d7727ba72b64d12d3cc7743124074c0a69f7b201512fc50c3e3f1569a"},
-]
-
-[package.dependencies]
-click = ">=7.0"
-h11 = ">=0.8"
-typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""}
-
-[package.extras]
-standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
-
-[[package]]
-name = "wrapt"
-version = "1.16.0"
-description = "Module for decorators, wrappers and monkey patching."
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"},
- {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"},
- {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"},
- {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"},
- {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"},
- {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"},
- {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"},
- {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"},
- {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"},
- {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"},
- {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"},
- {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"},
- {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"},
- {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"},
- {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"},
- {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"},
- {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"},
- {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"},
- {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"},
- {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"},
- {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"},
- {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"},
- {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"},
- {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"},
- {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"},
- {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"},
- {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"},
- {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"},
- {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"},
- {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"},
- {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"},
- {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"},
- {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"},
- {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"},
- {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"},
- {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"},
- {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"},
- {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"},
- {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"},
- {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"},
- {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"},
- {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"},
- {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"},
- {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"},
- {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"},
- {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"},
- {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"},
- {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"},
- {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"},
- {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"},
- {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"},
- {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"},
- {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"},
- {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"},
- {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"},
- {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"},
- {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"},
- {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"},
- {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"},
- {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"},
- {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"},
- {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"},
- {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"},
- {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"},
- {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"},
- {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"},
- {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"},
- {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"},
- {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"},
- {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
-]
-
-[metadata]
-lock-version = "2.0"
-python-versions = ">=3.8,<3.13"
-content-hash = "dd0e6170c529b64cb1d5e84a7944c73c5ca71eea49e0bcd71b5c7ece0bc1df58"
diff --git a/py-logger/Cargo.toml b/py-logger/Cargo.toml
new file mode 100644
index 0000000..a30f87c
--- /dev/null
+++ b/py-logger/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "py-rusty-logger"
+version = { workspace = true }
+edition = "2021"
+
+[lib]
+name = "py_rusty_logger"
+crate-type = ["cdylib", "rlib"]
+
+[dependencies]
+rusty-logger = { workspace = true }
+pyo3 = { workspace = true }
diff --git a/py-logger/README.md b/py-logger/README.md
new file mode 100644
index 0000000..61d6391
--- /dev/null
+++ b/py-logger/README.md
@@ -0,0 +1,153 @@
+
+
+
+
+
+# Attention
+
+## This crate/library is currently undergoing re-writes.
+
+
+[](https://github.com/thorrester/rusty-logger/actions/workflows/lint-testing.yml)
+[](https://codecov.io/gh/thorrester/rusty-logger)
+
+## Rusty Logger
+
+Simple, opinionated and fast python logging. `Rusty-Logger` is a thin python wrapper for `Rust's` `tracing` library that provides a *mostly* drop-in replacement for `python's` default logging.
+
+## Table of contents
+
+- [Attention](#attention)
+ - [This crate/library is currently undergoing re-writes.](#this-cratelibrary-is-currently-undergoing-re-writes)
+ - [Rusty Logger](#rusty-logger)
+ - [Table of contents](#table-of-contents)
+ - [Supported Configuration](#supported-configuration)
+ - [Constraints](#constraints)
+ - [Show Me The Code!](#show-me-the-code)
+ - [Basic Usage](#basic-usage)
+ - [JSON](#json)
+ - [Log to file](#log-to-file)
+ - [Record multiple places at once](#record-multiple-places-at-once)
+ - [Additional examples](#additional-examples)
+ - [Performance](#performance)
+ - [Contributing](#contributing)
+
+## Supported Configuration
+
+| Arg | Description | Default |
+| ------------- | :-------------:| :-------------: |
+| `stdout` | Log to stdout | `True` |
+| `stderr` | Log to stderr | `False` |
+| `level` | Level to log | `INFO` |
+| `app_env` | Application environment (APP_ENV env var) | `development` |
+| `lock_guard` | Whether to lock logger to current context | `False` |
+| `thread_id` | Whether to display the thread id | `False` |
+| `color` | Whether to enable ansi coloring of logs for standard logger | `False` |
+| `time_format` | Custom time format for logger | `[year]-[month]-[day]T[hour repr:24]:[minute]:[second]::[subsecond digits:4]` |
+| `json_config` | `JsonConig` | `None` |
+| `json_config.flatten` | Whether to flatten any passed fields | `True` |
+| `file_config` | `LogFileConfig` | `None` |
+| `file_config.filename` | Filename for log | `log/logs.log` |
+| `file_config.rotate` | File rotation specification. `daily`, `hourly`, `minutely` or `never` | `never` |
+
+## Constraints
+
+Time is currently limited to UTC; however, you can customize time format to your liking using the `time_format` arg. Please refer to (time docs)[https://time-rs.github.io/book/api/format-description.html] for formatting guidelines. In addition, because `Rusty-Logger` calls `Rust` directly, it's not currently possible to pull the specific line number where logging takes place unless python is directly used (if you're even interested in this feature :smile:). If you'd like to see this feature implemented, and you want to contribute, please refer to the [contributing](https://github.com/thorrester/rusty-logger/blob/main/CONTRIBUTING.md) guide.
+
+In addition, `Rusty-Logger` is a *mostly* drop-in replacement, meaning that you may need to make some minor changes to your existing code. For example, `Rusty-Logger` does not support current python lazy formatting (e.g. `logger.info("Number: %s", 10)`). Instead, `Rusty-Logger` uses Rust's default bracket ({}) formatting.
+
+```python
+# This is not supported
+logger.info("Number: %s", 10)
+
+# This is supported
+logger.info("Number: {}", 10)
+```
+
+## Show Me The Code!
+
+### Basic Usage
+
+```python
+from rusty_logger import Logger
+
+logger = Logger.get_logger(__file__)
+logger.info("Loggy McLogface")
+```
+
+output
+```shell
+2023-10-18T00:11:43::3194 INFO Loggy McLogface app_env="development" name="your_file.py"
+```
+
+### JSON
+
+```python
+from rusty_logger import Logger, LogConfig, JsonConfig
+
+logger = Logger.get_logger(__file__, LogConfig(json_config=JsonConfig()))
+logger.info("Loggy McLogface logs")
+```
+
+output
+```shell
+{"timestamp":"2023-10-18T00:10:59::9732","level":"INFO","message":"Loggy McLogface logs","app_env":"development","name":"your_file.py"}
+```
+
+### Log to file
+
+```python
+from rusty_logger import Logger, LogConfig, JsonConfig, LogLevel, LogFileConfig
+
+logger = Logger.get_logger(
+ name=__file__,
+ config=LogConfig(
+ stdout=False,
+ level=LogLevel.WARN,
+ json_config=JsonConfig(),
+ file_config=LogFileConfig(filename="logs/test.log"),
+ ),
+)
+logger.warning("Loggy McLogface logs logs")
+```
+
+output from `log/test.log`
+```shell
+{"timestamp":"2023-10-18T00:10:10::9364","level":"WARN","message":"Loggy McLogface logs logs","app_env":"development","name":"your_file.py"}
+
+```
+
+
+### Record multiple places at once
+
+```python
+from rusty_logger import Logger, LogConfig, JsonConfig, LogMetadata, LogLevel, LogFileConfig
+
+logger = Logger.get_logger(
+ __file__,
+ LogConfig(
+ stdout=True,
+ level=LogLevel.ERROR,
+ json_config=JsonConfig(),
+ file_config=LogFileConfig(filename="logs/test.log")
+ ),
+)
+logger.error("Loggy McLogface logs logs that are logs")
+```
+
+output
+```shell
+{"timestamp":"2023-10-18T00:09:32::4053","level":"ERROR","message":"Loggy McLogface logs logs that are logs","app_env":"development","name":"your_file.py"}
+```
+## Additional examples
+
+For additional examples, please see the [examples](https://github.com/thorrester/rusty-logger/tree/main/examples) directory which contains timed example of vanilla logger vs `Rusty-Logger`, `python-json-logger` vs `Rusty-Logger` as well as a multi-worker API example.
+
+## Performance
+Why would we do this when python logging is fine? Because we wanted something faster :smile:. From our own benchmarks, `Rusty-Logger` tends to be ~`4x` faster than vanilla python logging and ~`8x` faster than vanilla `JSON` logging. And while speed may not be mission critical for a few thousands logs, it can be for millions, which many companies deal with on a daily basis. Time is money and compute, and we want to save you both :moneybag: :computer:.
+
+## Contributing
+While `Rusty-Logger` is production ready out of the box, it is still in it's infancy and is ripe for additional contributions. If you'd like to contribute, please see the [contributing](https://github.com/thorrester/rusty-logger/blob/main/CONTRIBUTING.md) guide.
+
+
+Thank You!!! :heart: :heart: :heart:
diff --git a/py-logger/makefile b/py-logger/makefile
new file mode 100644
index 0000000..2551a32
--- /dev/null
+++ b/py-logger/makefile
@@ -0,0 +1,33 @@
+PROJECT=rusty-logger
+PYTHON_VERSION=3.12.8
+SOURCE_OBJECTS=python/rusty_logger
+
+format.isort:
+ uv run isort ${SOURCE_OBJECTS}
+format.black:
+ uv run black ${SOURCE_OBJECTS}
+format.ruff:
+ uv run ruff check --silent --exit-zero ${SOURCE_OBJECTS}
+format: format.isort format.ruff format.black
+
+lints.format_check:
+ uv run black --check ${SOURCE_OBJECTS}
+lints.ruff:
+ uv run ruff check ${SOURCE_OBJECTS}
+lints.mypy:
+ uv run mypy ${SOURCE_OBJECTS}
+lints.pylint:
+ uv run pylint ${SOURCE_OBJECTS}
+lints: lints.ruff lints.pylint lints.mypy
+lints.ci: lints.format_check lints.ruff lints.pylint lints.mypy
+
+setup.project:
+ uv sync --all-extras --group dev
+ uv run maturin develop --uv
+
+test.unit:
+ uv run pytest \
+ --cov \
+ --cov-fail-under=0 \
+ --cov-report xml:./coverage.xml \
+ --cov-report term
\ No newline at end of file
diff --git a/py-logger/pyproject.toml b/py-logger/pyproject.toml
new file mode 100644
index 0000000..2748dc9
--- /dev/null
+++ b/py-logger/pyproject.toml
@@ -0,0 +1,93 @@
+[project]
+name = "rusty-logger"
+requires-python = ">=3.10"
+classifiers = [
+ "Programming Language :: Rust",
+ "Programming Language :: Python :: Implementation :: CPython",
+ "Programming Language :: Python :: Implementation :: PyPy",
+]
+version = "0.4.0"
+description = ""
+authors = [{name = "Thorrester", email = ""}]
+readme = "README.md"
+
+[dependency-groups]
+dev = [
+ "maturin >= 1.4.0, < 2.0.0",
+ "pytest >= 7.0.0, < 8.0.0",
+ "pytest-cov >= 5.0.0, < 6.0.0",
+ "ruff >= 0.1.0, < 1.0.0",
+ "mypy >= 1.0.0, < 2.0.0",
+ "black >= 24.3.0, < 25.0.0",
+ "pylint >= 3.0.0, < 4.0.0",
+ "isort >= 5.13.2, < 6.0.0",
+]
+
+[tool.isort]
+profile = "black"
+
+[tool.black]
+line-length = 120
+target-version = ['py310']
+include = '\.pyi?$'
+
+[tool.pylint.MASTER]
+load-plugins = ["pylint.extensions.docparams"]
+
+[tool.pylint.messages_control]
+max-line-length = 130
+disable = [
+ "too-few-public-methods",
+ "design",
+ "duplicate-code",
+ "missing-class-docstring",
+ "missing-function-docstring",
+ "missing-module-docstring",
+ "too-many-nested-blocks",
+ "unused-argument",
+ "fixme",
+ "import-outside-toplevel",
+ "import-self",
+ "too-many-lines",
+]
+
+[tool.flake8]
+# This section is just a doc placeholder..see setup.cfg
+max-complexity = 10
+format = "pylint"
+statistics = "True"
+max-line-length = 125
+# ignore non-PEP8 lints
+ignore = ["E203", "W503", "W0511"]
+
+[tool.pytest.ini_options]
+log_cli = true
+log_cli_level = "INFO"
+testpaths = ["tests"]
+
+[tool.coverage.run]
+omit = [
+ "tests/*",
+]
+
+[tool.mypy]
+# platform configuration
+python_version = "3.12"
+platform = "linux"
+mypy_path = "stubs"
+ignore_missing_imports = true
+exclude = []
+
+[build-system]
+requires = ["maturin>=1.4,<2.0"]
+build-backend = "maturin"
+
+[tool.maturin]
+python-source = "python"
+module-name = "rusty_logger.rusty_logger"
+bindings = 'pyo3'
+features = ["pyo3/extension-module"]
+
+[tool.uv]
+package = false
+
diff --git a/tests/__init__.py b/py-logger/python/py.typed
similarity index 100%
rename from tests/__init__.py
rename to py-logger/python/py.typed
diff --git a/py-logger/python/rusty_logger/__init__.py b/py-logger/python/rusty_logger/__init__.py
new file mode 100644
index 0000000..95f30c4
--- /dev/null
+++ b/py-logger/python/rusty_logger/__init__.py
@@ -0,0 +1,14 @@
+# type: ignore
+# pylint: disable=no-name-in-module
+
+
+from .rusty_logger import LoggingConfig, LogLevel, RustyLogger, WriteLevel
+from .version import __version__
+
+__all__ = [
+ "RustyLogger",
+ "LogLevel",
+ "LoggingConfig",
+ "WriteLevel",
+ "__version__",
+]
diff --git a/py-logger/python/rusty_logger/rusty_logger.pyi b/py-logger/python/rusty_logger/rusty_logger.pyi
new file mode 100644
index 0000000..d72dfce
--- /dev/null
+++ b/py-logger/python/rusty_logger/rusty_logger.pyi
@@ -0,0 +1,118 @@
+from typing import Optional
+
+class LogLevel:
+ Debug: "LogLevel"
+ Info: "LogLevel"
+ Warn: "LogLevel"
+ Error: "LogLevel"
+ Trace: "LogLevel"
+
+class WriteLevel:
+ Stdout: "WriteLevel"
+ Stderr: "WriteLevel"
+
+class LoggingConfig:
+ show_threads: bool
+ log_level: LogLevel
+ write_level: WriteLevel
+ use_json: bool
+
+ def __init__(
+ self,
+ show_threads: Optional[bool] = False,
+ log_level: Optional[LogLevel] = LogLevel.Info,
+ write_level: Optional[WriteLevel] = WriteLevel.Stdout,
+ use_json: Optional[bool] = False,
+ ) -> None:
+ """Initializes the LoggingConfig.
+
+ Args:
+ show_threads (Optional[bool]):
+ Whether to show thread information in the logs. Defaults to False.
+ log_level (Optional[LogLevel]):
+ The log level to use. Defaults to LogLevel.Info.
+ write_level (Optional[WriteLevel]):
+ The write level to use. Defaults to WriteLevel.Stdout.
+ use_json (Optional[bool]):
+ Whether to use JSON format for logs. Defaults to False.
+ """
+
+class RustyLogger:
+ """The Rusty Logger class to use with your python and rust-backed projects."""
+
+ @staticmethod
+ def setup_logging(config: Optional[LoggingConfig] = None) -> None:
+ """Sets up the logger with the given configuration.
+
+ Args:
+ config (LoggingConfig):
+ The configuration to use for the logger.
+ """
+
+ @staticmethod
+ def get_logger(config: Optional[LoggingConfig] = None) -> "RustyLogger":
+ """Gets the logger instance.
+
+ Args:
+ config (LoggingConfig):
+ The configuration to use for the logger.
+
+ Returns:
+ RustyLogger:
+ The logger instance.
+ """
+
+ def debug(self, message: str, *args) -> None:
+ """Logs a debug message.
+
+ Args:
+ message (str):
+ The message to log.
+
+ *args:
+ Additional arguments to log.
+ """
+
+ def info(self, message: str, *args) -> None:
+ """Logs an info message.
+
+ Args:
+ message (str):
+ The message to log.
+
+ *args:
+ Additional arguments to log.
+ """
+
+ def warn(self, message: str, *args) -> None:
+ """Logs a warning message.
+
+ Args:
+ message (str):
+ The message to log.
+
+ *args:
+ Additional arguments to log.
+ """
+
+ def error(self, message: str, *args) -> None:
+ """Logs an error message.
+
+ Args:
+ message (str):
+ The message to log.
+
+ *args:
+ Additional arguments to log.
+ """
+
+ def trace(self, message: str, *args) -> None:
+ """Logs a trace message.
+
+ Args:
+ message (str):
+ The message to log.
+
+ *args:
+ Additional arguments to log.
+ """
diff --git a/python/rusty_logger/version.py b/py-logger/python/rusty_logger/version.py
similarity index 62%
rename from python/rusty_logger/version.py
rename to py-logger/python/rusty_logger/version.py
index c4272ad..7c71606 100644
--- a/python/rusty_logger/version.py
+++ b/py-logger/python/rusty_logger/version.py
@@ -1,5 +1,4 @@
-from importlib.metadata import version, PackageNotFoundError
-
+from importlib.metadata import PackageNotFoundError, version
try:
__version__ = version("rusty_logger")
diff --git a/py-logger/src/lib.rs b/py-logger/src/lib.rs
new file mode 100644
index 0000000..458778c
--- /dev/null
+++ b/py-logger/src/lib.rs
@@ -0,0 +1,12 @@
+use ::rusty_logger::logger::{LogLevel, LoggingConfig, RustyLogger, WriteLevel};
+use pyo3::prelude::*;
+/// Python implementation for the Rusty Logger
+///
+#[pymodule]
+fn rusty_logger(m: &Bound<'_, PyModule>) -> PyResult<()> {
+ m.add_class::()?;
+ m.add_class::()?;
+ m.add_class::()?;
+ m.add_class::()?;
+ Ok(())
+}
diff --git a/tests/color_logger.py b/py-logger/tests/__init__.py
similarity index 100%
rename from tests/color_logger.py
rename to py-logger/tests/__init__.py
diff --git a/py-logger/tests/test_logger.py b/py-logger/tests/test_logger.py
new file mode 100644
index 0000000..11c70d0
--- /dev/null
+++ b/py-logger/tests/test_logger.py
@@ -0,0 +1,24 @@
+from rusty_logger import RustyLogger, LoggingConfig # type: ignore
+
+
+def test_config():
+ LoggingConfig()
+
+
+def test_logger():
+ logger = RustyLogger.get_logger()
+ logger.debug("Debug message")
+ logger.info("Info message")
+ logger.warn("Warning message")
+ logger.error("Error message")
+ logger.trace("Trace message")
+
+
+def test_json_logger():
+ config = LoggingConfig(use_json=True)
+ logger = RustyLogger.get_logger(config)
+ logger.debug("Debug message")
+ logger.info("Info message")
+ logger.warn("Warning message")
+ logger.error("Error message")
+ logger.trace("Trace message")
diff --git a/py-logger/uv.lock b/py-logger/uv.lock
new file mode 100644
index 0000000..da391a6
--- /dev/null
+++ b/py-logger/uv.lock
@@ -0,0 +1,451 @@
+version = 1
+requires-python = ">=3.10"
+resolution-markers = [
+ "python_full_version < '3.11'",
+ "python_full_version == '3.11.*'",
+ "python_full_version >= '3.12'",
+]
+
+[[package]]
+name = "astroid"
+version = "3.3.8"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/80/c5/5c83c48bbf547f3dd8b587529db7cf5a265a3368b33e85e76af8ff6061d3/astroid-3.3.8.tar.gz", hash = "sha256:a88c7994f914a4ea8572fac479459f4955eeccc877be3f2d959a33273b0cf40b", size = 398196 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/07/28/0bc8a17d6cd4cc3c79ae41b7105a2b9a327c110e5ddd37a8a27b29a5c8a2/astroid-3.3.8-py3-none-any.whl", hash = "sha256:187ccc0c248bfbba564826c26f070494f7bc964fd286b6d9fff4420e55de828c", size = 275153 },
+]
+
+[[package]]
+name = "black"
+version = "24.10.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "mypy-extensions" },
+ { name = "packaging" },
+ { name = "pathspec" },
+ { name = "platformdirs" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d8/0d/cc2fb42b8c50d80143221515dd7e4766995bd07c56c9a3ed30baf080b6dc/black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875", size = 645813 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a3/f3/465c0eb5cddf7dbbfe1fecd9b875d1dcf51b88923cd2c1d7e9ab95c6336b/black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812", size = 1623211 },
+ { url = "https://files.pythonhosted.org/packages/df/57/b6d2da7d200773fdfcc224ffb87052cf283cec4d7102fab450b4a05996d8/black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea", size = 1457139 },
+ { url = "https://files.pythonhosted.org/packages/6e/c5/9023b7673904a5188f9be81f5e129fff69f51f5515655fbd1d5a4e80a47b/black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f", size = 1753774 },
+ { url = "https://files.pythonhosted.org/packages/e1/32/df7f18bd0e724e0d9748829765455d6643ec847b3f87e77456fc99d0edab/black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e", size = 1414209 },
+ { url = "https://files.pythonhosted.org/packages/c2/cc/7496bb63a9b06a954d3d0ac9fe7a73f3bf1cd92d7a58877c27f4ad1e9d41/black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad", size = 1607468 },
+ { url = "https://files.pythonhosted.org/packages/2b/e3/69a738fb5ba18b5422f50b4f143544c664d7da40f09c13969b2fd52900e0/black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50", size = 1437270 },
+ { url = "https://files.pythonhosted.org/packages/c9/9b/2db8045b45844665c720dcfe292fdaf2e49825810c0103e1191515fc101a/black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392", size = 1737061 },
+ { url = "https://files.pythonhosted.org/packages/a3/95/17d4a09a5be5f8c65aa4a361444d95edc45def0de887810f508d3f65db7a/black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175", size = 1423293 },
+ { url = "https://files.pythonhosted.org/packages/90/04/bf74c71f592bcd761610bbf67e23e6a3cff824780761f536512437f1e655/black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3", size = 1644256 },
+ { url = "https://files.pythonhosted.org/packages/4c/ea/a77bab4cf1887f4b2e0bce5516ea0b3ff7d04ba96af21d65024629afedb6/black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65", size = 1448534 },
+ { url = "https://files.pythonhosted.org/packages/4e/3e/443ef8bc1fbda78e61f79157f303893f3fddf19ca3c8989b163eb3469a12/black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f", size = 1761892 },
+ { url = "https://files.pythonhosted.org/packages/52/93/eac95ff229049a6901bc84fec6908a5124b8a0b7c26ea766b3b8a5debd22/black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8", size = 1434796 },
+ { url = "https://files.pythonhosted.org/packages/d0/a0/a993f58d4ecfba035e61fca4e9f64a2ecae838fc9f33ab798c62173ed75c/black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981", size = 1643986 },
+ { url = "https://files.pythonhosted.org/packages/37/d5/602d0ef5dfcace3fb4f79c436762f130abd9ee8d950fa2abdbf8bbc555e0/black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b", size = 1448085 },
+ { url = "https://files.pythonhosted.org/packages/47/6d/a3a239e938960df1a662b93d6230d4f3e9b4a22982d060fc38c42f45a56b/black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2", size = 1760928 },
+ { url = "https://files.pythonhosted.org/packages/dd/cf/af018e13b0eddfb434df4d9cd1b2b7892bab119f7a20123e93f6910982e8/black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b", size = 1436875 },
+ { url = "https://files.pythonhosted.org/packages/8d/a7/4b27c50537ebca8bec139b872861f9d2bf501c5ec51fcf897cb924d9e264/black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d", size = 206898 },
+]
+
+[[package]]
+name = "click"
+version = "8.1.8"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "platform_system == 'Windows'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
+]
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
+]
+
+[[package]]
+name = "coverage"
+version = "7.6.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/84/ba/ac14d281f80aab516275012e8875991bb06203957aa1e19950139238d658/coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23", size = 803868 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c5/12/2a2a923edf4ddabdffed7ad6da50d96a5c126dae7b80a33df7310e329a1e/coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78", size = 207982 },
+ { url = "https://files.pythonhosted.org/packages/ca/49/6985dbca9c7be3f3cb62a2e6e492a0c88b65bf40579e16c71ae9c33c6b23/coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c", size = 208414 },
+ { url = "https://files.pythonhosted.org/packages/35/93/287e8f1d1ed2646f4e0b2605d14616c9a8a2697d0d1b453815eb5c6cebdb/coverage-7.6.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a", size = 236860 },
+ { url = "https://files.pythonhosted.org/packages/de/e1/cfdb5627a03567a10031acc629b75d45a4ca1616e54f7133ca1fa366050a/coverage-7.6.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165", size = 234758 },
+ { url = "https://files.pythonhosted.org/packages/6d/85/fc0de2bcda3f97c2ee9fe8568f7d48f7279e91068958e5b2cc19e0e5f600/coverage-7.6.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988", size = 235920 },
+ { url = "https://files.pythonhosted.org/packages/79/73/ef4ea0105531506a6f4cf4ba571a214b14a884630b567ed65b3d9c1975e1/coverage-7.6.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5", size = 234986 },
+ { url = "https://files.pythonhosted.org/packages/c6/4d/75afcfe4432e2ad0405c6f27adeb109ff8976c5e636af8604f94f29fa3fc/coverage-7.6.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3", size = 233446 },
+ { url = "https://files.pythonhosted.org/packages/86/5b/efee56a89c16171288cafff022e8af44f8f94075c2d8da563c3935212871/coverage-7.6.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5", size = 234566 },
+ { url = "https://files.pythonhosted.org/packages/f2/db/67770cceb4a64d3198bf2aa49946f411b85ec6b0a9b489e61c8467a4253b/coverage-7.6.10-cp310-cp310-win32.whl", hash = "sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244", size = 210675 },
+ { url = "https://files.pythonhosted.org/packages/8d/27/e8bfc43f5345ec2c27bc8a1fa77cdc5ce9dcf954445e11f14bb70b889d14/coverage-7.6.10-cp310-cp310-win_amd64.whl", hash = "sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e", size = 211518 },
+ { url = "https://files.pythonhosted.org/packages/85/d2/5e175fcf6766cf7501a8541d81778fd2f52f4870100e791f5327fd23270b/coverage-7.6.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3", size = 208088 },
+ { url = "https://files.pythonhosted.org/packages/4b/6f/06db4dc8fca33c13b673986e20e466fd936235a6ec1f0045c3853ac1b593/coverage-7.6.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43", size = 208536 },
+ { url = "https://files.pythonhosted.org/packages/0d/62/c6a0cf80318c1c1af376d52df444da3608eafc913b82c84a4600d8349472/coverage-7.6.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132", size = 240474 },
+ { url = "https://files.pythonhosted.org/packages/a3/59/750adafc2e57786d2e8739a46b680d4fb0fbc2d57fbcb161290a9f1ecf23/coverage-7.6.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f", size = 237880 },
+ { url = "https://files.pythonhosted.org/packages/2c/f8/ef009b3b98e9f7033c19deb40d629354aab1d8b2d7f9cfec284dbedf5096/coverage-7.6.10-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994", size = 239750 },
+ { url = "https://files.pythonhosted.org/packages/a6/e2/6622f3b70f5f5b59f705e680dae6db64421af05a5d1e389afd24dae62e5b/coverage-7.6.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99", size = 238642 },
+ { url = "https://files.pythonhosted.org/packages/2d/10/57ac3f191a3c95c67844099514ff44e6e19b2915cd1c22269fb27f9b17b6/coverage-7.6.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd", size = 237266 },
+ { url = "https://files.pythonhosted.org/packages/ee/2d/7016f4ad9d553cabcb7333ed78ff9d27248ec4eba8dd21fa488254dff894/coverage-7.6.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377", size = 238045 },
+ { url = "https://files.pythonhosted.org/packages/a7/fe/45af5c82389a71e0cae4546413266d2195c3744849669b0bab4b5f2c75da/coverage-7.6.10-cp311-cp311-win32.whl", hash = "sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8", size = 210647 },
+ { url = "https://files.pythonhosted.org/packages/db/11/3f8e803a43b79bc534c6a506674da9d614e990e37118b4506faf70d46ed6/coverage-7.6.10-cp311-cp311-win_amd64.whl", hash = "sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609", size = 211508 },
+ { url = "https://files.pythonhosted.org/packages/86/77/19d09ea06f92fdf0487499283b1b7af06bc422ea94534c8fe3a4cd023641/coverage-7.6.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853", size = 208281 },
+ { url = "https://files.pythonhosted.org/packages/b6/67/5479b9f2f99fcfb49c0d5cf61912a5255ef80b6e80a3cddba39c38146cf4/coverage-7.6.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078", size = 208514 },
+ { url = "https://files.pythonhosted.org/packages/15/d1/febf59030ce1c83b7331c3546d7317e5120c5966471727aa7ac157729c4b/coverage-7.6.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0", size = 241537 },
+ { url = "https://files.pythonhosted.org/packages/4b/7e/5ac4c90192130e7cf8b63153fe620c8bfd9068f89a6d9b5f26f1550f7a26/coverage-7.6.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50", size = 238572 },
+ { url = "https://files.pythonhosted.org/packages/dc/03/0334a79b26ecf59958f2fe9dd1f5ab3e2f88db876f5071933de39af09647/coverage-7.6.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022", size = 240639 },
+ { url = "https://files.pythonhosted.org/packages/d7/45/8a707f23c202208d7b286d78ad6233f50dcf929319b664b6cc18a03c1aae/coverage-7.6.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b", size = 240072 },
+ { url = "https://files.pythonhosted.org/packages/66/02/603ce0ac2d02bc7b393279ef618940b4a0535b0868ee791140bda9ecfa40/coverage-7.6.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0", size = 238386 },
+ { url = "https://files.pythonhosted.org/packages/04/62/4e6887e9be060f5d18f1dd58c2838b2d9646faf353232dec4e2d4b1c8644/coverage-7.6.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852", size = 240054 },
+ { url = "https://files.pythonhosted.org/packages/5c/74/83ae4151c170d8bd071924f212add22a0e62a7fe2b149edf016aeecad17c/coverage-7.6.10-cp312-cp312-win32.whl", hash = "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359", size = 210904 },
+ { url = "https://files.pythonhosted.org/packages/c3/54/de0893186a221478f5880283119fc40483bc460b27c4c71d1b8bba3474b9/coverage-7.6.10-cp312-cp312-win_amd64.whl", hash = "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247", size = 211692 },
+ { url = "https://files.pythonhosted.org/packages/25/6d/31883d78865529257bf847df5789e2ae80e99de8a460c3453dbfbe0db069/coverage-7.6.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9", size = 208308 },
+ { url = "https://files.pythonhosted.org/packages/70/22/3f2b129cc08de00c83b0ad6252e034320946abfc3e4235c009e57cfeee05/coverage-7.6.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b", size = 208565 },
+ { url = "https://files.pythonhosted.org/packages/97/0a/d89bc2d1cc61d3a8dfe9e9d75217b2be85f6c73ebf1b9e3c2f4e797f4531/coverage-7.6.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690", size = 241083 },
+ { url = "https://files.pythonhosted.org/packages/4c/81/6d64b88a00c7a7aaed3a657b8eaa0931f37a6395fcef61e53ff742b49c97/coverage-7.6.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18", size = 238235 },
+ { url = "https://files.pythonhosted.org/packages/9a/0b/7797d4193f5adb4b837207ed87fecf5fc38f7cc612b369a8e8e12d9fa114/coverage-7.6.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c", size = 240220 },
+ { url = "https://files.pythonhosted.org/packages/65/4d/6f83ca1bddcf8e51bf8ff71572f39a1c73c34cf50e752a952c34f24d0a60/coverage-7.6.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd", size = 239847 },
+ { url = "https://files.pythonhosted.org/packages/30/9d/2470df6aa146aff4c65fee0f87f58d2164a67533c771c9cc12ffcdb865d5/coverage-7.6.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e", size = 237922 },
+ { url = "https://files.pythonhosted.org/packages/08/dd/723fef5d901e6a89f2507094db66c091449c8ba03272861eaefa773ad95c/coverage-7.6.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694", size = 239783 },
+ { url = "https://files.pythonhosted.org/packages/3d/f7/64d3298b2baf261cb35466000628706ce20a82d42faf9b771af447cd2b76/coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6", size = 210965 },
+ { url = "https://files.pythonhosted.org/packages/d5/58/ec43499a7fc681212fe7742fe90b2bc361cdb72e3181ace1604247a5b24d/coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e", size = 211719 },
+ { url = "https://files.pythonhosted.org/packages/ab/c9/f2857a135bcff4330c1e90e7d03446b036b2363d4ad37eb5e3a47bbac8a6/coverage-7.6.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe", size = 209050 },
+ { url = "https://files.pythonhosted.org/packages/aa/b3/f840e5bd777d8433caa9e4a1eb20503495709f697341ac1a8ee6a3c906ad/coverage-7.6.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273", size = 209321 },
+ { url = "https://files.pythonhosted.org/packages/85/7d/125a5362180fcc1c03d91850fc020f3831d5cda09319522bcfa6b2b70be7/coverage-7.6.10-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8", size = 252039 },
+ { url = "https://files.pythonhosted.org/packages/a9/9c/4358bf3c74baf1f9bddd2baf3756b54c07f2cfd2535f0a47f1e7757e54b3/coverage-7.6.10-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098", size = 247758 },
+ { url = "https://files.pythonhosted.org/packages/cf/c7/de3eb6fc5263b26fab5cda3de7a0f80e317597a4bad4781859f72885f300/coverage-7.6.10-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb", size = 250119 },
+ { url = "https://files.pythonhosted.org/packages/3e/e6/43de91f8ba2ec9140c6a4af1102141712949903dc732cf739167cfa7a3bc/coverage-7.6.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0", size = 249597 },
+ { url = "https://files.pythonhosted.org/packages/08/40/61158b5499aa2adf9e37bc6d0117e8f6788625b283d51e7e0c53cf340530/coverage-7.6.10-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf", size = 247473 },
+ { url = "https://files.pythonhosted.org/packages/50/69/b3f2416725621e9f112e74e8470793d5b5995f146f596f133678a633b77e/coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2", size = 248737 },
+ { url = "https://files.pythonhosted.org/packages/3c/6e/fe899fb937657db6df31cc3e61c6968cb56d36d7326361847440a430152e/coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312", size = 211611 },
+ { url = "https://files.pythonhosted.org/packages/1c/55/52f5e66142a9d7bc93a15192eba7a78513d2abf6b3558d77b4ca32f5f424/coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d", size = 212781 },
+ { url = "https://files.pythonhosted.org/packages/a1/70/de81bfec9ed38a64fc44a77c7665e20ca507fc3265597c28b0d989e4082e/coverage-7.6.10-pp39.pp310-none-any.whl", hash = "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f", size = 200223 },
+]
+
+[package.optional-dependencies]
+toml = [
+ { name = "tomli", marker = "python_full_version <= '3.11'" },
+]
+
+[[package]]
+name = "dill"
+version = "0.3.9"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/70/43/86fe3f9e130c4137b0f1b50784dd70a5087b911fe07fa81e53e0c4c47fea/dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c", size = 187000 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/46/d1/e73b6ad76f0b1fb7f23c35c6d95dbc506a9c8804f43dda8cb5b0fa6331fd/dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a", size = 119418 },
+]
+
+[[package]]
+name = "exceptiongroup"
+version = "1.2.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 },
+]
+
+[[package]]
+name = "iniconfig"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
+]
+
+[[package]]
+name = "isort"
+version = "5.13.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/87/f9/c1eb8635a24e87ade2efce21e3ce8cd6b8630bb685ddc9cdaca1349b2eb5/isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", size = 175303 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/b3/8def84f539e7d2289a02f0524b944b15d7c75dab7628bedf1c4f0992029c/isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6", size = 92310 },
+]
+
+[[package]]
+name = "maturin"
+version = "1.8.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9a/08/ccb0f917722a35ab0d758be9bb5edaf645c3a3d6170061f10d396ecd273f/maturin-1.8.1.tar.gz", hash = "sha256:49cd964aabf59f8b0a6969f9860d2cdf194ac331529caae14c884f5659568857", size = 197397 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4c/00/f34077315f34db8ad2ccf6bfe11b864ca27baab3a1320634da8e3cf89a48/maturin-1.8.1-py3-none-linux_armv6l.whl", hash = "sha256:7e590a23d9076b8a994f2e67bc63dc9a2d1c9a41b1e7b45ac354ba8275254e89", size = 7568415 },
+ { url = "https://files.pythonhosted.org/packages/5c/07/9219976135ce0cb32d2fa6ea5c6d0ad709013d9a17967312e149b98153a6/maturin-1.8.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8d8251a95682c83ea60988c804b620c181911cd824aa107b4a49ac5333c92968", size = 14527816 },
+ { url = "https://files.pythonhosted.org/packages/e6/04/fa009a00903acdd1785d58322193140bfe358595347c39f315112dabdf9e/maturin-1.8.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b9fc1a4354cac5e32c190410208039812ea88c4a36bd2b6499268ec49ef5de00", size = 7580446 },
+ { url = "https://files.pythonhosted.org/packages/9b/d4/414b2aab9bbfe88182b734d3aa1b4fef7d7701e50f6be48500378b8c8721/maturin-1.8.1-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:621e171c6b39f95f1d0df69a118416034fbd59c0f89dcaea8c2ea62019deecba", size = 7650535 },
+ { url = "https://files.pythonhosted.org/packages/f0/64/879418a8a0196013ec1fb19eada0781c04a30e8d6d9227e80f91275a4f5b/maturin-1.8.1-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:98f638739a5132962347871b85c91f525c9246ef4d99796ae98a2031e3df029f", size = 8006702 },
+ { url = "https://files.pythonhosted.org/packages/39/c2/605829324f8371294f70303aca130682df75318958efed246873d3d604ab/maturin-1.8.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:f9f5c47521924b6e515cbc652a042fe5f17f8747445be9d931048e5d8ddb50a4", size = 7368164 },
+ { url = "https://files.pythonhosted.org/packages/be/6c/30e136d397bb146b94b628c0ef7f17708281611b97849e2cf37847025ac7/maturin-1.8.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:0f4407c7353c31bfbb8cdeb82bc2170e474cbfb97b5ba27568f440c9d6c1fdd4", size = 7450889 },
+ { url = "https://files.pythonhosted.org/packages/1b/50/e1f5023512696d4e56096f702e2f68d6d9a30afe0a4eec82b0e27b8eb4e4/maturin-1.8.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:ec49cd70cad3c389946c6e2bc0bd50772a7fcb463040dd800720345897eec9bf", size = 9585819 },
+ { url = "https://files.pythonhosted.org/packages/b7/80/b24b5248d89d2e5982553900237a337ea098ca9297b8369ca2aa95549e0f/maturin-1.8.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08767d794de8f8a11c5c8b1b47a4ff9fb6ae2d2d97679e27030f2f509c8c2a0", size = 10920801 },
+ { url = "https://files.pythonhosted.org/packages/6e/f4/8ede7a662fabf93456b44390a5ad22630e25fb5ddaecf787251071b2e143/maturin-1.8.1-py3-none-win32.whl", hash = "sha256:d678407713f3e10df33c5b3d7a343ec0551eb7f14d8ad9ba6febeb96f4e4c75c", size = 6873556 },
+ { url = "https://files.pythonhosted.org/packages/9c/22/757f093ed0e319e9648155b8c9d716765442bea5bc98ebc58ad4ad5b0524/maturin-1.8.1-py3-none-win_amd64.whl", hash = "sha256:a526f90fe0e5cb59ffb81f4ff547ddc42e823bbdeae4a31012c0893ca6dcaf46", size = 7823153 },
+ { url = "https://files.pythonhosted.org/packages/a4/f5/051413e04f6da25069db5e76759ecdb8cd2a8ab4a94045b5a3bf548c66fa/maturin-1.8.1-py3-none-win_arm64.whl", hash = "sha256:e95f077fd2ddd2f048182880eed458c308571a534be3eb2add4d3dac55bf57f4", size = 6552131 },
+]
+
+[[package]]
+name = "mccabe"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350 },
+]
+
+[[package]]
+name = "mypy"
+version = "1.14.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mypy-extensions" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b9/eb/2c92d8ea1e684440f54fa49ac5d9a5f19967b7b472a281f419e69a8d228e/mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", size = 3216051 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9b/7a/87ae2adb31d68402da6da1e5f30c07ea6063e9f09b5e7cfc9dfa44075e74/mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb", size = 11211002 },
+ { url = "https://files.pythonhosted.org/packages/e1/23/eada4c38608b444618a132be0d199b280049ded278b24cbb9d3fc59658e4/mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0", size = 10358400 },
+ { url = "https://files.pythonhosted.org/packages/43/c9/d6785c6f66241c62fd2992b05057f404237deaad1566545e9f144ced07f5/mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d", size = 12095172 },
+ { url = "https://files.pythonhosted.org/packages/c3/62/daa7e787770c83c52ce2aaf1a111eae5893de9e004743f51bfcad9e487ec/mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b", size = 12828732 },
+ { url = "https://files.pythonhosted.org/packages/1b/a2/5fb18318a3637f29f16f4e41340b795da14f4751ef4f51c99ff39ab62e52/mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427", size = 13012197 },
+ { url = "https://files.pythonhosted.org/packages/28/99/e153ce39105d164b5f02c06c35c7ba958aaff50a2babba7d080988b03fe7/mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f", size = 9780836 },
+ { url = "https://files.pythonhosted.org/packages/da/11/a9422850fd506edbcdc7f6090682ecceaf1f87b9dd847f9df79942da8506/mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c", size = 11120432 },
+ { url = "https://files.pythonhosted.org/packages/b6/9e/47e450fd39078d9c02d620545b2cb37993a8a8bdf7db3652ace2f80521ca/mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1", size = 10279515 },
+ { url = "https://files.pythonhosted.org/packages/01/b5/6c8d33bd0f851a7692a8bfe4ee75eb82b6983a3cf39e5e32a5d2a723f0c1/mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8", size = 12025791 },
+ { url = "https://files.pythonhosted.org/packages/f0/4c/e10e2c46ea37cab5c471d0ddaaa9a434dc1d28650078ac1b56c2d7b9b2e4/mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f", size = 12749203 },
+ { url = "https://files.pythonhosted.org/packages/88/55/beacb0c69beab2153a0f57671ec07861d27d735a0faff135a494cd4f5020/mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1", size = 12885900 },
+ { url = "https://files.pythonhosted.org/packages/a2/75/8c93ff7f315c4d086a2dfcde02f713004357d70a163eddb6c56a6a5eff40/mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae", size = 9777869 },
+ { url = "https://files.pythonhosted.org/packages/43/1b/b38c079609bb4627905b74fc6a49849835acf68547ac33d8ceb707de5f52/mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", size = 11266668 },
+ { url = "https://files.pythonhosted.org/packages/6b/75/2ed0d2964c1ffc9971c729f7a544e9cd34b2cdabbe2d11afd148d7838aa2/mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", size = 10254060 },
+ { url = "https://files.pythonhosted.org/packages/a1/5f/7b8051552d4da3c51bbe8fcafffd76a6823779101a2b198d80886cd8f08e/mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", size = 11933167 },
+ { url = "https://files.pythonhosted.org/packages/04/90/f53971d3ac39d8b68bbaab9a4c6c58c8caa4d5fd3d587d16f5927eeeabe1/mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", size = 12864341 },
+ { url = "https://files.pythonhosted.org/packages/03/d2/8bc0aeaaf2e88c977db41583559319f1821c069e943ada2701e86d0430b7/mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89", size = 12972991 },
+ { url = "https://files.pythonhosted.org/packages/6f/17/07815114b903b49b0f2cf7499f1c130e5aa459411596668267535fe9243c/mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", size = 9879016 },
+ { url = "https://files.pythonhosted.org/packages/9e/15/bb6a686901f59222275ab228453de741185f9d54fecbaacec041679496c6/mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", size = 11252097 },
+ { url = "https://files.pythonhosted.org/packages/f8/b3/8b0f74dfd072c802b7fa368829defdf3ee1566ba74c32a2cb2403f68024c/mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", size = 10239728 },
+ { url = "https://files.pythonhosted.org/packages/c5/9b/4fd95ab20c52bb5b8c03cc49169be5905d931de17edfe4d9d2986800b52e/mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", size = 11924965 },
+ { url = "https://files.pythonhosted.org/packages/56/9d/4a236b9c57f5d8f08ed346914b3f091a62dd7e19336b2b2a0d85485f82ff/mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", size = 12867660 },
+ { url = "https://files.pythonhosted.org/packages/40/88/a61a5497e2f68d9027de2bb139c7bb9abaeb1be1584649fa9d807f80a338/mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", size = 12969198 },
+ { url = "https://files.pythonhosted.org/packages/54/da/3d6fc5d92d324701b0c23fb413c853892bfe0e1dbe06c9138037d459756b/mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", size = 9885276 },
+ { url = "https://files.pythonhosted.org/packages/a0/b5/32dd67b69a16d088e533962e5044e51004176a9952419de0370cdaead0f8/mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", size = 2752905 },
+]
+
+[[package]]
+name = "mypy-extensions"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
+]
+
+[[package]]
+name = "packaging"
+version = "24.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
+]
+
+[[package]]
+name = "pathspec"
+version = "0.12.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 },
+]
+
+[[package]]
+name = "platformdirs"
+version = "4.3.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 },
+]
+
+[[package]]
+name = "pluggy"
+version = "1.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
+]
+
+[[package]]
+name = "pylint"
+version = "3.3.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "astroid" },
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "dill" },
+ { name = "isort" },
+ { name = "mccabe" },
+ { name = "platformdirs" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+ { name = "tomlkit" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/17/fd/e9a739afac274a39596bbe562e9d966db6f3917fdb2bd7322ffc56da0ba2/pylint-3.3.3.tar.gz", hash = "sha256:07c607523b17e6d16e2ae0d7ef59602e332caa762af64203c24b41c27139f36a", size = 1516550 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/91/e1/26d55acea92b1ea4d33672e48f09ceeb274e84d7d542a4fb9a32a556db46/pylint-3.3.3-py3-none-any.whl", hash = "sha256:26e271a2bc8bce0fc23833805a9076dd9b4d5194e2a02164942cb3cdc37b4183", size = 521918 },
+]
+
+[[package]]
+name = "pytest"
+version = "7.4.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
+ { name = "iniconfig" },
+ { name = "packaging" },
+ { name = "pluggy" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/80/1f/9d8e98e4133ffb16c90f3b405c43e38d3abb715bb5d7a63a5a684f7e46a3/pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280", size = 1357116 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/51/ff/f6e8b8f39e08547faece4bd80f89d5a8de68a38b2d179cc1c4490ffa3286/pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8", size = 325287 },
+]
+
+[[package]]
+name = "pytest-cov"
+version = "5.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "coverage", extra = ["toml"] },
+ { name = "pytest" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/74/67/00efc8d11b630c56f15f4ad9c7f9223f1e5ec275aaae3fa9118c6a223ad2/pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857", size = 63042 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/3a/af5b4fa5961d9a1e6237b530eb87dd04aea6eb83da09d2a4073d81b54ccf/pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652", size = 21990 },
+]
+
+[[package]]
+name = "ruff"
+version = "0.9.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/80/63/77ecca9d21177600f551d1c58ab0e5a0b260940ea7312195bd2a4798f8a8/ruff-0.9.2.tar.gz", hash = "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0", size = 3553799 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/af/b9/0e168e4e7fb3af851f739e8f07889b91d1a33a30fca8c29fa3149d6b03ec/ruff-0.9.2-py3-none-linux_armv6l.whl", hash = "sha256:80605a039ba1454d002b32139e4970becf84b5fee3a3c3bf1c2af6f61a784347", size = 11652408 },
+ { url = "https://files.pythonhosted.org/packages/2c/22/08ede5db17cf701372a461d1cb8fdde037da1d4fa622b69ac21960e6237e/ruff-0.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b9aab82bb20afd5f596527045c01e6ae25a718ff1784cb92947bff1f83068b00", size = 11587553 },
+ { url = "https://files.pythonhosted.org/packages/42/05/dedfc70f0bf010230229e33dec6e7b2235b2a1b8cbb2a991c710743e343f/ruff-0.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbd337bac1cfa96be615f6efcd4bc4d077edbc127ef30e2b8ba2a27e18c054d4", size = 11020755 },
+ { url = "https://files.pythonhosted.org/packages/df/9b/65d87ad9b2e3def67342830bd1af98803af731243da1255537ddb8f22209/ruff-0.9.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b35259b0cbf8daa22a498018e300b9bb0174c2bbb7bcba593935158a78054d", size = 11826502 },
+ { url = "https://files.pythonhosted.org/packages/93/02/f2239f56786479e1a89c3da9bc9391120057fc6f4a8266a5b091314e72ce/ruff-0.9.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b6a9701d1e371bf41dca22015c3f89769da7576884d2add7317ec1ec8cb9c3c", size = 11390562 },
+ { url = "https://files.pythonhosted.org/packages/c9/37/d3a854dba9931f8cb1b2a19509bfe59e00875f48ade632e95aefcb7a0aee/ruff-0.9.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc53e68b3c5ae41e8faf83a3b89f4a5d7b2cb666dff4b366bb86ed2a85b481f", size = 12548968 },
+ { url = "https://files.pythonhosted.org/packages/fa/c3/c7b812bb256c7a1d5553433e95980934ffa85396d332401f6b391d3c4569/ruff-0.9.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8efd9da7a1ee314b910da155ca7e8953094a7c10d0c0a39bfde3fcfd2a015684", size = 13187155 },
+ { url = "https://files.pythonhosted.org/packages/bd/5a/3c7f9696a7875522b66aa9bba9e326e4e5894b4366bd1dc32aa6791cb1ff/ruff-0.9.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3292c5a22ea9a5f9a185e2d131dc7f98f8534a32fb6d2ee7b9944569239c648d", size = 12704674 },
+ { url = "https://files.pythonhosted.org/packages/be/d6/d908762257a96ce5912187ae9ae86792e677ca4f3dc973b71e7508ff6282/ruff-0.9.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a605fdcf6e8b2d39f9436d343d1f0ff70c365a1e681546de0104bef81ce88df", size = 14529328 },
+ { url = "https://files.pythonhosted.org/packages/2d/c2/049f1e6755d12d9cd8823242fa105968f34ee4c669d04cac8cea51a50407/ruff-0.9.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c547f7f256aa366834829a08375c297fa63386cbe5f1459efaf174086b564247", size = 12385955 },
+ { url = "https://files.pythonhosted.org/packages/91/5a/a9bdb50e39810bd9627074e42743b00e6dc4009d42ae9f9351bc3dbc28e7/ruff-0.9.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d18bba3d3353ed916e882521bc3e0af403949dbada344c20c16ea78f47af965e", size = 11810149 },
+ { url = "https://files.pythonhosted.org/packages/e5/fd/57df1a0543182f79a1236e82a79c68ce210efb00e97c30657d5bdb12b478/ruff-0.9.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b338edc4610142355ccf6b87bd356729b62bf1bc152a2fad5b0c7dc04af77bfe", size = 11479141 },
+ { url = "https://files.pythonhosted.org/packages/dc/16/bc3fd1d38974f6775fc152a0554f8c210ff80f2764b43777163c3c45d61b/ruff-0.9.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:492a5e44ad9b22a0ea98cf72e40305cbdaf27fac0d927f8bc9e1df316dcc96eb", size = 12014073 },
+ { url = "https://files.pythonhosted.org/packages/47/6b/e4ca048a8f2047eb652e1e8c755f384d1b7944f69ed69066a37acd4118b0/ruff-0.9.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:af1e9e9fe7b1f767264d26b1075ac4ad831c7db976911fa362d09b2d0356426a", size = 12435758 },
+ { url = "https://files.pythonhosted.org/packages/c2/40/4d3d6c979c67ba24cf183d29f706051a53c36d78358036a9cd21421582ab/ruff-0.9.2-py3-none-win32.whl", hash = "sha256:71cbe22e178c5da20e1514e1e01029c73dc09288a8028a5d3446e6bba87a5145", size = 9796916 },
+ { url = "https://files.pythonhosted.org/packages/c3/ef/7f548752bdb6867e6939489c87fe4da489ab36191525fadc5cede2a6e8e2/ruff-0.9.2-py3-none-win_amd64.whl", hash = "sha256:c5e1d6abc798419cf46eed03f54f2e0c3adb1ad4b801119dedf23fcaf69b55b5", size = 10773080 },
+ { url = "https://files.pythonhosted.org/packages/0e/4e/33df635528292bd2d18404e4daabcd74ca8a9853b2e1df85ed3d32d24362/ruff-0.9.2-py3-none-win_arm64.whl", hash = "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6", size = 10001738 },
+]
+
+[[package]]
+name = "rusty-logger"
+version = "0.4.0"
+source = { virtual = "." }
+
+[package.dev-dependencies]
+dev = [
+ { name = "black" },
+ { name = "isort" },
+ { name = "maturin" },
+ { name = "mypy" },
+ { name = "pylint" },
+ { name = "pytest" },
+ { name = "pytest-cov" },
+ { name = "ruff" },
+]
+
+[package.metadata]
+
+[package.metadata.requires-dev]
+dev = [
+ { name = "black", specifier = ">=24.3.0,<25.0.0" },
+ { name = "isort", specifier = ">=5.13.2,<6.0.0" },
+ { name = "maturin", specifier = ">=1.4.0,<2.0.0" },
+ { name = "mypy", specifier = ">=1.0.0,<2.0.0" },
+ { name = "pylint", specifier = ">=3.0.0,<4.0.0" },
+ { name = "pytest", specifier = ">=7.0.0,<8.0.0" },
+ { name = "pytest-cov", specifier = ">=5.0.0,<6.0.0" },
+ { name = "ruff", specifier = ">=0.1.0,<1.0.0" },
+]
+
+[[package]]
+name = "tomli"
+version = "2.2.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 },
+ { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 },
+ { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 },
+ { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 },
+ { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 },
+ { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 },
+ { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 },
+ { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 },
+ { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 },
+ { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 },
+ { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 },
+ { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 },
+ { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 },
+ { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 },
+ { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 },
+ { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 },
+ { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 },
+ { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 },
+ { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 },
+ { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 },
+ { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 },
+ { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 },
+ { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 },
+ { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 },
+ { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 },
+ { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 },
+ { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 },
+ { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 },
+ { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 },
+ { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 },
+ { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 },
+]
+
+[[package]]
+name = "tomlkit"
+version = "0.13.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b1/09/a439bec5888f00a54b8b9f05fa94d7f901d6735ef4e55dcec9bc37b5d8fa/tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79", size = 192885 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955 },
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.12.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
+]
diff --git a/pyproject.toml b/pyproject.toml
deleted file mode 100644
index 5cd05de..0000000
--- a/pyproject.toml
+++ /dev/null
@@ -1,133 +0,0 @@
-[tool.poetry]
-name = "rusty-logger"
-version = "0.3.2"
-description = "Rust-based python logger"
-readme = "README.md"
-authors = ["Thorrester <48217609+thorrester@users.noreply.github.com>"]
-
-[tool.poetry.dependencies]
-# required dependencies
-python = ">=3.8,<3.13"
-smokeshow = "^0.4.0"
-
-
-[tool.poetry.group.dev]
-optional = true
-
-[tool.poetry.group.dev.dependencies]
-pytest = "^7.1.2"
-pytest-cov = "^3.0.0"
-ruff = "^0.1.0"
-mypy = "^1"
-black = "22.3.0"
-pylint = "^2.17.5"
-python-json-logger = "^2.0.7"
-fastapi = "^0.109.2"
-uvicorn = "^0.23"
-gunicorn = "^22"
-
-[tool.ruff]
-line-length = 120
-
-
-[tool.isort]
-profile = "black"
-
-[tool.black]
-line-length = 120
-target-version = ['py310']
-include = '\.pyi?$'
-
-[tool.pylint.MASTER]
-load-plugins = ["pylint.extensions.docparams"]
-
-[tool.pylint.messages_control]
-max-line-length = 120
-disable = [
- "too-few-public-methods",
- "design",
- "duplicate-code",
- "missing-class-docstring",
- "missing-function-docstring",
- "missing-module-docstring",
- "too-many-nested-blocks",
- "unused-argument",
- "fixme",
- "import-outside-toplevel"
-]
-
-
-[tool.flake8]
-# This section is just a doc placeholder..see setup.cfg
-max-complexity = 10
-format = "pylint"
-statistics = "True"
-max-line-length = 125
-# ignore non-PEP8 lints
-ignore = ["E203", "W503", "W0511"]
-
-[tool.pytest.ini_options]
-log_cli = true
-log_cli_level = "INFO"
-testpaths = ["tests"]
-
-
-[tool.mypy]
-# platform configuration
-python_version = "3.9"
-platform = "linux"
-mypy_path = "stubs"
-ignore_missing_imports = true
-
-
-[build-system]
-requires = ["maturin>=1.2,<2.0"]
-build-backend = "maturin"
-
-[project]
-name = "rusty-logger"
-description = 'Super fast logging for python'
-requires-python = ">=3.8, <3.13"
-authors = [
- {name = 'Steven Forrester', email = 'sjforrester32@gmail.com'}
-]
-classifiers = [
- "Programming Language :: Rust",
- "Programming Language :: Python :: Implementation :: CPython",
- "Programming Language :: Python :: Implementation :: PyPy",
- "Development Status :: 5 - Production/Stable",
- "Environment :: Console",
- "License :: OSI Approved :: MIT License",
- "Operating System :: OS Independent",
- "Programming Language :: Python",
- "Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3 :: Only",
- "Programming Language :: Python :: 3.9",
- "Programming Language :: Python :: 3.10",
- "Programming Language :: Python :: 3.11",
- "Programming Language :: Python :: 3.12",
- "Topic :: Scientific/Engineering",
- 'Intended Audience :: Developers',
- 'Intended Audience :: Information Technology',
- "Intended Audience :: Science/Research",
- 'Operating System :: POSIX :: Linux',
- 'Operating System :: Microsoft :: Windows',
- 'Operating System :: MacOS',
- 'Typing :: Typed',
-]
-dynamic = [
- 'license',
- 'readme',
-]
-
-[project.urls]
-"Homepage" = "https://github.com/thorrester/rusty-logger"
-"Source Code" = "https://github.com/thorrester/rusty-logger"
-"Issues" = "https://github.com/thorrester/rusty-logger/issues"
-
-
-[tool.maturin]
-python-source = "python"
-module-name = "rusty_logger._rusty_logger"
-bindings = 'pyo3'
-features = ["pyo3/extension-module"]
\ No newline at end of file
diff --git a/python/rusty_logger/__init__.py b/python/rusty_logger/__init__.py
deleted file mode 100644
index a86a016..0000000
--- a/python/rusty_logger/__init__.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# pylint: disable=no-name-in-module
-
-
-from ._rusty_logger import Logger, LogConfig, JsonConfig, LogLevel, LogFileConfig
-from .version import __version__
-
-__all__ = [
- "Logger",
- "LogConfig",
- "JsonConfig",
- "LogLevel",
- "LogFileConfig",
- "__version__",
-]
diff --git a/python/rusty_logger/_rusty_logger.pyi b/python/rusty_logger/_rusty_logger.pyi
deleted file mode 100644
index 653c8bd..0000000
--- a/python/rusty_logger/_rusty_logger.pyi
+++ /dev/null
@@ -1,225 +0,0 @@
-from typing import Optional
-
-class LogLevel:
- """Enum for log levels"""
-
- @property
- def INFO(self) -> str:
- """The INFO log level"""
- ...
- @property
- def DEBUG(self) -> str:
- """The DEBUG log level"""
- ...
- @property
- def WARN(self) -> str:
- """The WARNING log level"""
- ...
- @property
- def ERROR(self) -> str:
- """The ERROR log level"""
- ...
- @property
- def TRACE(self) -> str:
- """The TRACE log level"""
- ...
-
-class JsonConfig:
- def __init__(
- self,
- flatten: bool = True,
- ):
- """Creates logger json configuration
-
- Args:
- flatten:
- Whether to flatten the any fields that are passed
- """
- ...
- @property
- def flatten(self) -> bool:
- """Whether to flatten any fields that are passed"""
- ...
-
-class LogFileConfig:
- def __init__(self, filename: str = "log/logs.log", rotate: str = "never"):
- """Creates logger file configuration for recording logs
-
- Args:
- filename:
- The name of the file to write logs to
- rotate:
- The rotation policy for the log file. Can be "never", "daily", "hourly", or "minutely"
- """
- ...
-
-class LogConfig:
- def __init__(
- self,
- stdout: bool = True,
- stderr: bool = False,
- level: str = "INFO",
- name: Optional[str] = None,
- app_env: str = "development",
- time_format: str = "[year]-[month]-[day]T[hour repr:24]:[minute]:[second]::[subsecond digits:4]",
- lock_guard: bool = False,
- thread_id: bool = False,
- color: bool = False,
- json_config: Optional[JsonConfig] = None,
- file_config: Optional[LogFileConfig] = None,
- ):
- """Creates logger configuration
-
- Args:
- stdout:
- Whether to log to stdout
- stderr:
- Whether to log to stderr
- filename:
- Optional name of log file to write to. Can be a path (logs/test.log)
- or just a name (test.log)
- level:
- The level to log at
- app_env:
- The environment name to associate with logs. Defaults to "development"
- name:
- Name to record when logging events. Usually this is the name of the module that is using the logger
- time_format:
- The time format to use for logs
- lock_guard:
- Boolean indicating whether to lock this logger to current context. Usually this will be false
- thread_id:
- Whether to record the thread id in logs
- color:
- Whether to colorize logs
- json_config:
- Optional json logger configuration
- file_config:
- Optional file logger configuration
- """
- ...
- @property
- def stdout(self) -> bool:
- """Whether to log to stdout"""
- ...
- @property
- def stderr(self) -> bool:
- """Whether to log to stderr"""
- ...
- @property
- def filename(self) -> Optional[str]:
- """Optional name of log file to write to. Can be a path (logs/test.log)
- or just a name (test.log)"""
- ...
- @property
- def level(self) -> str:
- """The level to log at"""
- ...
- @property
- def app_env(self) -> Optional[str]:
- """The environment name to associate with logs. Defaults to "development"""
- ...
- @property
- def name(self) -> bool:
- """Name to record when logging events. Usually this is the name of the module that is using the logger"""
- ...
- @property
- def time_format(self) -> Optional[str]:
- """The time format to use for logs"""
- ...
- @property
- def json_config(self) -> Optional[JsonConfig]:
- """Optional json logger configuration"""
- ...
- @property
- def lock_guard(self) -> bool:
- """Boolean indicating whether to lock this logger to current context"""
- ...
- @property
- def thread_id(self) -> bool:
- """Whether to record the thread id in logs"""
- ...
- @property
- def color(self) -> bool:
- """Whether to colorize logs"""
- ...
-
-class Logger:
- @classmethod
- def get_logger(self, name: Optional[str] = None, config: Optional[LogConfig] = None) -> Logger:
- """Gets a new logger.
-
- Args:
- name:
- Name to record with logger. Usually this is the name of the module that is using the logger
- config:
- The configuration for the logger
-
- Returns:
- `RustyLogger`
- """
- ...
- def set_level(self, level: str) -> None:
- """Sets the log level of the logger.
-
- Args:
- level:
- The level to log at.
- """
- ...
- @property
- def config(self) -> LogConfig:
- """The configuration for the logger."""
- ...
- def info(self, message: str, *args, **kwargs) -> None:
- """Logs a message at the INFO level.
-
- Args:
- message:
- The message to log
- args:
- Args to format the message with
- kwargs:
- Kwargs to format the message with
- """
- ...
- def debug(self, message: str, *args) -> None:
- """Logs a message at the DEBUG level.
-
- Args:
- message:
- The message to log
- args:
- Args to format the message with
- """
- ...
- def warning(self, message: str, *args) -> None:
- """Logs a message at the WARNING level.
-
- Args:
- message:
- The message to log
- args:
- Args to format the message with
- """
- ...
- def error(self, message: str, *args) -> None:
- """Logs a message at the ERROR level.
-
- Args:
- message:
- The message to log
- args:
- Args to format the message with
- """
- ...
- def trace(self, message: str, *args) -> None:
- """Logs a message at the TRACE level.
-
- Args:
- message:
- The message to log
- args:
- Args to format the message with
- """
- ...
diff --git a/release-plz.toml b/release-plz.toml
new file mode 100644
index 0000000..5d5c369
--- /dev/null
+++ b/release-plz.toml
@@ -0,0 +1,14 @@
+[workspace]
+dependencies_update = true
+changelog_update = true
+pr_labels = ["release"]
+semver_check = true
+publish_timeout = "10m" # set a timeout for `cargo publish`
+git_tag_enable = false
+git_release_enable = false
+publish = true
+repo_url = "https://github.com/demml/rusty-logger"
+
+[[package]]
+name = "py-rusty-logger"
+publish = false
\ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
deleted file mode 100644
index 1faed58..0000000
--- a/src/lib.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-mod logger;
-
-use crate::logger::pylogger::{LogLevel, PyLogger};
-use crate::logger::rust_logger::{JsonConfig, LogConfig, LogFileConfig};
-use pyo3::prelude::*;
-
-/// Python implementation for the Rusty Logger
-#[pymodule]
-fn _rusty_logger(_py: Python, m: &PyModule) -> PyResult<()> {
- m.add_class::()?;
- m.add_class::()?;
- m.add_class::()?;
- m.add_class::()?;
- m.add_class::()?;
- Ok(())
-}
diff --git a/src/logger/mod.rs b/src/logger/mod.rs
deleted file mode 100644
index 53e4c20..0000000
--- a/src/logger/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub mod pylogger;
-pub mod rust_logger;
diff --git a/src/logger/pylogger.rs b/src/logger/pylogger.rs
deleted file mode 100644
index 9562419..0000000
--- a/src/logger/pylogger.rs
+++ /dev/null
@@ -1,226 +0,0 @@
-use crate::logger::rust_logger::{get_file_name, LogConfig, RustLogger};
-use owo_colors::AnsiColors;
-use owo_colors::OwoColorize;
-use pyo3::prelude::*;
-use pyo3::types::{PyTuple, PyType};
-use serde_json::{json, to_string_pretty};
-use std::collections::HashMap;
-
-#[derive(FromPyObject, Debug)]
-enum PyTypes<'a> {
- CatchAll(&'a PyAny),
-}
-
-#[pyclass]
-pub struct LogLevel {}
-
-#[pymethods]
-#[allow(non_snake_case)]
-impl LogLevel {
- #[classattr]
- fn DEBUG() -> String {
- "DEBUG".to_string()
- }
-
- #[classattr]
- fn WARN() -> String {
- "WARN".to_string()
- }
-
- #[classattr]
- fn INFO() -> String {
- "INFO".to_string()
- }
-
- #[classattr]
- fn ERROR() -> String {
- "ERROR".to_string()
- }
-
- #[classattr]
- fn TRACE() -> String {
- "TRACE".to_string()
- }
-}
-
-pub fn parse_args(args: &PyTuple) -> Option> {
- let args: Option> = if args.is_empty() {
- None
- } else {
- Some(
- args.iter()
- .map(|x| match x.extract::() {
- Ok(PyTypes::CatchAll(c)) => c.to_string(),
- Err(e) => {
- println!("Error: {}", e);
- "".to_string()
- }
- })
- .collect::>(),
- )
- };
-
- args
-}
-
-#[pyclass(name = "Logger", subclass)]
-pub struct PyLogger {
- logger: RustLogger,
-
- #[pyo3(get, set)]
- pub config: LogConfig,
-}
-
-#[pymethods]
-#[allow(unused_variables)]
-impl PyLogger {
- /// Create a new logger
- ///
- /// # Arguments
- /// * `config` - The log config to use
- ///
- /// # Returns
- /// A new logger
- #[classmethod]
- pub fn get_logger(cls: &PyType, name: Option, config: Option) -> PyLogger {
- let mut log_config = config.unwrap_or_else(|| {
- // get default
- LogConfig::new(
- None, None, None, None, None, None, None, None, None, None, None,
- )
- });
-
- // in case where user provides log config and name
- // check if user provided name in log config first
- if log_config.name.is_none() {
- log_config.name = name.map(|val| get_file_name(&val));
- }
-
- let logger = RustLogger::new(&log_config);
-
- PyLogger {
- logger,
- config: log_config,
- }
- }
-
- /// Set the log level for the logger
- ///
- /// # Arguments
- /// * `level` - The log level to set
- ///
- pub fn set_level(&mut self, level: String) {
- let mut config = self.config.clone();
- config.log_level(level);
- self.logger.reload_level(&config.level).unwrap()
- }
-
- fn check_format_kwargs(&self, message: &str, kwargs: Option>) -> String {
- if self.config.file_config.is_some() {
- return message.to_string();
- };
-
- if kwargs.is_some() {
- let kwargs = kwargs.unwrap();
- let color = kwargs.get("color").expect("No color provided");
-
- let ansi_color = match *color {
- "red" => AnsiColors::Red,
- "green" => AnsiColors::Green,
- "yellow" => AnsiColors::Yellow,
- "blue" => AnsiColors::Blue,
- "magenta" => AnsiColors::Magenta,
- "cyan" => AnsiColors::Cyan,
- "white" => AnsiColors::White,
- "black" => AnsiColors::Black,
- _ => AnsiColors::Black,
- };
-
- message.color(ansi_color).to_string()
- } else {
- message.to_string()
- }
- }
-
- /// Log at INFO level
- ///
- /// # Arguments
- /// * `message` - The message to log
- /// * `args` - The arguments to log
- /// * `kwargs` - Optional formatting kwargs
- ///
- #[pyo3(signature = (message, *args, **kwargs))]
- pub fn info(&self, message: &str, args: &PyTuple, kwargs: Option>) {
- let args = parse_args(args);
- let message = self.check_format_kwargs(message, kwargs);
- self.logger.info(&message, args, &self.config);
- }
-
- /// Log at DEBUG level
- ///
- /// # Arguments
- /// * `message` - The message to log
- /// * `args` - The arguments to log
- /// * `kwargs` - Optional formatting kwargs
- ///
- #[pyo3(signature = (message, *args, **kwargs))]
- pub fn debug(&self, message: &str, args: &PyTuple, kwargs: Option>) {
- let args = parse_args(args);
- let message = self.check_format_kwargs(message, kwargs);
- self.logger.debug(&message, args, &self.config);
- }
-
- /// Log at WARN level
- ///
- /// # Arguments
- /// * `message` - The message to log
- /// * `args` - The arguments to log
- /// * `kwargs` - Optional formatting kwargs
- ///
- #[pyo3(signature = (message, *args, **kwargs))]
- pub fn warning(&self, message: &str, args: &PyTuple, kwargs: Option>) {
- let args = parse_args(args);
- let message = self.check_format_kwargs(message, kwargs);
- self.logger.warning(&message, args, &self.config);
- }
-
- /// Log at ERROR level
- ///
- /// # Arguments
- /// * `message` - The message to log
- /// * `args` - The arguments to log
- /// * `kwargs` - Optional formatting kwargs
- ///
- #[pyo3(signature = (message, *args, **kwargs))]
- pub fn error(&self, message: &str, args: &PyTuple, kwargs: Option>) {
- let args = parse_args(args);
- let message = self.check_format_kwargs(message, kwargs);
- self.logger.error(&message, args, &self.config);
- }
-
- /// Log at TRACE level
- ///
- /// # Arguments
- /// * `message` - The message to log
- /// * `args` - The arguments to log
- /// * `kwargs` - Optional formatting kwargs
- ///
- #[pyo3(signature = (message, *args, **kwargs))]
- pub fn trace(&self, message: &str, args: &PyTuple, kwargs: Option>) {
- let args = parse_args(args);
- let message = self.check_format_kwargs(message, kwargs);
- self.logger.trace(&message, args, &self.config);
- }
-
- /// String magic method for PyLogger class
- pub fn __str__(&self) -> PyResult {
- let json = json!({
- "type": "Logger",
- "name": self.config.name,
- "level": self.config.app_env,
- "config": self.config,
- });
-
- Ok(to_string_pretty(&json).unwrap())
- }
-}
diff --git a/src/logger/rust_logger.rs b/src/logger/rust_logger.rs
deleted file mode 100644
index 2bf6270..0000000
--- a/src/logger/rust_logger.rs
+++ /dev/null
@@ -1,768 +0,0 @@
-use pyo3::prelude::*;
-use serde::{Deserialize, Serialize};
-use std::env;
-use std::io;
-use std::path::Path;
-use tracing_core::dispatcher::DefaultGuard;
-use tracing_subscriber::filter::LevelFilter;
-use tracing_subscriber::layer::Layered;
-
-use dynfmt::{Format, SimpleCurlyFormat};
-use owo_colors::OwoColorize;
-use time::format_description::FormatItem;
-use tracing_subscriber::fmt::time::UtcTime;
-use tracing_subscriber::fmt::MakeWriter;
-use tracing_subscriber::prelude::__tracing_subscriber_Layer;
-use tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt;
-use tracing_subscriber::registry::LookupSpan;
-use tracing_subscriber::reload;
-use tracing_subscriber::util::SubscriberInitExt;
-use tracing_subscriber::Layer;
-use tracing_subscriber::Registry;
-
-static DEFAULT_TIME_PATTERN: &str =
- "[year]-[month]-[day]T[hour repr:24]:[minute]:[second]::[subsecond digits:4]";
-
-type ReloadHandle =
- reload::Handle + Send + Sync>>, Registry>>;
-
-#[pyclass(dict)]
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct JsonConfig {
- #[pyo3(get, set)]
- pub flatten: bool,
-}
-
-#[pymethods]
-impl JsonConfig {
- #[new]
- pub fn new(flatten: Option) -> Self {
- JsonConfig {
- flatten: flatten.unwrap_or(true),
- }
- }
-
- pub fn __str__(&self) -> PyResult {
- Ok(serde_json::to_string_pretty(&self).unwrap())
- }
-}
-
-#[pyclass(dict)]
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct LogFileConfig {
- #[pyo3(get, set)]
- pub filename: String,
-
- #[pyo3(get, set)]
- pub rotate: String,
-}
-
-#[pymethods]
-impl LogFileConfig {
- #[new]
- pub fn new(filename: Option, rotate: Option) -> Self {
- LogFileConfig {
- filename: filename.unwrap_or("log/logs.log".to_string()),
- rotate: rotate.unwrap_or("never".to_string()),
- }
- }
-
- pub fn __str__(&self) -> PyResult {
- Ok(serde_json::to_string_pretty(&self).unwrap())
- }
-}
-
-#[pyclass]
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct LogConfig {
- #[pyo3(get, set)]
- pub stdout: bool,
-
- #[pyo3(get, set)]
- pub stderr: bool,
-
- #[pyo3(get, set)]
- pub level: String,
-
- #[pyo3(get, set)]
- pub app_env: String,
-
- #[pyo3(get, set)]
- pub name: Option,
-
- #[pyo3(get, set)]
- pub time_format: String,
-
- #[pyo3(get, set)]
- pub json_config: Option,
-
- #[pyo3(get, set)]
- pub file_config: Option,
-
- #[pyo3(get, set)]
- pub lock_guard: bool,
-
- #[pyo3(get, set)]
- pub thread_id: bool,
-
- #[pyo3(get, set)]
- pub color: bool,
-}
-
-#[pymethods]
-#[allow(clippy::too_many_arguments)]
-impl LogConfig {
- // py init
- #[new]
- pub fn new(
- stdout: Option,
- stderr: Option,
- level: Option,
- app_env: Option,
- name: Option,
- time_format: Option,
- json_config: Option,
- file_config: Option,
- lock_guard: Option,
- thread_id: Option,
- color: Option,
- ) -> Self {
- let log_env = match app_env {
- Some(val) => val,
- None => match env::var("APP_ENV") {
- Ok(val) => val,
- Err(_e) => "development".to_string(),
- },
- };
-
- let name = name.map(|val| get_file_name(&val));
-
- let stdout = stdout.unwrap_or(false);
- let stderr = stderr.unwrap_or(false);
-
- let stdout = if !stdout && !stderr && file_config.is_none() {
- let msg = format!(
- "{}: {}. {}",
- "LogConfig".bold().red(),
- "No output specified",
- "Defaulting to stdout".green(),
- );
- println!("{}", msg);
-
- true
- } else {
- stdout
- };
-
- LogConfig {
- stdout,
- stderr,
- level: level.unwrap_or_else(|| "INFO".to_string()),
- app_env: log_env,
- name,
- time_format: time_format.unwrap_or_else(|| DEFAULT_TIME_PATTERN.to_string()),
- json_config,
- file_config,
- lock_guard: lock_guard.unwrap_or(false),
- thread_id: thread_id.unwrap_or(false),
- color: color.unwrap_or(false),
- }
- }
-
- pub fn log_level(&mut self, level: String) {
- self.level = level;
- }
-
- pub fn update_name(&mut self, name: String) {
- let filename = get_file_name(&name);
- self.name = Some(filename);
- }
-
- pub fn __str__(&self) -> PyResult {
- Ok(serde_json::to_string_pretty(&self).unwrap())
- }
-}
-
-/// Get the name of the file
-///
-/// # Arguments
-///
-/// * `name` - The name of the file
-///
-pub fn get_file_name(filename: &str) -> String {
- Path::new(&filename)
- .file_name()
- .and_then(|s| s.to_str())
- .unwrap()
- .to_string()
-}
-
-#[allow(clippy::single_char_pattern)]
-fn get_log_directory(output: &str) -> String {
- let file_splits = output.split("/").to_owned().collect::>().clone();
- let directory = file_splits[..file_splits.len() - 1].join("/");
-
- if directory.is_empty() {
- "./logs".to_string()
- } else {
- directory
- }
-}
-
-fn get_file_params(file_config: &LogFileConfig) -> (String, String) {
- let directory = get_log_directory(&file_config.filename);
- let file_name_prefix = get_file_name(&file_config.filename);
-
- (directory, file_name_prefix)
-}
-
-/// Get the file appender
-///
-/// # Arguments
-///
-/// * `rotate` - The rotation type
-/// * `directory` - The directory to write the file to
-/// * `file_name_prefix` - The prefix of the file name
-///
-/// # Returns
-///
-/// * `RollingFileAppender` - The file appender
-fn get_file_appender(
- rotate: &str,
- directory: &str,
- file_name_prefix: &str,
-) -> tracing_appender::rolling::RollingFileAppender {
- if rotate == "hourly" {
- tracing_appender::rolling::hourly(directory, file_name_prefix)
- } else if rotate == "daily" {
- tracing_appender::rolling::daily(directory, file_name_prefix)
- } else if rotate == "minutely" {
- tracing_appender::rolling::minutely(directory, file_name_prefix)
- } else {
- tracing_appender::rolling::never(directory, file_name_prefix)
- }
-}
-
-#[allow(clippy::len_zero)] // len tends to be faster than is_empty in tests
-fn format_string(message: &str, args: &Vec) -> String {
- if args.len() > 0 {
- SimpleCurlyFormat
- .format(message, args)
- .unwrap_or_else(|_| message.into())
- .to_string()
- } else {
- message.to_string()
- }
-}
-
-/// Rust logging class
-///
-/// # Arguments
-///
-/// * `output` - The output of the logger. Either "stdout" or "stderr"
-/// * `level` - The level of the logger. Either "info", "debug", "warn", or "error"
-/// * `name` - The name of the file
-///
-#[allow(dead_code)]
-pub struct RustLogger {
- reload_handle: ReloadHandle,
- pub guard: Option,
-}
-
-impl RustLogger {
- pub fn get_timer(time_format: String) -> UtcTime>> {
- let time = Box::new(time_format);
- let time_format_result = time::format_description::parse(Box::leak(time).as_str());
-
- // handle invalid user time format
- // Very rare that this would fail but let's handle it anyway
- let time_format = time_format_result.unwrap_or_else(|error| {
- println!("{}: {}", "Invalid time format:".bold().red(), error);
- println!("Defaulting to pattern: {}", DEFAULT_TIME_PATTERN.green());
-
- time::format_description::parse(DEFAULT_TIME_PATTERN).unwrap()
- });
-
- UtcTime::new(time_format)
- }
-
- pub fn get_level_filter(level: &str) -> LevelFilter {
- match level {
- "DEBUG" => LevelFilter::DEBUG,
- "INFO" => LevelFilter::INFO,
- "WARN" => LevelFilter::WARN,
- "ERROR" => LevelFilter::ERROR,
- "TRACE" => LevelFilter::TRACE,
- _ => LevelFilter::INFO,
- }
- }
-
- /// Create a new logger
- ///
- /// # Arguments
- ///
- /// * `log_config` - The configuration for the logger
- ///
- /// # Returns
- /// RustLogger
- pub fn new(log_config: &LogConfig) -> RustLogger {
- let layers = RustLogger::build_layers(log_config);
- let filter = RustLogger::get_level_filter(&log_config.level);
- let (filter, reload_handle) = reload::Layer::new(filter);
-
- // in case we want to lock the guard (default behavior is not to lock)
- if log_config.lock_guard {
- let guard = tracing_subscriber::registry()
- .with(layers)
- .with(filter)
- .set_default();
-
- Self {
- reload_handle,
- guard: Some(guard),
- }
-
- // Don't lock guard (set global default subscriber)
- } else {
- let subscriber_result = tracing_subscriber::registry()
- .with(layers)
- .with(filter)
- .try_init();
-
- match subscriber_result {
- Ok(val) => val,
- Err(_e) => (),
- }
-
- Self {
- reload_handle,
- guard: None,
- }
- }
- }
-
- /// Reload the logger level
- pub fn reload_level(&mut self, level: &str) -> Result<(), reload::Error> {
- let filter = RustLogger::get_level_filter(level);
- self.reload_handle.reload(filter)
- }
-
- /// Build the json layer for the logger
- ///
- /// # Arguments
- ///
- /// * `log_config` - The configuration for the logger
- /// * `writer` - The writer for the logger
- ///
- /// # Returns
- /// * `Box + Send + Sync>` - The layer for the logger
- fn construct_json_layer(
- log_config: &LogConfig,
- writer: W2,
- ) -> Box + Send + Sync>
- where
- S: tracing_core::Subscriber,
- W2: for<'writer> MakeWriter<'writer> + 'static + Send + Sync,
- for<'a> S: LookupSpan<'a>,
- {
- let flatten = log_config.json_config.as_ref().unwrap().flatten;
- let timer = RustLogger::get_timer(log_config.time_format.clone());
-
- tracing_subscriber::fmt::layer()
- .with_target(false)
- .json()
- .flatten_event(flatten)
- .with_thread_ids(log_config.thread_id)
- .with_timer(timer)
- .with_writer(writer)
- .with_ansi(log_config.color)
- .boxed()
- }
-
- /// Build the json layers for the logger
- ///
- /// # Arguments
- ///
- /// * `log_config` - The configuration for the logger
- ///
- /// # Returns
- /// * `Vec + Send + Sync>>` - The layers for the logger
- fn construct_json_layers(log_config: &LogConfig) -> Vec + Send + Sync>>
- where
- S: tracing_core::Subscriber,
- for<'a> S: LookupSpan<'a>,
- {
- let mut layers = Vec::new();
-
- if log_config.stdout {
- layers.push(RustLogger::construct_json_layer(log_config, io::stdout));
- }
-
- if log_config.stderr {
- layers.push(RustLogger::construct_json_layer(log_config, io::stderr));
- }
-
- if log_config.file_config.is_some() {
- let (directory, file_name_prefix) =
- get_file_params(log_config.file_config.as_ref().unwrap());
- let file_appender = get_file_appender(
- &log_config.file_config.as_ref().unwrap().rotate,
- &directory,
- &file_name_prefix,
- );
- layers.push(RustLogger::construct_json_layer(log_config, file_appender));
- }
-
- layers
- }
-
- fn construct_cmd_layer(
- log_config: &LogConfig,
- writer: W2,
- ) -> Box + Send + Sync>
- where
- S: tracing_core::Subscriber,
- W2: for<'writer> MakeWriter<'writer> + 'static + Send + Sync,
- for<'a> S: LookupSpan<'a>,
- {
- let timer = RustLogger::get_timer(log_config.time_format.clone());
- tracing_subscriber::fmt::layer()
- .with_target(false)
- .with_thread_ids(log_config.thread_id)
- .with_timer(timer)
- .with_writer(writer)
- .with_ansi(log_config.color)
- .boxed()
- }
-
- /// Build the layers for the logger
- ///
- /// # Arguments
- ///
- /// * `log_config` - The configuration for the logger
- ///
- /// # Returns
- ///
- /// * `Vec + Send + Sync>>` - The layers for the logger
- fn construct_cmd_layers(log_config: &LogConfig) -> Vec + Send + Sync>>
- where
- S: tracing_core::Subscriber,
- for<'a> S: LookupSpan<'a>,
- {
- let mut layers = Vec::new();
- // set time format (applies to all layers)
-
- if log_config.stdout {
- let writer = io::stdout;
- layers.push(RustLogger::construct_cmd_layer(log_config, writer));
- }
-
- if log_config.stderr {
- let writer = io::stderr;
- layers.push(RustLogger::construct_cmd_layer(log_config, writer));
- }
-
- if log_config.file_config.is_some() {
- let (directory, file_name_prefix) =
- get_file_params(log_config.file_config.as_ref().unwrap());
- let writer = get_file_appender(
- &log_config.file_config.as_ref().unwrap().rotate,
- &directory,
- &file_name_prefix,
- );
-
- layers.push(RustLogger::construct_cmd_layer(log_config, writer));
- }
-
- layers
- }
-
- /// Build the layers for the logger
- ///
- /// # Arguments
- ///
- /// * `log_config` - The configuration for the logger
- ///
- /// # Returns
- ///
- /// * `Vec + Send + Sync>>` - The layers for the logger
- fn build_layers(log_config: &LogConfig) -> Vec + Send + Sync>>
- where
- S: tracing_core::Subscriber,
- for<'a> S: LookupSpan<'a>,
- {
- if log_config.json_config.is_some() {
- RustLogger::construct_json_layers(log_config)
- } else {
- RustLogger::construct_cmd_layers(log_config)
- }
- }
-
- /// Log an info message
- ///
- /// # Arguments
- ///
- /// * `message` - The message to log
- /// * `args` - The arguments to log
- /// * `log_config` - The configuration for the logger
- ///
- pub fn info(&self, message: &str, args: Option>, log_config: &LogConfig) {
- // format string first
- let msg = match args {
- Some(val) => format_string(message, &val),
- None => message.to_string(),
- };
-
- match log_config.name {
- Some(ref val) => tracing::info!(
- message = msg,
- app_env = log_config.app_env,
- name = val.as_str()
- ),
- None => tracing::info!(message = msg, app_env = log_config.app_env),
- };
- }
-
- /// Log a debug message
- ///
- /// # Arguments
- ///
- /// * `message` - The message to log
- /// * `args` - The arguments to log
- /// * `log_config` - The configuration for the logger
- ///
- pub fn debug(&self, message: &str, args: Option>, log_config: &LogConfig) {
- let msg = match args {
- Some(val) => format_string(message, &val),
- None => message.to_string(),
- };
- match log_config.name {
- Some(ref val) => tracing::debug!(
- message = msg,
- app_env = log_config.app_env,
- name = val.as_str()
- ),
- None => tracing::debug!(message = msg, app_env = log_config.app_env),
- };
- }
-
- /// Log a warning message
- ///
- /// # Arguments
- ///
- /// * `message` - The message to log
- /// * `args` - The arguments to log
- /// * `log_config` - The configuration for the logger
- ///
- pub fn warning(&self, message: &str, args: Option>, log_config: &LogConfig) {
- let msg = match args {
- Some(val) => format_string(message, &val),
- None => message.to_string(),
- };
- match log_config.name {
- Some(ref val) => tracing::warn!(
- message = msg,
- app_env = log_config.app_env,
- name = val.as_str()
- ),
- None => tracing::warn!(message = msg, app_env = log_config.app_env),
- };
- }
-
- /// Log an error message
- ///
- /// # Arguments
- ///
- /// * `message` - The message to log
- /// * `args` - The arguments to log
- /// * `log_config` - The configuration for the logger
- ///
- pub fn error(&self, message: &str, args: Option>, log_config: &LogConfig) {
- let msg = match args {
- Some(val) => format_string(message, &val),
- None => message.to_string(),
- };
- match log_config.name {
- Some(ref val) => tracing::error!(
- message = msg,
- app_env = log_config.app_env,
- name = val.as_str()
- ),
- None => tracing::error!(message = msg, app_env = log_config.app_env),
- };
- }
-
- /// Log an trace message
- ///
- /// # Arguments
- ///
- /// * `message` - The message to log
- /// * `args` - The arguments to log
- /// * `log_config` - The configuration for the logger
- ///
- pub fn trace(&self, message: &str, args: Option>, log_config: &LogConfig) {
- let msg = match args {
- Some(val) => format_string(message, &val),
- None => message.to_string(),
- };
- match log_config.name {
- Some(ref val) => tracing::trace!(
- message = msg,
- app_env = log_config.app_env,
- name = val.as_str()
- ),
- None => tracing::trace!(message = msg, app_env = log_config.app_env),
- };
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::{JsonConfig, LogConfig, LogFileConfig, RustLogger};
-
- fn generate_test_file_config(
- level: String,
- stdout: bool,
- stderr: bool,
- rotate: &str,
- filename: &str,
- ) -> LogConfig {
- LogConfig {
- stdout,
- stderr,
- level,
- app_env: "development".to_string(),
- name: None,
- time_format:
- "[year]-[month]-[day] [hour repr:24]:[minute]:[second]::[subsecond digits:4]"
- .to_string(),
- json_config: Some(JsonConfig::new(None)),
- file_config: Some(LogFileConfig::new(
- Some(filename.to_string()),
- Some(rotate.to_string()),
- )),
- lock_guard: true,
- thread_id: false,
- color: true,
- }
- }
-
- fn generate_test_json_config(level: String, stdout: bool, stderr: bool) -> LogConfig {
- LogConfig {
- stdout,
- stderr,
- level,
- app_env: "development".to_string(),
- name: None,
- time_format:
- "[year]-[month]-[day] [hour repr:24]:[minute]:[second]::[subsecond digits:4]"
- .to_string(),
- json_config: Some(JsonConfig::new(None)),
- file_config: None,
- lock_guard: true,
- thread_id: true,
- color: true,
- }
- }
-
- fn generate_test_incorrect_config() -> LogConfig {
- LogConfig {
- stdout: false,
- stderr: false,
- level: "INFO".to_string(),
- app_env: "development".to_string(),
- name: None,
- time_format:
- "[year]-[month]-[day] [hour repr:24]:[minute]:[second]::[subsecond digits:4]"
- .to_string(),
- json_config: Some(JsonConfig::new(None)),
- file_config: None,
- lock_guard: true,
- thread_id: true,
- color: true,
- }
- }
-
- #[test]
- fn test_stdout_logger() {
- let levels = ["INFO", "DEBUG", "WARN", "ERROR", "TRACE"];
-
- levels.iter().for_each(|level| {
- let args = Some(vec!["10".to_string(), "10".to_string()]);
- let config = generate_test_json_config(level.to_string(), true, false);
- let logger = RustLogger::new(&config);
- logger.info("test", args.clone(), &config);
- logger.debug("test", args.clone(), &config);
- logger.warning("test", args.clone(), &config);
- logger.error("test", args.clone(), &config);
- logger.trace("test", args.clone(), &config);
- });
- }
-
- #[test]
- fn test_stderr_logger() {
- let levels = ["INFO", "DEBUG", "WARN", "ERROR", "TRACE"];
-
- levels.iter().for_each(|level| {
- let config = generate_test_json_config(level.to_string(), false, true);
- let logger = RustLogger::new(&config);
- logger.info("test", None, &config);
- logger.debug("test", None, &config);
- logger.warning("test", None, &config);
- logger.error("test", None, &config);
- logger.trace("test", None, &config);
- });
- }
-
- #[test]
- fn test_file_logger_minute() {
- let config = generate_test_file_config(
- "INFO".to_string(),
- true,
- false,
- "minutely",
- "minute/log.log",
- );
- let logger = RustLogger::new(&config);
- logger.info("test", None, &config);
-
- std::fs::remove_dir_all("minute").unwrap();
- }
-
- #[test]
- fn test_file_logger_hourly() {
- let config =
- generate_test_file_config("INFO".to_string(), true, false, "hourly", "hourly/log.log");
- let logger = RustLogger::new(&config);
- logger.info("test", None, &config);
-
- std::fs::remove_dir_all("hourly").unwrap();
- }
-
- #[test]
- fn test_file_logger_daily() {
- let config =
- generate_test_file_config("INFO".to_string(), true, false, "daily", "daily/log.log");
- let logger = RustLogger::new(&config);
- logger.info("test", None, &config);
-
- std::fs::remove_dir_all("daily").unwrap();
- }
-
- #[test]
- fn test_file_logger_never() {
- let config =
- generate_test_file_config("INFO".to_string(), true, false, "never", "never/log.log");
- let logger = RustLogger::new(&config);
- logger.info("test", None, &config);
-
- std::fs::remove_dir_all("never").unwrap();
- }
-
- #[test]
- fn test_invalid_output() {
- let config = generate_test_incorrect_config();
- let logger = RustLogger::new(&config);
- logger.info("test", None, &config);
- }
-}
diff --git a/tests/_test_scratch.py b/tests/_test_scratch.py
deleted file mode 100644
index d45222f..0000000
--- a/tests/_test_scratch.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from rusty_logger import Logger
-
-logger = Logger.get_logger(__file__)
-logger.info("Loggy McLogface")
diff --git a/tests/conftest.py b/tests/conftest.py
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/file_logger_test.py b/tests/file_logger_test.py
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/mod_one.py b/tests/mod_one.py
deleted file mode 100644
index 0ec239b..0000000
--- a/tests/mod_one.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from rusty_logger import Logger, LogConfig, JsonConfig, LogFileConfig
-
-
-logger = Logger.get_logger(
- name=__file__,
- config=LogConfig(
- json_config=JsonConfig(),
- file_config=LogFileConfig(
- filename="log/test.log",
- ),
- lock_guard=True,
- ),
-)
-
-
-class TestOne:
- @staticmethod
- def test_logger():
- pointer = logger
- assert pointer == logger
- logger.info("This is a test log")
diff --git a/tests/mod_two.py b/tests/mod_two.py
deleted file mode 100644
index a0cdd72..0000000
--- a/tests/mod_two.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from rusty_logger import Logger, LogConfig, JsonConfig, LogFileConfig
-
-
-logger = Logger.get_logger(
- name=__file__,
- config=LogConfig(
- json_config=JsonConfig(),
- file_config=LogFileConfig(
- filename="log/test.log",
- ),
- lock_guard=True,
- ),
-)
-
-
-class TestTwo:
- @staticmethod
- def test_logger():
- logger.info("This is a test log")
diff --git a/tests/requirements.txt b/tests/requirements.txt
deleted file mode 100644
index 484db85..0000000
--- a/tests/requirements.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-pytest==7.4.2
-pytest-cov==3.0.0
\ No newline at end of file
diff --git a/tests/standard_logger.py b/tests/standard_logger.py
deleted file mode 100644
index e0707b0..0000000
--- a/tests/standard_logger.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from rusty_logger import Logger, __version__
-
-
-logger = Logger.get_logger(name=__file__)
-logger.info("Rusty Logger version: {}", __version__)
-logger.info("test info")
-logger.debug("test debug")
-logger.warning("test warning")
-logger.error("test error")
-logger.trace("test trace")
diff --git a/tests/test_logger.py b/tests/test_logger.py
deleted file mode 100644
index aaf8578..0000000
--- a/tests/test_logger.py
+++ /dev/null
@@ -1,177 +0,0 @@
-import glob
-from rusty_logger import LogConfig, JsonConfig, LogLevel, LogFileConfig, __version__, Logger
-import shutil
-
-"""All tests are performed with guard locking
-Guard locking is a feature that allows you to lock a logger to a specific context that is dropped on end of context.
-All tests are performed with loggers scoped to their function context.
-If lock_guard is set to False, a default global logger is used that runs the duration of the application
-and is immutable after instantiation. Thus, subsequent tests with different logger configurations will fail.
-However, guard_lock = false (default) is useful for applications that do not require multiple loggers.
-"""
-
-
-def test_version():
- assert __version__ is not None
-
-
-def test_log_config():
- config = LogConfig()
- assert config.stdout is True
- assert config.stderr is False
- assert config.level == "INFO"
- assert config.app_env == "development"
- assert config.json_config is None
-
-
-def test_log_level():
- assert LogLevel.DEBUG == "DEBUG"
- assert LogLevel.INFO == "INFO"
- assert LogLevel.WARN == "WARN"
- assert LogLevel.ERROR == "ERROR"
- assert LogLevel.TRACE == "TRACE"
-
-
-def test_info_logger_stdout():
- logger = Logger.get_logger(
- name=__file__,
- config=LogConfig(lock_guard=True),
- )
- logger.info("test info")
- logger.debug("test debug")
- logger.warning("test warning")
- logger.error("test error")
- logger.trace("test trace")
- logger.info("test info", color="red") # testing color parsing
-
-
-def test_debug_logger_file():
- file_config = LogFileConfig(filename="log/test.log")
- logger = Logger.get_logger(
- name=__file__,
- config=LogConfig(
- lock_guard=True,
- file_config=file_config,
- level="DEBUG",
- color=True,
- ),
- )
-
- logger.info("test info")
- logger.debug("test debug")
- logger.warning("test warning")
- logger.error("test error")
-
- assert glob.glob("log/test.log*")
-
- for name in glob.glob("log/test.log*"):
- with open(name, "r") as fp:
- for count, line in enumerate(fp):
- pass
- count = count + 1
- assert count == 4
- shutil.rmtree("log", ignore_errors=False)
-
-
-def test_warn_logger_file():
- file_config = LogFileConfig(filename="log/test.log")
- logger = Logger.get_logger(
- name=__file__,
- config=LogConfig(
- level="TRACE",
- json_config=JsonConfig(flatten=True),
- file_config=file_config,
- lock_guard=True,
- ),
- )
-
- logger.info("test info")
- logger.debug("test debug")
- logger.warning("test warning")
- logger.error("test error")
- logger.trace("test error")
- logger.info("test info", color="red") # this will be skipped
-
- assert glob.glob("log/test.log*")
-
- for name in glob.glob("log/test.log*"):
- with open(name, "r") as fp:
- for count, line in enumerate(fp):
- pass
- count = count + 1
- assert count == 6
- shutil.rmtree("log", ignore_errors=False)
-
-
-def test_error_logger_file():
- file_config = LogFileConfig(filename="log/test.log")
- logger = Logger.get_logger(
- name=__name__,
- config=LogConfig(
- level="ERROR",
- json_config=JsonConfig(flatten=True),
- file_config=file_config,
- lock_guard=True,
- thread_id=True,
- ),
- )
-
- logger.info("test info")
- logger.debug("test debug")
- logger.warning("test warning")
- logger.error("test error")
-
- assert glob.glob("log/test.log*")
-
- for name in glob.glob("log/test.log*"):
- with open(name, "r") as fp:
- for count, line in enumerate(fp):
- count
- pass
- count = count + 1
- assert count == 1
- shutil.rmtree("log", ignore_errors=False)
-
-
-def _test_modules():
- from tests.mod_one import TestOne
- from tests.mod_two import TestTwo
-
- TestOne.test_logger()
- TestTwo.test_logger()
-
- for name in glob.glob("log/test.log*"):
- with open(name, "r") as fp:
- for count, line in enumerate(fp):
- pass
- count = count + 1
- assert count == 2
- shutil.rmtree("log", ignore_errors=False)
-
-
-def test_invalid_config_format():
- logger = Logger.get_logger(
- name=__file__,
- config=LogConfig(
- stderr=False,
- stdout=False,
- time_format="[hour]:[minute]",
- lock_guard=True,
- ),
- )
-
- # Logger will default to stdout true if not set
- assert logger.config.stdout
- logger.info("blah")
-
-
-def test_info_logger_stdout_args():
- # turn off guard locking for last test
- logger = Logger.get_logger(
- name=__file__,
- config=LogConfig(
- lock_guard=True,
- ),
- )
-
- logger.info("test info {} {} {} ", "test", 10.43, {"test": "test"})