envsub 0.1.15__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,57 @@
1
+ # Build envsub tarballs for supported python.
2
+ #
3
+ # Adapted from uv and from pydantic-core CI
4
+ name: "Build tarball"
5
+
6
+ on:
7
+ workflow_call:
8
+ inputs:
9
+ release-version:
10
+ required: true
11
+ type: string
12
+ dry-run:
13
+ required: true
14
+ type: boolean
15
+ pull_request:
16
+ paths:
17
+ # When we change pyproject.toml, we want to ensure that the maturin builds still work.
18
+ - pyproject.toml
19
+ # And when we change this workflow itself...
20
+ - .github/workflows/build-sdist.yml
21
+
22
+ concurrency:
23
+ group: sdist-${{ github.workflow }}-${{ github.ref }}
24
+ cancel-in-progress: true
25
+
26
+ env:
27
+ PACKAGE_NAME: envsub
28
+ MODULE_NAME: envsub
29
+
30
+ jobs:
31
+ sdist:
32
+ runs-on: ubuntu-latest
33
+ steps:
34
+ - uses: actions/checkout@v4
35
+ - uses: actions/setup-python@v5
36
+ with:
37
+ python-version: ${{ env.PYTHON_VERSION }}
38
+ - uses: PyO3/maturin-action@v1
39
+
40
+ - name: Install uv
41
+ uses: astral-sh/setup-uv@v3
42
+
43
+ - name: Install the project
44
+ run: uv sync
45
+
46
+ - name: Build tarball
47
+ run: uv build
48
+
49
+ - name: "Test sdist"
50
+ run: |
51
+ pip install dist/${{ env.PACKAGE_NAME }}-*.tar.gz --force-reinstall
52
+ echo 'Hello, ${USER}'| python -m ${{ env.MODULE_NAME }}
53
+ - name: "Upload sdist"
54
+ uses: actions/upload-artifact@v4
55
+ with:
56
+ name: pypi_files_sdist
57
+ path: dist/*.tar.gz
@@ -0,0 +1,116 @@
1
+ # Build wheels envsub on all platforms.
2
+ #
3
+ # Adapted from uv and from pydantic-core CI
4
+ name: "Build wheels"
5
+
6
+ on:
7
+ workflow_call:
8
+ inputs:
9
+ release-version:
10
+ required: true
11
+ type: string
12
+ dry-run:
13
+ required: true
14
+ type: boolean
15
+ pull_request:
16
+ paths:
17
+ # When we change pyproject.toml, we want to ensure that the maturin builds still work.
18
+ - pyproject.toml
19
+ # And when we change this workflow itself...
20
+ - .github/workflows/build-wheels.yml
21
+
22
+ concurrency:
23
+ group: wheels-${{ github.workflow }}-${{ github.ref }}
24
+ cancel-in-progress: true
25
+
26
+
27
+ jobs:
28
+
29
+ build:
30
+ name: build on ${{ matrix.os }} (${{ matrix.target }} - ${{ matrix.interpreter || 'all' }}${{ matrix.os == 'linux' && format(' - {0}', matrix.manylinux == 'auto' && 'manylinux' || matrix.manylinux) || '' }})
31
+
32
+ strategy:
33
+ fail-fast: false
34
+ matrix:
35
+ os: [linux, macos, windows]
36
+ target: [x86_64, aarch64]
37
+ manylinux: [auto]
38
+ include:
39
+ - os: linux
40
+ manylinux: auto
41
+ target: i686
42
+ - os: linux
43
+ manylinux: auto
44
+ target: aarch64
45
+ - os: linux
46
+ manylinux: auto
47
+ target: armv7
48
+ interpreter: 3.9 3.10 3.11 3.12 3.13
49
+ - os: linux
50
+ manylinux: auto
51
+ target: ppc64le
52
+ interpreter: 3.9 3.10 3.11 3.12 3.13
53
+ - os: linux
54
+ manylinux: auto
55
+ target: s390x
56
+ interpreter: 3.9 3.10 3.11 3.12 3.13
57
+ - os: linux
58
+ manylinux: auto
59
+ target: x86_64
60
+ - os: linux
61
+ manylinux: musllinux_1_1
62
+ target: x86_64
63
+ - os: linux
64
+ manylinux: musllinux_1_1
65
+ target: aarch64
66
+ - os: linux
67
+ manylinux: musllinux_1_1
68
+ target: armv7
69
+
70
+ - os: macos
71
+ target: x86_64
72
+ - os: macos
73
+ target: aarch64
74
+ interpreter: 3.9 pypy3.9 pypy3.10
75
+
76
+ - os: windows
77
+ target: x86_64
78
+ interpreter: 3.11 3.12 3.13
79
+ - os: windows
80
+ target: i686
81
+ python-architecture: x86
82
+ interpreter: 3.11 3.12 3.13
83
+ exclude:
84
+ # Exclude failed build
85
+ - os: windows
86
+ target: aarch64
87
+
88
+ runs-on: ${{ (matrix.os == 'linux' && 'ubuntu') || matrix.os }}-latest
89
+ steps:
90
+ - uses: actions/checkout@v4
91
+
92
+ - name: set up python
93
+ uses: actions/setup-python@v5
94
+ with:
95
+ python-version: '3.13'
96
+ architecture: ${{ matrix.python-architecture || 'x64' }}
97
+ - name: install build deps
98
+ run: pip install twine
99
+
100
+ - name: build wheels
101
+ uses: PyO3/maturin-action@v1
102
+ with:
103
+ target: ${{ matrix.target }}
104
+ manylinux: ${{ matrix.manylinux }}
105
+ args: --release --out dist --interpreter ${{ matrix.interpreter || '3.9 3.10 3.11 3.12 3.13 pypy3.9 pypy3.10' }}
106
+ rust-toolchain: 'stable'
107
+ docker-options: -e CI
108
+
109
+ - run: ${{ (matrix.os == 'windows' && 'dir') || 'ls -lh' }} dist/
110
+
111
+ - run: twine check --strict dist/*
112
+
113
+ - uses: actions/upload-artifact@v4
114
+ with:
115
+ name: pypi_files_${{ matrix.os }}_${{ matrix.target }}_${{ matrix.interpreter || 'all' }}_${{ matrix.manylinux }}
116
+ path: dist/*.whl
@@ -0,0 +1,40 @@
1
+ # Publish a release to PyPI.
2
+ #
3
+ # Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a publish job
4
+ # within `cargo-dist`.
5
+ name: "Publish to PyPI"
6
+
7
+ on:
8
+ workflow_call:
9
+ inputs:
10
+ release-version:
11
+ required: true
12
+ type: string
13
+ dry-run:
14
+ required: true
15
+ type: boolean
16
+ secrets:
17
+ PYPI_TOKEN:
18
+ required: true
19
+
20
+ jobs:
21
+ pypi-publish:
22
+ name: Upload to PyPI
23
+ runs-on: ubuntu-latest
24
+ if: ${{ !inputs.dry-run }}
25
+
26
+ steps:
27
+ - uses: actions/download-artifact@v4
28
+ with:
29
+ pattern: pypi_files_*
30
+ path: dist
31
+ merge-multiple: true
32
+
33
+ - name: Install twine
34
+ run: pip install twine
35
+
36
+ - name: Publish to PyPI
37
+ env:
38
+ TWINE_USERNAME: __token__
39
+ TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
40
+ run: twine upload --verbose dist/*
@@ -0,0 +1,76 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*' # Automatically trigger on version tags
7
+ - 'dry-run'
8
+
9
+ workflow_dispatch:
10
+ inputs:
11
+ tag:
12
+ description: "Release Tag"
13
+ required: true
14
+ default: "dry-run"
15
+ type: string
16
+
17
+ jobs:
18
+ plan:
19
+ runs-on: ubuntu-latest
20
+ outputs:
21
+ release_version: "release-version.outputs.version"
22
+ dry_run: "release-version.outputs.dry_run"
23
+
24
+ steps:
25
+ - name: Checkout repository
26
+ uses: actions/checkout@v4
27
+
28
+ - name: Set Release Version
29
+ id: release-version
30
+ run: |
31
+ if [ "${{ github.event_name }}" == "push" ]; then
32
+ echo "version=${{ github.ref_name }}" >> $GITHUB_OUTPUT
33
+ if [ "${{ github.ref_name }}" == "dry-run" ]; then
34
+ echo "dry_run=true" >> $GITHUB_OUTPUT
35
+ else
36
+ echo "dry_run=false" >> $GITHUB_OUTPUT
37
+ fi
38
+ else
39
+ version="${{ github.event.inputs.tag || 'dry-run' }}"
40
+ echo "version=${version}" >> $GITHUB_OUTPUT
41
+ if [ "${version}" == "dry-run" ]; then
42
+ echo "dry_run=true" >> $GITHUB_OUTPUT
43
+ else
44
+ echo "dry_run=false" >> $GITHUB_OUTPUT
45
+ fi
46
+ fi
47
+ - name: Display Release Version
48
+ run: echo "The release version is ${{ steps.release-version.outputs.version }}"
49
+
50
+
51
+ build-wheels:
52
+ needs:
53
+ - plan
54
+ uses: ./.github/workflows/build-wheels.yml
55
+ with:
56
+ release-version: ${{ needs.plan.outputs.release_version }}
57
+ dry-run: ${{ needs.plan.outputs.dry_run == 'true' }}
58
+
59
+ build-sdist:
60
+ needs:
61
+ - plan
62
+ uses: ./.github/workflows/build-sdist.yml
63
+ with:
64
+ release-version: ${{ needs.plan.outputs.release_version }}
65
+ dry-run: ${{ needs.plan.outputs.dry_run == 'true' }}
66
+
67
+ publish-pypi:
68
+ needs:
69
+ - build-wheels
70
+ - build-sdist
71
+ uses: ./.github/workflows/publish-pypi.yml
72
+ with:
73
+ release-version: ${{ needs.plan.outputs.release_version }}
74
+ dry-run: ${{ needs.plan.outputs.dry_run == 'true' }}
75
+ secrets:
76
+ PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
@@ -0,0 +1,35 @@
1
+ name: tests
2
+
3
+ # Controls when the workflow will run
4
+ on:
5
+ # Triggers the workflow on push or pull request events but only for the main branch
6
+ push:
7
+ branches: [main]
8
+ pull_request:
9
+ branches: [main]
10
+
11
+ # Allows you to run this workflow manually from the Actions tab
12
+ workflow_dispatch:
13
+
14
+ jobs:
15
+ unittests:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: ${{ env.PYTHON_VERSION }}
22
+ - uses: PyO3/maturin-action@v1
23
+
24
+ - name: Install uv
25
+ uses: astral-sh/setup-uv@v3
26
+
27
+ - name: Install the project
28
+ run: uv sync
29
+
30
+ - name: Build tarball
31
+ run: uv build
32
+
33
+ - name: "Test sdist"
34
+ run: |
35
+ uv run pytest -sxv
@@ -0,0 +1,14 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Rust stuff
13
+ target
14
+ envsub/*.so
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,18 @@
1
+ ## 0.1.15 - 2024-10-24
2
+
3
+ * Fix CI pipeline while publishing using a tag
4
+
5
+ ## 0.1.14 - 2024-10-23
6
+
7
+ * Fix CI pipeline while publishing using a tag
8
+
9
+ ## 0.1.1 - 2024-10-23
10
+
11
+ * Add CI pipeline
12
+ * Improve implementation of the read method
13
+ * Implement readline
14
+ * Implement readlines
15
+
16
+ ## Release 0.1.0 - 2024-10-20
17
+
18
+ * Initiale release
@@ -0,0 +1,171 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 3
4
+
5
+ [[package]]
6
+ name = "autocfg"
7
+ version = "1.4.0"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
10
+
11
+ [[package]]
12
+ name = "cfg-if"
13
+ version = "1.0.0"
14
+ source = "registry+https://github.com/rust-lang/crates.io-index"
15
+ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
16
+
17
+ [[package]]
18
+ name = "envsub"
19
+ version = "0.1.15"
20
+ dependencies = [
21
+ "pyo3",
22
+ ]
23
+
24
+ [[package]]
25
+ name = "heck"
26
+ version = "0.5.0"
27
+ source = "registry+https://github.com/rust-lang/crates.io-index"
28
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
29
+
30
+ [[package]]
31
+ name = "indoc"
32
+ version = "2.0.5"
33
+ source = "registry+https://github.com/rust-lang/crates.io-index"
34
+ checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
35
+
36
+ [[package]]
37
+ name = "libc"
38
+ version = "0.2.161"
39
+ source = "registry+https://github.com/rust-lang/crates.io-index"
40
+ checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
41
+
42
+ [[package]]
43
+ name = "memoffset"
44
+ version = "0.9.1"
45
+ source = "registry+https://github.com/rust-lang/crates.io-index"
46
+ checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
47
+ dependencies = [
48
+ "autocfg",
49
+ ]
50
+
51
+ [[package]]
52
+ name = "once_cell"
53
+ version = "1.20.2"
54
+ source = "registry+https://github.com/rust-lang/crates.io-index"
55
+ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
56
+
57
+ [[package]]
58
+ name = "portable-atomic"
59
+ version = "1.9.0"
60
+ source = "registry+https://github.com/rust-lang/crates.io-index"
61
+ checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2"
62
+
63
+ [[package]]
64
+ name = "proc-macro2"
65
+ version = "1.0.88"
66
+ source = "registry+https://github.com/rust-lang/crates.io-index"
67
+ checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9"
68
+ dependencies = [
69
+ "unicode-ident",
70
+ ]
71
+
72
+ [[package]]
73
+ name = "pyo3"
74
+ version = "0.22.5"
75
+ source = "registry+https://github.com/rust-lang/crates.io-index"
76
+ checksum = "3d922163ba1f79c04bc49073ba7b32fd5a8d3b76a87c955921234b8e77333c51"
77
+ dependencies = [
78
+ "cfg-if",
79
+ "indoc",
80
+ "libc",
81
+ "memoffset",
82
+ "once_cell",
83
+ "portable-atomic",
84
+ "pyo3-build-config",
85
+ "pyo3-ffi",
86
+ "pyo3-macros",
87
+ "unindent",
88
+ ]
89
+
90
+ [[package]]
91
+ name = "pyo3-build-config"
92
+ version = "0.22.5"
93
+ source = "registry+https://github.com/rust-lang/crates.io-index"
94
+ checksum = "bc38c5feeb496c8321091edf3d63e9a6829eab4b863b4a6a65f26f3e9cc6b179"
95
+ dependencies = [
96
+ "once_cell",
97
+ "target-lexicon",
98
+ ]
99
+
100
+ [[package]]
101
+ name = "pyo3-ffi"
102
+ version = "0.22.5"
103
+ source = "registry+https://github.com/rust-lang/crates.io-index"
104
+ checksum = "94845622d88ae274d2729fcefc850e63d7a3ddff5e3ce11bd88486db9f1d357d"
105
+ dependencies = [
106
+ "libc",
107
+ "pyo3-build-config",
108
+ ]
109
+
110
+ [[package]]
111
+ name = "pyo3-macros"
112
+ version = "0.22.5"
113
+ source = "registry+https://github.com/rust-lang/crates.io-index"
114
+ checksum = "e655aad15e09b94ffdb3ce3d217acf652e26bbc37697ef012f5e5e348c716e5e"
115
+ dependencies = [
116
+ "proc-macro2",
117
+ "pyo3-macros-backend",
118
+ "quote",
119
+ "syn",
120
+ ]
121
+
122
+ [[package]]
123
+ name = "pyo3-macros-backend"
124
+ version = "0.22.5"
125
+ source = "registry+https://github.com/rust-lang/crates.io-index"
126
+ checksum = "ae1e3f09eecd94618f60a455a23def79f79eba4dc561a97324bf9ac8c6df30ce"
127
+ dependencies = [
128
+ "heck",
129
+ "proc-macro2",
130
+ "pyo3-build-config",
131
+ "quote",
132
+ "syn",
133
+ ]
134
+
135
+ [[package]]
136
+ name = "quote"
137
+ version = "1.0.37"
138
+ source = "registry+https://github.com/rust-lang/crates.io-index"
139
+ checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
140
+ dependencies = [
141
+ "proc-macro2",
142
+ ]
143
+
144
+ [[package]]
145
+ name = "syn"
146
+ version = "2.0.81"
147
+ source = "registry+https://github.com/rust-lang/crates.io-index"
148
+ checksum = "198514704ca887dd5a1e408c6c6cdcba43672f9b4062e1b24aa34e74e6d7faae"
149
+ dependencies = [
150
+ "proc-macro2",
151
+ "quote",
152
+ "unicode-ident",
153
+ ]
154
+
155
+ [[package]]
156
+ name = "target-lexicon"
157
+ version = "0.12.16"
158
+ source = "registry+https://github.com/rust-lang/crates.io-index"
159
+ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
160
+
161
+ [[package]]
162
+ name = "unicode-ident"
163
+ version = "1.0.13"
164
+ source = "registry+https://github.com/rust-lang/crates.io-index"
165
+ checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
166
+
167
+ [[package]]
168
+ name = "unindent"
169
+ version = "0.2.3"
170
+ source = "registry+https://github.com/rust-lang/crates.io-index"
171
+ checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce"
@@ -0,0 +1,21 @@
1
+ [package]
2
+ name = "envsub"
3
+ version = "0.1.15"
4
+ authors = ["Guillaume Gauvrit <guillaume@gauvr.it>"]
5
+ edition = "2021"
6
+ description = "A text preprocessing tool with environment variable substitution and default value support."
7
+ license = "BSD-3-Clause"
8
+ readme = "README.md"
9
+
10
+ [dependencies]
11
+ pyo3 = { version = "0.22.5", features = ["extension-module"] }
12
+
13
+ [lib]
14
+ name = "envsub"
15
+ crate-type = ["staticlib"]
16
+
17
+ [package.metadata.maturin]
18
+ bindings = "pyo3"
19
+
20
+ [tool.maturin]
21
+ features = ["pyo3/extension-module"]
envsub-0.1.15/Justfile ADDED
@@ -0,0 +1,32 @@
1
+ default_testsuite:='tests'
2
+
3
+ develop:
4
+ uv run maturin develop
5
+
6
+ test testsuite=default_testsuite: develop
7
+ uv run pytest -sxv {{testsuite}}
8
+
9
+ lf:
10
+ uv run pytest --lf -vvv
11
+
12
+
13
+ release major_minor_patch: && changelog
14
+ #!/bin/bash
15
+ cargo release patch --no-confirm --no-tag --no-push --no-publish --execute
16
+ git reset --soft HEAD^
17
+ export VERSION=$(head -n 10 Cargo.toml | grep version | sed 's/.*"\([^"]*\)".*/\1/')
18
+ sed -i "s/version = \"\(.*\)\"/version = \"${VERSION}\"/" pyproject.toml
19
+ uv sync
20
+
21
+ changelog:
22
+ uv run python scripts/write_changelog.py
23
+ cat CHANGELOG.md >> CHANGELOG.md.new
24
+ rm CHANGELOG.md
25
+ mv CHANGELOG.md.new CHANGELOG.md
26
+ $EDITOR CHANGELOG.md
27
+
28
+ publish:
29
+ git commit -am "Release $(uv run scripts/get_version.py)"
30
+ git push
31
+ git tag "v$(uv run scripts/get_version.py)"
32
+ git push origin "v$(uv run scripts/get_version.py)"
envsub-0.1.15/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2024, Guillaume Gauvrit
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ * Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
envsub-0.1.15/PKG-INFO ADDED
@@ -0,0 +1,32 @@
1
+ Metadata-Version: 2.3
2
+ Name: envsub
3
+ Version: 0.1.15
4
+ Classifier: License :: OSI Approved :: MIT License
5
+ Classifier: Programming Language :: Python :: 3
6
+ Classifier: Programming Language :: Rust
7
+ License-File: LICENSE
8
+ Summary: A text preprocessing tool with environment variable substitution.
9
+ Keywords: environment,substitution,variables
10
+ Author: Guillaume Gauvrit <guillaume@gauvr.it>
11
+ Author-email: Guillaume Gauvrit <guillaume@gauvr.it>
12
+ License: BSD-3-Clause
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
15
+
16
+ # envsub
17
+
18
+ **envsub** is a text preprocessing tool that performs environment variable
19
+ substitution in files, with support for default values. Written in Rust and
20
+ callable from Python, it provides a fast and reliable way to inject environment
21
+ variables into text files during runtime.
22
+
23
+ ## Features
24
+
25
+ - Environment variable substitution in any text file.
26
+ - Support for default values when environment variables are missing.
27
+ - Fast performance with Rust under the hood, accessible from Python.
28
+
29
+ ## Installation
30
+
31
+ This project is currently under development 🚧 and has not been released yet
32
+
@@ -0,0 +1,16 @@
1
+ # envsub
2
+
3
+ **envsub** is a text preprocessing tool that performs environment variable
4
+ substitution in files, with support for default values. Written in Rust and
5
+ callable from Python, it provides a fast and reliable way to inject environment
6
+ variables into text files during runtime.
7
+
8
+ ## Features
9
+
10
+ - Environment variable substitution in any text file.
11
+ - Support for default values when environment variables are missing.
12
+ - Fast performance with Rust under the hood, accessible from Python.
13
+
14
+ ## Installation
15
+
16
+ This project is currently under development 🚧 and has not been released yet
@@ -0,0 +1,2 @@
1
+ from .envsub import sub
2
+ __all__ = ["sub"]
@@ -0,0 +1,17 @@
1
+ import sys
2
+ from .envsub import sub
3
+
4
+
5
+ def main():
6
+ cnt = 0
7
+ while True:
8
+ cnt += 1
9
+ res = sub(sys.stdin).read()
10
+ if not res:
11
+ return
12
+ else:
13
+ print(res, end="")
14
+
15
+
16
+ if __name__ == "__main__":
17
+ main()
@@ -0,0 +1,12 @@
1
+ """Pyo3 binding interface definition."""
2
+
3
+ from typing import TextIO
4
+
5
+
6
+ def sub(downstream: TextIO) -> TextIO:
7
+ """
8
+ Replace all the envar from a downstream and return an upstream.
9
+
10
+ :downstream: A file-like object that provides text input.
11
+ :return: A file-like object with environment variables replaced.
12
+ """
File without changes
@@ -0,0 +1,34 @@
1
+ [project]
2
+ name = "envsub"
3
+ version = "0.1.15"
4
+ description = "A text preprocessing tool with environment variable substitution."
5
+ readme = "README.md"
6
+ license = "BSD-3-Clause"
7
+ authors = [
8
+ { name = "Guillaume Gauvrit", email = "guillaume@gauvr.it" }
9
+ ]
10
+ requires-python = ">=3.9"
11
+ keywords = ["environment", "substitution", "variables"]
12
+ classifiers = [
13
+ "License :: OSI Approved :: MIT License",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Rust",
16
+ ]
17
+ dependencies = []
18
+
19
+ [project.scripts]
20
+ envsub = "envsub:sub"
21
+
22
+ [tool.maturin]
23
+ bindings = "pyo3"
24
+
25
+ [tool.uv]
26
+ dev-dependencies = [
27
+ "pip>=24.2",
28
+ "pytest>=8.3.3",
29
+ "pyyaml>=6.0.2",
30
+ ]
31
+
32
+ [build-system]
33
+ requires = ["maturin>=1.0,<2.0"]
34
+ build-backend = "maturin"
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env python3
2
+ from importlib.metadata import version
3
+
4
+ print(version("envsub"))
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env python3
2
+ import datetime
3
+ from importlib.metadata import version
4
+
5
+ header = (
6
+ f"## {version('envsub')} - " f" {datetime.datetime.now().date().isoformat()}"
7
+ )
8
+ with open("CHANGELOG.md.new", "w") as changelog:
9
+ changelog.write(header)
10
+ changelog.write("\n")
11
+ changelog.write("\n")
12
+ changelog.write("* please write here \n\n")
@@ -0,0 +1,115 @@
1
+ use pyo3::prelude::*;
2
+ use pyo3::types::PyAny;
3
+
4
+ mod subst;
5
+
6
+ #[pyclass]
7
+ struct RustTextIOWrapper {
8
+ inner: Py<PyAny>,
9
+ buffer: String,
10
+ }
11
+
12
+ impl RustTextIOWrapper {
13
+ fn readstream<'p>(&mut self, py: Python<'p>) -> PyResult<Option<usize>> {
14
+ let result = self.inner.call0(py)?;
15
+ let py_str: &str = result.extract(py)?;
16
+ let resp = subst::substr(py_str);
17
+ self.buffer.push_str(resp.as_str());
18
+ let r = self.buffer.find('\n');
19
+ Ok(r)
20
+ }
21
+ }
22
+
23
+ #[pymethods]
24
+ impl RustTextIOWrapper {
25
+ #[new]
26
+ pub fn new(input: Py<PyAny>) -> Self {
27
+ Self {
28
+ inner: input,
29
+ buffer: String::new(),
30
+ }
31
+ }
32
+
33
+ #[pyo3(signature = (size=-1))]
34
+ pub fn read<'p>(mut slf: PyRefMut<'p, Self>, py: Python<'p>, size: i32) -> PyResult<String> {
35
+ loop {
36
+ if size > 0 {
37
+ let unsize = size as usize;
38
+ if slf.buffer.len() >= unsize {
39
+ break;
40
+ }
41
+ }
42
+ let result = slf.inner.call0(py)?;
43
+ let py_str: &str = result.extract(py)?;
44
+ if py_str.len() == 0 {
45
+ break;
46
+ }
47
+ let resp = subst::substr(py_str);
48
+ slf.buffer.push_str(resp.as_str());
49
+ }
50
+ if size > 0 {
51
+ let resp = std::mem::replace(&mut slf.buffer, String::new());
52
+ Ok(resp)
53
+ } else {
54
+ let unsize = size as usize;
55
+ let unsize = unsize.min(slf.buffer.len());
56
+ let resp = slf.buffer[..unsize].to_string();
57
+ slf.buffer = slf.buffer[unsize..].to_string();
58
+ Ok(resp)
59
+ }
60
+ }
61
+ #[pyo3(signature = ())]
62
+ pub fn readline<'p>(mut slf: PyRefMut<'p, Self>, py: Python<'p>) -> PyResult<String> {
63
+ let lastchar = slf.readstream(py)?;
64
+ match lastchar {
65
+ Some(pos) => {
66
+ let resp = slf.buffer[..pos + 1].to_string();
67
+ slf.buffer = slf.buffer[pos + 1..].to_string();
68
+ Ok(resp)
69
+ }
70
+ None => {
71
+ let resp = std::mem::replace(&mut slf.buffer, String::new());
72
+ Ok(resp)
73
+ }
74
+ }
75
+ }
76
+ #[pyo3(signature = ())]
77
+ pub fn readlines<'p>(mut slf: PyRefMut<'p, Self>, py: Python<'p>) -> PyResult<Vec<String>> {
78
+ let mut res = Vec::new();
79
+ loop {
80
+ let lastchar = slf.readstream(py)?;
81
+ match lastchar {
82
+ Some(pos) => {
83
+ let resp = slf.buffer[..pos + 1].to_string();
84
+ slf.buffer = slf.buffer[pos + 1..].to_string();
85
+ res.push(resp)
86
+ }
87
+ None => {
88
+ let resp = std::mem::replace(&mut slf.buffer, String::new());
89
+ res.push(resp);
90
+ break;
91
+ }
92
+ }
93
+ }
94
+ return Ok(res);
95
+ }
96
+
97
+ pub fn __enter__<'p>(slf: PyRef<'p, Self>, _py: Python<'p>) -> PyResult<PyRef<'p, Self>> {
98
+ Ok(slf)
99
+ }
100
+
101
+ pub fn __exit__(&mut self, _exc_type: PyObject, _exc_value: PyObject, _traceback: PyObject) {}
102
+ }
103
+
104
+ #[pyfunction]
105
+ fn sub<'py>(py: Python<'py>, input: Py<PyAny>) -> PyResult<RustTextIOWrapper> {
106
+ let read_line: Py<PyAny> = input.getattr(py, "readline")?.into();
107
+ let res = RustTextIOWrapper::new(read_line);
108
+ Ok(res)
109
+ }
110
+
111
+ #[pymodule]
112
+ fn envsub(m: &Bound<'_, PyModule>) -> PyResult<()> {
113
+ m.add_function(wrap_pyfunction!(sub, m)?)?;
114
+ Ok(())
115
+ }
@@ -0,0 +1,65 @@
1
+ use std::env;
2
+
3
+ #[derive(Debug)]
4
+ enum Token {
5
+ Str(String), // Represents plain strings
6
+ Var(String), // Represents variables in the form ${NAME}
7
+ VarDefault(String, String), // Represents variables in the form ${NAME}
8
+ }
9
+
10
+ impl Token {
11
+ fn to_string(&self) -> String {
12
+ match self {
13
+ Token::Str(s) => s.clone(), // Return the string as is
14
+ Token::Var(var) => {
15
+ // Remove the ${} from the variable name
16
+ env::var(var).unwrap_or_else(|_| format!("${{{}}}", var.clone()))
17
+ }
18
+ Token::VarDefault(var, default) => {
19
+ // Remove the ${} from the variable name
20
+ env::var(var).unwrap_or(default.clone())
21
+ }
22
+ }
23
+ }
24
+ }
25
+
26
+ fn tokenize(input: &str) -> Vec<Token> {
27
+ let mut tokens = Vec::new();
28
+ let mut last_pos = 0;
29
+
30
+ while let Some(start) = input[last_pos..].find("${") {
31
+ let start = last_pos + start;
32
+
33
+ if last_pos < start {
34
+ tokens.push(Token::Str(input[last_pos..start].to_string()));
35
+ }
36
+
37
+ if let Some(end) = input[start..].find('}') {
38
+ let end = start + end + 1;
39
+ let token = input[start + 2..end - 1].to_string();
40
+ if let Some(default_start) = token.find('-') {
41
+ tokens.push(Token::VarDefault(
42
+ token[0..default_start].to_string(),
43
+ token[default_start + 1..].to_string(),
44
+ ));
45
+ } else {
46
+ tokens.push(Token::Var(token.to_string()));
47
+ }
48
+ last_pos = end; // Move past the placeholder
49
+ } else {
50
+ break; // No closing brace found, exit the loop
51
+ }
52
+ }
53
+
54
+ if last_pos < input.len() {
55
+ tokens.push(Token::Str(input[last_pos..].to_string()));
56
+ }
57
+
58
+ tokens
59
+ }
60
+
61
+ pub fn substr(input: &str) -> String {
62
+ let tokens = tokenize(input);
63
+ let result: String = tokens.iter().map(|token| token.to_string()).collect();
64
+ return result;
65
+ }
File without changes
@@ -0,0 +1,12 @@
1
+ import io
2
+
3
+ from envsub.__main__ import main
4
+
5
+
6
+ def test_main(monkeypatch):
7
+ monkeypatch.setenv('NAME', 'World')
8
+ stdout = io.StringIO()
9
+ monkeypatch.setattr('sys.stdin', io.StringIO('Hello, ${NAME}!'))
10
+ monkeypatch.setattr('sys.stdout', stdout)
11
+ main()
12
+ assert stdout.getvalue() == 'Hello, World!'
@@ -0,0 +1,151 @@
1
+ import os
2
+
3
+ from io import StringIO
4
+ import pytest
5
+ import yaml
6
+
7
+ from envsub import sub
8
+
9
+
10
+ @pytest.fixture()
11
+ def envvars(params, monkeypatch):
12
+ if "envvars" in params:
13
+ for key, val in params["envvars"].items():
14
+ monkeypatch.setenv(key, val)
15
+ yield params.get("envvars")
16
+
17
+
18
+ @pytest.mark.parametrize(
19
+ "params",
20
+ [
21
+ pytest.param(
22
+ {
23
+ "envvars": {"name": "Bob", "alt": "Harriet"},
24
+ "stdin": [
25
+ "str1: Hello, ${name}!",
26
+ "str2: Hello, also ${alt}!",
27
+ ],
28
+ "expected": {
29
+ "str1": "Hello, Bob!",
30
+ "str2": "Hello, also Harriet!",
31
+ },
32
+ },
33
+ id="All vars",
34
+ ),
35
+ pytest.param(
36
+ {
37
+ "envvars": {"name": "Bob"},
38
+ "stdin": [
39
+ "str1: Hello, ${name}!",
40
+ "str2: Hello, also ${alt}!",
41
+ ],
42
+ "expected": {
43
+ "str1": "Hello, Bob!",
44
+ "str2": "Hello, also ${alt}!",
45
+ },
46
+ },
47
+ id="Missing vars stays unset",
48
+ ),
49
+ pytest.param(
50
+ {
51
+ "envvars": {"name": "Bob"},
52
+ "stdin": [
53
+ "str1: Hello, ${name}!",
54
+ "str2: Hello, also ${alt-Harriet}!",
55
+ ],
56
+ "expected": {
57
+ "str1": "Hello, Bob!",
58
+ "str2": "Hello, also Harriet!",
59
+ },
60
+ },
61
+ id="Missing vars with default",
62
+ ),
63
+ pytest.param(
64
+ {
65
+ "envvars": {
66
+ "b": "b" * 1000,
67
+ "e": "e" * 1000,
68
+ },
69
+ "stdin": [
70
+ f"str1: {'a' * 1000}",
71
+ "str2: ${b}",
72
+ f"str3: {'c' * 1000}",
73
+ f"str4: {'d' * 1000}",
74
+ "str5: ${e}",
75
+ ],
76
+ "expected": {
77
+ "str1": "a" * 1000,
78
+ "str2": "b" * 1000,
79
+ "str3": "c" * 1000,
80
+ "str4": "d" * 1000,
81
+ "str5": "e" * 1000,
82
+ },
83
+ },
84
+ id="More than 4096 chars (yaml buffer size)",
85
+ ),
86
+ ],
87
+ )
88
+ def test_sub(envvars, params):
89
+ downstream = StringIO("\n".join(params["stdin"]))
90
+ with sub(downstream) as upstream:
91
+ res = yaml.safe_load(upstream)
92
+
93
+ assert res == params["expected"]
94
+
95
+
96
+ @pytest.mark.parametrize(
97
+ "params",
98
+ [
99
+ pytest.param(
100
+ {
101
+ "envvars": {"name": "Bob", "alt": "Harriet"},
102
+ "stdin": [
103
+ "Hello, ${name}!",
104
+ "Hello, also ${alt}!",
105
+ ],
106
+ "expected": [
107
+ "Hello, Bob!\n",
108
+ "Hello, also Harriet!",
109
+ ],
110
+ },
111
+ id="line by line",
112
+ ),
113
+ ],
114
+ )
115
+ def test_readline(envvars, params):
116
+ downstream = StringIO("\n".join(params["stdin"]))
117
+ upstream = sub(downstream)
118
+ line = upstream.readline()
119
+ assert line == params["expected"][0]
120
+ line = upstream.readline()
121
+ assert line == params["expected"][1]
122
+ line = upstream.readline()
123
+ assert line == ''
124
+ line = upstream.readline()
125
+ assert line == ''
126
+
127
+
128
+ @pytest.mark.parametrize(
129
+ "params",
130
+ [
131
+ pytest.param(
132
+ {
133
+ "envvars": {"name": "Bob", "alt": "Harriet"},
134
+ "stdin": [
135
+ "Hello, ${name}!",
136
+ "Hello, also ${alt}!",
137
+ ],
138
+ "expected": [
139
+ "Hello, Bob!\n",
140
+ "Hello, also Harriet!",
141
+ ],
142
+ },
143
+ id="all lines",
144
+ ),
145
+ ],
146
+ )
147
+ def test_readlines(envvars, params):
148
+ downstream = StringIO("\n".join(params["stdin"]))
149
+ upstream = sub(downstream)
150
+ lines = upstream.readlines()
151
+ assert lines == params["expected"]
envsub-0.1.15/uv.lock ADDED
@@ -0,0 +1,156 @@
1
+ version = 1
2
+ requires-python = ">=3.9"
3
+
4
+ [[package]]
5
+ name = "colorama"
6
+ version = "0.4.6"
7
+ source = { registry = "https://pypi.org/simple" }
8
+ sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
9
+ wheels = [
10
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
11
+ ]
12
+
13
+ [[package]]
14
+ name = "envsub"
15
+ version = "0.1.15"
16
+ source = { editable = "." }
17
+
18
+ [package.dev-dependencies]
19
+ dev = [
20
+ { name = "pip" },
21
+ { name = "pytest" },
22
+ { name = "pyyaml" },
23
+ ]
24
+
25
+ [package.metadata]
26
+
27
+ [package.metadata.requires-dev]
28
+ dev = [
29
+ { name = "pip", specifier = ">=24.2" },
30
+ { name = "pytest", specifier = ">=8.3.3" },
31
+ { name = "pyyaml", specifier = ">=6.0.2" },
32
+ ]
33
+
34
+ [[package]]
35
+ name = "exceptiongroup"
36
+ version = "1.2.2"
37
+ source = { registry = "https://pypi.org/simple" }
38
+ sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 }
39
+ wheels = [
40
+ { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 },
41
+ ]
42
+
43
+ [[package]]
44
+ name = "iniconfig"
45
+ version = "2.0.0"
46
+ source = { registry = "https://pypi.org/simple" }
47
+ sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
48
+ wheels = [
49
+ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
50
+ ]
51
+
52
+ [[package]]
53
+ name = "packaging"
54
+ version = "24.1"
55
+ source = { registry = "https://pypi.org/simple" }
56
+ sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788 }
57
+ wheels = [
58
+ { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 },
59
+ ]
60
+
61
+ [[package]]
62
+ name = "pip"
63
+ version = "24.2"
64
+ source = { registry = "https://pypi.org/simple" }
65
+ sdist = { url = "https://files.pythonhosted.org/packages/4d/87/fb90046e096a03aeab235e139436b3fe804cdd447ed2093b0d70eba3f7f8/pip-24.2.tar.gz", hash = "sha256:5b5e490b5e9cb275c879595064adce9ebd31b854e3e803740b72f9ccf34a45b8", size = 1922041 }
66
+ wheels = [
67
+ { url = "https://files.pythonhosted.org/packages/d4/55/90db48d85f7689ec6f81c0db0622d704306c5284850383c090e6c7195a5c/pip-24.2-py3-none-any.whl", hash = "sha256:2cd581cf58ab7fcfca4ce8efa6dcacd0de5bf8d0a3eb9ec927e07405f4d9e2a2", size = 1815170 },
68
+ ]
69
+
70
+ [[package]]
71
+ name = "pluggy"
72
+ version = "1.5.0"
73
+ source = { registry = "https://pypi.org/simple" }
74
+ sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
75
+ wheels = [
76
+ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
77
+ ]
78
+
79
+ [[package]]
80
+ name = "pytest"
81
+ version = "8.3.3"
82
+ source = { registry = "https://pypi.org/simple" }
83
+ dependencies = [
84
+ { name = "colorama", marker = "sys_platform == 'win32'" },
85
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
86
+ { name = "iniconfig" },
87
+ { name = "packaging" },
88
+ { name = "pluggy" },
89
+ { name = "tomli", marker = "python_full_version < '3.11'" },
90
+ ]
91
+ sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 }
92
+ wheels = [
93
+ { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 },
94
+ ]
95
+
96
+ [[package]]
97
+ name = "pyyaml"
98
+ version = "6.0.2"
99
+ source = { registry = "https://pypi.org/simple" }
100
+ sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 }
101
+ wheels = [
102
+ { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 },
103
+ { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 },
104
+ { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 },
105
+ { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 },
106
+ { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 },
107
+ { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 },
108
+ { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 },
109
+ { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 },
110
+ { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 },
111
+ { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 },
112
+ { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 },
113
+ { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 },
114
+ { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 },
115
+ { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 },
116
+ { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 },
117
+ { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 },
118
+ { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 },
119
+ { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 },
120
+ { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 },
121
+ { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 },
122
+ { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 },
123
+ { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 },
124
+ { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 },
125
+ { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 },
126
+ { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 },
127
+ { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 },
128
+ { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 },
129
+ { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 },
130
+ { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 },
131
+ { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 },
132
+ { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 },
133
+ { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 },
134
+ { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 },
135
+ { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 },
136
+ { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 },
137
+ { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 },
138
+ { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 },
139
+ { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 },
140
+ { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 },
141
+ { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 },
142
+ { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 },
143
+ { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 },
144
+ { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 },
145
+ { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 },
146
+ { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 },
147
+ ]
148
+
149
+ [[package]]
150
+ name = "tomli"
151
+ version = "2.0.2"
152
+ source = { registry = "https://pypi.org/simple" }
153
+ sdist = { url = "https://files.pythonhosted.org/packages/35/b9/de2a5c0144d7d75a57ff355c0c24054f965b2dc3036456ae03a51ea6264b/tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed", size = 16096 }
154
+ wheels = [
155
+ { url = "https://files.pythonhosted.org/packages/cf/db/ce8eda256fa131af12e0a76d481711abe4681b6923c27efb9a255c9e4594/tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38", size = 13237 },
156
+ ]