water-bottle-please 0.6.4__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,32 @@
1
+ name: "Publish"
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ # Publish on any tag starting with a `v`, e.g., v0.1.0
7
+ - v*
8
+
9
+ jobs:
10
+ run:
11
+ runs-on: ubuntu-latest
12
+ environment:
13
+ name: pypi
14
+ permissions:
15
+ id-token: write
16
+ contents: read
17
+ steps:
18
+ - name: Checkout
19
+ uses: actions/checkout@v6
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@v7
22
+ - name: Install Python 3.13
23
+ run: uv python install 3.13
24
+ - name: Build
25
+ run: uv build
26
+ # Check that basic features work and we didn't miss to include crucial files
27
+ - name: Smoke test (wheel)
28
+ run: uv run --isolated --no-project --with dist/*.whl tests/smoke_test.py
29
+ - name: Smoke test (source distribution)
30
+ run: uv run --isolated --no-project --with dist/*.tar.gz tests/smoke_test.py
31
+ - name: Publish
32
+ run: uv publish
@@ -0,0 +1,141 @@
1
+ name: Build, Test, Changelog, and Release Python Package
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ unit-tests:
10
+ name: Test Python Package
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v5
18
+ with:
19
+ version: "0.6.5" # pin a specific version is best practice
20
+ enable-cache: true
21
+
22
+ # install python in
23
+ - name: "Set up Python"
24
+ uses: actions/setup-python@v5
25
+ with:
26
+ python-version-file: "pyproject.toml"
27
+
28
+ - name: Run tests
29
+ # For example, using `pytest`
30
+ run: uv run pytest tests
31
+
32
+ build-and-release:
33
+ name: Generate Changelog, build, and release
34
+ runs-on: ubuntu-latest
35
+ needs: unit-tests # This makes sure the build job runs first
36
+
37
+ outputs:
38
+ released: ${{ steps.changelog.outputs.skipped == 'false' }}
39
+
40
+ steps:
41
+ # Checkout the code
42
+ - uses: actions/checkout@v4
43
+
44
+ # Generate changelog
45
+ - name: Conventional Changelog Action
46
+ id: changelog
47
+ uses: TriPSs/conventional-changelog-action@v3
48
+ with:
49
+ github-token: ${{ secrets.github_token }}
50
+ create-summary: true
51
+
52
+
53
+ - name: Set up uv
54
+ if: ${{ steps.changelog.outputs.skipped == 'false' }}
55
+ run: curl -LsSf https://astral.sh/uv/install.sh | sh
56
+
57
+ - name: Build package
58
+ if: ${{ steps.changelog.outputs.skipped == 'false' }}
59
+ run: |
60
+ # VERSION=$(uvx dunamai from any --no-metadata --style pep440)
61
+ VERSION=$(uvx dunamai from any --no-metadata --style pep440 | sed -E 's/\.post[0-9]+\.dev[0-9]+//')
62
+ echo $VERSION
63
+ uvx --from=toml-cli toml set --toml-path=pyproject.toml project.version $VERSION
64
+ uv build
65
+
66
+ - name: Create GitHub release and discussion
67
+ if: ${{ steps.changelog.outputs.skipped == 'false' }}
68
+ run: |
69
+ gh release create $TAG $FILES \
70
+ --title "$TAG" \
71
+ --notes "$CHANGELOG" \
72
+ --discussion-category "Announcements"
73
+ env:
74
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
75
+ TAG: ${{ steps.changelog.outputs.tag }}
76
+ CHANGELOG: ${{ steps.changelog.outputs.clean_changelog }}
77
+ FILES: dist/*.tar.gz dist/*.whl
78
+
79
+ - name: Upload dist artifacts
80
+ if: ${{ steps.changelog.outputs.skipped == 'false' }}
81
+ uses: actions/upload-artifact@v6
82
+ with:
83
+ name: dist
84
+ path: dist/
85
+
86
+ - name: Push Files for GitHub Action
87
+ if: ${{ steps.changelog.outputs.skipped == 'false' }}
88
+ run: |
89
+ git config user.name "Your GitHub Actions Bot"
90
+ git config user.email "your-email@example.com"
91
+ git add .
92
+ git commit -m "Build package and update version"
93
+ git push
94
+ env:
95
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
96
+
97
+ publish:
98
+ name: publish to pypi
99
+ needs: build-and-release
100
+ if: >
101
+ needs.build-and-release.result == 'success' &&
102
+ needs.build-and-release.outputs.released == 'true'
103
+
104
+ runs-on: ubuntu-latest
105
+
106
+ environment:
107
+ name: pypi-env
108
+
109
+ permissions:
110
+ id-token: write
111
+ contents: read
112
+
113
+ steps:
114
+ - uses: actions/checkout@v6
115
+
116
+ - name: Install uv
117
+ uses: astral-sh/setup-uv@v7
118
+
119
+ - name: Set up Python
120
+ run: uv python install 3.13
121
+
122
+ - name: Download dist artifacts
123
+ uses: actions/download-artifact@v7
124
+ with:
125
+ name: dist
126
+ path: dist
127
+
128
+ - name: Smoke test (wheel)
129
+ run: uv run --isolated --no-project --with dist/*.whl tests/smoke_test.py
130
+
131
+ - name: Smoke test (source distribution)
132
+ run: uv run --isolated --no-project --with dist/*.tar.gz tests/smoke_test.py
133
+
134
+ - name: Publish
135
+ run: uv publish
136
+
137
+
138
+
139
+
140
+
141
+
@@ -0,0 +1,171 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ #uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+
110
+ # pdm
111
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112
+ #pdm.lock
113
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114
+ # in version control.
115
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116
+ .pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121
+ __pypackages__/
122
+
123
+ # Celery stuff
124
+ celerybeat-schedule
125
+ celerybeat.pid
126
+
127
+ # SageMath parsed files
128
+ *.sage.py
129
+
130
+ # Environments
131
+ .env
132
+ .venv
133
+ env/
134
+ venv/
135
+ ENV/
136
+ env.bak/
137
+ venv.bak/
138
+
139
+ # Spyder project settings
140
+ .spyderproject
141
+ .spyproject
142
+
143
+ # Rope project settings
144
+ .ropeproject
145
+
146
+ # mkdocs documentation
147
+ /site
148
+
149
+ # mypy
150
+ .mypy_cache/
151
+ .dmypy.json
152
+ dmypy.json
153
+
154
+ # Pyre type checker
155
+ .pyre/
156
+
157
+ # pytype static type analyzer
158
+ .pytype/
159
+
160
+ # Cython debug symbols
161
+ cython_debug/
162
+
163
+ # PyCharm
164
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
167
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
168
+ #.idea/
169
+
170
+ # PyPI configuration file
171
+ .pypirc
@@ -0,0 +1 @@
1
+ 3.11
@@ -0,0 +1,50 @@
1
+ ## [0.6.4](https://github.com/coe-test-org/test_project/compare/v0.6.3...v0.6.4) (2026-02-05)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * idk ([05c4748](https://github.com/coe-test-org/test_project/commit/05c47481abfabfdceeab775d7faa1b1adf509017))
7
+
8
+
9
+
10
+ ## [0.6.3](https://github.com/coe-test-org/test_project/compare/v0.6.2...v0.6.3) (2026-02-05)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * Update release.yml ([3e371d6](https://github.com/coe-test-org/test_project/commit/3e371d6fb9ebb308f4775339657cc5c26a9eec4f))
16
+
17
+
18
+
19
+ ## [0.6.2](https://github.com/coe-test-org/test_project/compare/v0.6.1...v0.6.2) (2026-02-05)
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * Update release.yml ([1b8f4af](https://github.com/coe-test-org/test_project/commit/1b8f4afe1eaea6fa58b7bb55905b0eae1613680d))
25
+
26
+
27
+
28
+ ## [0.6.1](https://github.com/coe-test-org/test_project/compare/v0.6.0...v0.6.1) (2026-02-05)
29
+
30
+
31
+ ### Bug Fixes
32
+
33
+ * trigger Update release.yml ([c51a05b](https://github.com/coe-test-org/test_project/commit/c51a05be47f2d89b91046a9e93f6ca8f289cf237))
34
+
35
+
36
+
37
+ # [0.6.0](https://github.com/coe-test-org/test_project/compare/v0.5.1...v0.6.0) (2026-02-05)
38
+
39
+
40
+ ### Bug Fixes
41
+
42
+ * smoke_test ([54b090f](https://github.com/coe-test-org/test_project/commit/54b090fa7ed1fee927b6b0008863d20fd315e124))
43
+
44
+
45
+ ### Features
46
+
47
+ * Create smoke_test.py ([d17e0e6](https://github.com/coe-test-org/test_project/commit/d17e0e6bf53c134e671fc5b48f4f297a61a0d4d3))
48
+
49
+
50
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 coe-test-org
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,213 @@
1
+ Metadata-Version: 2.4
2
+ Name: water-bottle-please
3
+ Version: 0.6.4
4
+ Summary: Add your description here
5
+ Author: Frank Aragclear
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: polars>=1.24.0
9
+ Description-Content-Type: text/markdown
10
+
11
+ # test_project
12
+ testing out making a python package
13
+
14
+ # How to install the package
15
+
16
+ Open PowerShell terminal (or Ubuntu bash terminal)
17
+
18
+ 1. Make a new folder
19
+ 2. `pip install uv`
20
+ 3. `uv venv --python 3.11`
21
+ 4. `.\.venv\Scripts\activate`
22
+ 5. `uv pip install polars`
23
+ 6. `uv pip install git+https://github.com/coe-test-org/test_project.git#egg=test_project`
24
+
25
+ To install a specific version, find the git tag noted in the GitHub Release section (something like `v0.2.5`) and then put it in the install statement like `.git@v0.2.5`:
26
+
27
+ ```bash
28
+ uv pip install git+https://github.com/coe-test-org/test_project.git@v0.2.5#egg=test_project
29
+ ```
30
+
31
+ here's what it looks like when updating the install version:
32
+
33
+ ![image](https://github.com/user-attachments/assets/ac571054-4918-40a1-b601-000869b97866)
34
+
35
+
36
+
37
+ # How to test it
38
+ create a script called `main.py` in your folder and run this code:
39
+
40
+ ```python
41
+
42
+ from test_project import helpers
43
+ import polars as pl
44
+
45
+
46
+ df = pl.DataFrame({
47
+ 'date': [
48
+ '2022-01-03',
49
+ '01-02-2020',
50
+ '44115',
51
+ None,
52
+ "2022-12-27 08:26:49",
53
+ # "2022-12-27T08:26:49",
54
+ '01/02/1995',
55
+ '2/3/2022',
56
+ '2/16/2022'
57
+ ]
58
+ })
59
+
60
+ df_output = (
61
+ df
62
+ .with_columns(
63
+ output_date = helpers.date_format('date')
64
+ )
65
+ .select('output_date')
66
+ )
67
+
68
+ print(df_output)
69
+ ```
70
+
71
+ it should give you this output
72
+
73
+ ```python
74
+ >>> print(df_output)
75
+ shape: (8, 1)
76
+ ┌─────────────┐
77
+ │ output_date │
78
+ │ --- │
79
+ │ date │
80
+ ╞═════════════╡
81
+ │ 2022-01-03 │
82
+ │ 2020-01-02 │
83
+ │ null │
84
+ │ null │
85
+ │ 2022-12-27 │
86
+ │ 1995-01-02 │
87
+ │ 2022-02-03 │
88
+ │ 2022-02-16 │
89
+ └─────────────┘
90
+ ```
91
+
92
+
93
+ # For Devs
94
+
95
+ ## how to create a package with uv
96
+ 1. In a new repo, run `uv init --package <package_name>`
97
+ It gives you this:
98
+ ```bash
99
+ .
100
+ ├── LICENSE
101
+ ├── README.md
102
+ └── test_package
103
+ ├── README.md
104
+ ├── pyproject.toml
105
+ └── src
106
+ └── test_package
107
+ └── __init__.py
108
+
109
+ 3 directories, 5 files
110
+
111
+ ```
112
+ 2. Create a venv with `uv venv`
113
+ a. This creates a venv you can activate (called your package name)
114
+ b. Creates a `uv.lock` file
115
+ c. `Uv sync` to install and update your venv
116
+ 3. Run `uv build` and it will build your package, adding a `dist/` folder with a `.tar.gz` file of the package.
117
+ a. Amazing
118
+ b. [follow these docs](https://docs.astral.sh/uv/guides/package/#publishing-your-package) to publish it to pypi or somewhere else privately
119
+ 4. To install from github: `uv pip install git+https://github.com/coe-test-org/test_project.git#egg=test_project`
120
+ If you get python version error, add python version to your .venv with `uv python install 3.11` or rebuild the venv with `uv venv --python 3.11`
121
+
122
+ 5. for dev packages that you need but aren't package dependencies, like `pytest`, you can add them to the project with [development dependencies](https://docs.astral.sh/uv/concepts/projects/dependencies/#development-dependencies) like this:
123
+
124
+ ```bash
125
+ uv add --dev pytest
126
+ ```
127
+
128
+ and that will add pytest to the `uv.lock` and `pyproject.toml` but as a separate dependency from the actual packages. In other words, the package won't be dependent on `pytest`, but it will be installed for devs that want to run the unit tests.
129
+
130
+ ## how to build the package automatically with CI/CD
131
+
132
+ this repo has a github action that automatically runs unit tests, builds the package, and outputs a changelog for a github release.
133
+
134
+ The github action has two steps:
135
+
136
+ ### 1. unit-tests
137
+
138
+ This step will install `uv`, install python, and then run the unit tests in the `tests` folder.
139
+
140
+ ```yaml
141
+ unit-tests:
142
+ name: Build and Test Python Package
143
+ runs-on: ubuntu-latest
144
+
145
+ steps:
146
+ - uses: actions/checkout@v4
147
+
148
+ - name: Install uv
149
+ uses: astral-sh/setup-uv@v5
150
+ with:
151
+ version: "0.6.5" # pin a specific version is best practice
152
+ enable-cache: true
153
+
154
+ # install python in
155
+ - name: "Set up Python"
156
+ uses: actions/setup-python@v5
157
+ with:
158
+ python-version-file: "pyproject.toml"
159
+
160
+ - name: Run tests
161
+ # For example, using `pytest`
162
+ run: uv run pytest tests
163
+ ```
164
+
165
+ ### 2. build-and-release
166
+
167
+ This step is dependent on the first step above. If any unit-tests fail, then this build/release will not run.
168
+
169
+ The build step works like this:
170
+
171
+ 1. Scan the code for conventional commit messages (like `fix:`, `feat:`, etc) and render a changelog based on the commits
172
+ 2. Create a GitHub Release (git tags) that bump up the version of the codebase (like `v1.0.0` to `v1.1.0`)
173
+ 3. Build the python package and have its version match the git tag version. `uv` currently doesn't have a built in way to do this, but [they are currently working on it](https://github.com/astral-sh/uv/issues/6298). In the meantime, that link has a way to link the python package version to the git release like this:
174
+
175
+ ```yaml
176
+ # install uv
177
+ - name: Set up uv
178
+ run: curl -LsSf https://astral.sh/uv/install.sh | sh
179
+
180
+ - name: Build package
181
+ if: ${{ steps.changelog.outputs.skipped == 'false' }}
182
+ run: |
183
+ VERSION=$(uvx dunamai from any --no-metadata --style pep440) # find the version of the git tag for the python package
184
+ echo $VERSION
185
+ uvx --from=toml-cli toml set --toml-path=pyproject.toml project.version $VERSION # update the version in the pyproject.toml
186
+ uv build # build the package
187
+
188
+ ```
189
+
190
+ so it will update the `pyproject.toml` to have the most up to date version that matches the git tag version and then build the package with `uv build`. The [build function](https://docs.astral.sh/uv/concepts/projects/build/#using-uv-build) builds the package and outputs a `dist/` folder with a `.tar.gz` file and a `.whl` file, both of which are source files for installing the package.
191
+
192
+
193
+ I also wanted to take the `.tar.gz` and `.whl` files and add them to the `Assets` in the changelog for GitHub. That way we can always save copies of our package whenever we make a new release, just to be safe.
194
+
195
+ ```yaml
196
+ # Attach the build assets to the GitHub release (only if changelog creation was successful)
197
+ - name: Upload sdist to GitHub release
198
+ run: gh release upload $TAG $FILES --clobber
199
+ env:
200
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
201
+ TAG: ${{ steps.changelog.outputs.tag }} # this is pulled from the changelog step where it creates the git tag
202
+ FILES: dist/*.tar.gz dist/*.whl # these files are created in the build step
203
+ ```
204
+
205
+
206
+
207
+
208
+
209
+
210
+
211
+
212
+
213
+
@@ -0,0 +1,203 @@
1
+ # test_project
2
+ testing out making a python package
3
+
4
+ # How to install the package
5
+
6
+ Open PowerShell terminal (or Ubuntu bash terminal)
7
+
8
+ 1. Make a new folder
9
+ 2. `pip install uv`
10
+ 3. `uv venv --python 3.11`
11
+ 4. `.\.venv\Scripts\activate`
12
+ 5. `uv pip install polars`
13
+ 6. `uv pip install git+https://github.com/coe-test-org/test_project.git#egg=test_project`
14
+
15
+ To install a specific version, find the git tag noted in the GitHub Release section (something like `v0.2.5`) and then put it in the install statement like `.git@v0.2.5`:
16
+
17
+ ```bash
18
+ uv pip install git+https://github.com/coe-test-org/test_project.git@v0.2.5#egg=test_project
19
+ ```
20
+
21
+ here's what it looks like when updating the install version:
22
+
23
+ ![image](https://github.com/user-attachments/assets/ac571054-4918-40a1-b601-000869b97866)
24
+
25
+
26
+
27
+ # How to test it
28
+ create a script called `main.py` in your folder and run this code:
29
+
30
+ ```python
31
+
32
+ from test_project import helpers
33
+ import polars as pl
34
+
35
+
36
+ df = pl.DataFrame({
37
+ 'date': [
38
+ '2022-01-03',
39
+ '01-02-2020',
40
+ '44115',
41
+ None,
42
+ "2022-12-27 08:26:49",
43
+ # "2022-12-27T08:26:49",
44
+ '01/02/1995',
45
+ '2/3/2022',
46
+ '2/16/2022'
47
+ ]
48
+ })
49
+
50
+ df_output = (
51
+ df
52
+ .with_columns(
53
+ output_date = helpers.date_format('date')
54
+ )
55
+ .select('output_date')
56
+ )
57
+
58
+ print(df_output)
59
+ ```
60
+
61
+ it should give you this output
62
+
63
+ ```python
64
+ >>> print(df_output)
65
+ shape: (8, 1)
66
+ ┌─────────────┐
67
+ │ output_date │
68
+ │ --- │
69
+ │ date │
70
+ ╞═════════════╡
71
+ │ 2022-01-03 │
72
+ │ 2020-01-02 │
73
+ │ null │
74
+ │ null │
75
+ │ 2022-12-27 │
76
+ │ 1995-01-02 │
77
+ │ 2022-02-03 │
78
+ │ 2022-02-16 │
79
+ └─────────────┘
80
+ ```
81
+
82
+
83
+ # For Devs
84
+
85
+ ## how to create a package with uv
86
+ 1. In a new repo, run `uv init --package <package_name>`
87
+ It gives you this:
88
+ ```bash
89
+ .
90
+ ├── LICENSE
91
+ ├── README.md
92
+ └── test_package
93
+ ├── README.md
94
+ ├── pyproject.toml
95
+ └── src
96
+ └── test_package
97
+ └── __init__.py
98
+
99
+ 3 directories, 5 files
100
+
101
+ ```
102
+ 2. Create a venv with `uv venv`
103
+ a. This creates a venv you can activate (called your package name)
104
+ b. Creates a `uv.lock` file
105
+ c. `Uv sync` to install and update your venv
106
+ 3. Run `uv build` and it will build your package, adding a `dist/` folder with a `.tar.gz` file of the package.
107
+ a. Amazing
108
+ b. [follow these docs](https://docs.astral.sh/uv/guides/package/#publishing-your-package) to publish it to pypi or somewhere else privately
109
+ 4. To install from github: `uv pip install git+https://github.com/coe-test-org/test_project.git#egg=test_project`
110
+ If you get python version error, add python version to your .venv with `uv python install 3.11` or rebuild the venv with `uv venv --python 3.11`
111
+
112
+ 5. for dev packages that you need but aren't package dependencies, like `pytest`, you can add them to the project with [development dependencies](https://docs.astral.sh/uv/concepts/projects/dependencies/#development-dependencies) like this:
113
+
114
+ ```bash
115
+ uv add --dev pytest
116
+ ```
117
+
118
+ and that will add pytest to the `uv.lock` and `pyproject.toml` but as a separate dependency from the actual packages. In other words, the package won't be dependent on `pytest`, but it will be installed for devs that want to run the unit tests.
119
+
120
+ ## how to build the package automatically with CI/CD
121
+
122
+ this repo has a github action that automatically runs unit tests, builds the package, and outputs a changelog for a github release.
123
+
124
+ The github action has two steps:
125
+
126
+ ### 1. unit-tests
127
+
128
+ This step will install `uv`, install python, and then run the unit tests in the `tests` folder.
129
+
130
+ ```yaml
131
+ unit-tests:
132
+ name: Build and Test Python Package
133
+ runs-on: ubuntu-latest
134
+
135
+ steps:
136
+ - uses: actions/checkout@v4
137
+
138
+ - name: Install uv
139
+ uses: astral-sh/setup-uv@v5
140
+ with:
141
+ version: "0.6.5" # pin a specific version is best practice
142
+ enable-cache: true
143
+
144
+ # install python in
145
+ - name: "Set up Python"
146
+ uses: actions/setup-python@v5
147
+ with:
148
+ python-version-file: "pyproject.toml"
149
+
150
+ - name: Run tests
151
+ # For example, using `pytest`
152
+ run: uv run pytest tests
153
+ ```
154
+
155
+ ### 2. build-and-release
156
+
157
+ This step is dependent on the first step above. If any unit-tests fail, then this build/release will not run.
158
+
159
+ The build step works like this:
160
+
161
+ 1. Scan the code for conventional commit messages (like `fix:`, `feat:`, etc) and render a changelog based on the commits
162
+ 2. Create a GitHub Release (git tags) that bump up the version of the codebase (like `v1.0.0` to `v1.1.0`)
163
+ 3. Build the python package and have its version match the git tag version. `uv` currently doesn't have a built in way to do this, but [they are currently working on it](https://github.com/astral-sh/uv/issues/6298). In the meantime, that link has a way to link the python package version to the git release like this:
164
+
165
+ ```yaml
166
+ # install uv
167
+ - name: Set up uv
168
+ run: curl -LsSf https://astral.sh/uv/install.sh | sh
169
+
170
+ - name: Build package
171
+ if: ${{ steps.changelog.outputs.skipped == 'false' }}
172
+ run: |
173
+ VERSION=$(uvx dunamai from any --no-metadata --style pep440) # find the version of the git tag for the python package
174
+ echo $VERSION
175
+ uvx --from=toml-cli toml set --toml-path=pyproject.toml project.version $VERSION # update the version in the pyproject.toml
176
+ uv build # build the package
177
+
178
+ ```
179
+
180
+ so it will update the `pyproject.toml` to have the most up to date version that matches the git tag version and then build the package with `uv build`. The [build function](https://docs.astral.sh/uv/concepts/projects/build/#using-uv-build) builds the package and outputs a `dist/` folder with a `.tar.gz` file and a `.whl` file, both of which are source files for installing the package.
181
+
182
+
183
+ I also wanted to take the `.tar.gz` and `.whl` files and add them to the `Assets` in the changelog for GitHub. That way we can always save copies of our package whenever we make a new release, just to be safe.
184
+
185
+ ```yaml
186
+ # Attach the build assets to the GitHub release (only if changelog creation was successful)
187
+ - name: Upload sdist to GitHub release
188
+ run: gh release upload $TAG $FILES --clobber
189
+ env:
190
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
191
+ TAG: ${{ steps.changelog.outputs.tag }} # this is pulled from the changelog step where it creates the git tag
192
+ FILES: dist/*.tar.gz dist/*.whl # these files are created in the build step
193
+ ```
194
+
195
+
196
+
197
+
198
+
199
+
200
+
201
+
202
+
203
+
File without changes
@@ -0,0 +1,3 @@
1
+ {
2
+ "version": "0.6.4"
3
+ }
@@ -0,0 +1,24 @@
1
+ [project]
2
+ name = "water-bottle-please"
3
+ version = "0.6.4"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Frank Aragclear" }
8
+ ]
9
+ requires-python = ">=3.10"
10
+ dependencies = [
11
+ "polars>=1.24.0",
12
+ ]
13
+
14
+ [project.scripts]
15
+ water-bottle-please = "water_bottle_please:main"
16
+
17
+ [build-system]
18
+ requires = ["hatchling"]
19
+ build-backend = "hatchling.build"
20
+
21
+ [dependency-groups]
22
+ dev = [
23
+ "pytest>=8.3.5",
24
+ ]
@@ -0,0 +1,2 @@
1
+ def main() -> None:
2
+ print("Hello from test-project!")
@@ -0,0 +1,157 @@
1
+ import polars as pl
2
+ from datetime import datetime
3
+ from datetime import date
4
+
5
+ def date_format(col: str,output_date_name = "output_date_name"):
6
+ """ Format Dates
7
+
8
+ Convert string dates into a yyyy-mm-dd format.
9
+ The function uses pl.coalesce to try to process different formats.
10
+ For example, it will first try to convert m/d/y, and then if that doesn't work it will try d/m/y.
11
+ It's not perfect, but if someone messes up the date it's their fault.
12
+
13
+ **Note: it won't attempt to convert excel dates. If someone sends us excel dates we will file a lawsuit.**
14
+
15
+ Usage
16
+ -----
17
+ To be applied to a string date column.
18
+
19
+ Parameters
20
+ ----------
21
+ col: str
22
+ a string column that has a date
23
+
24
+ Returns
25
+ -------
26
+ output_date: date
27
+ a date column
28
+
29
+ Examples
30
+ --------
31
+ ```{python}
32
+ #| echo: false
33
+ {{< include "../_setup.qmd" >}}
34
+ ```
35
+ ```{python}
36
+ import polars as pl
37
+ from src.subtype_link.utils.helpers import date_format
38
+
39
+
40
+ df = pl.DataFrame({
41
+ "dates": [
42
+ "2024-10-30", # ISO format
43
+ "30/10/2024", # European format
44
+ "10/20/2024", # US format
45
+ "10-30-2024", # US format
46
+ "October 30, 2024", # Full month name format,
47
+ "45496", # an excel date LOL
48
+ "2022-12-27 08:26:49"
49
+ ]
50
+ })
51
+
52
+ print(
53
+ df
54
+ .with_columns(
55
+ new_date=date_format('dates','new_date')
56
+ )
57
+ )
58
+ # add something new
59
+
60
+ ```
61
+
62
+ """
63
+ return pl.coalesce(
64
+ # see this for date types https://docs.rs/chrono/latest/chrono/format/strftime/index.html
65
+ # regular dates like sane people yyyy-mm-dd
66
+ pl.col(col).str.strptime(pl.Date, "%F", strict=False),
67
+ # datetimes - semi sane
68
+ pl.col(col).str.strptime(pl.Date, "%F %T", strict=False),
69
+ # m/d/y - gettin wild
70
+ pl.col(col).str.strptime(pl.Date, "%D", strict=False),
71
+ # dont even ask
72
+ pl.col(col).str.strptime(pl.Date, "%c", strict=False),
73
+ # mm-dd-yyyy
74
+ pl.col(col).str.strptime(pl.Date, "%m-%d-%Y", strict=False),
75
+ # dd-mm-yyyy
76
+ pl.col(col).str.strptime(pl.Date, "%d-%m-%Y", strict=False),
77
+ # mm/dd/yyyy
78
+ pl.col(col).str.strptime(pl.Date, "%m/%d/%Y", strict=False),
79
+ # dd/mm/yyyy
80
+ pl.col(col).str.strptime(pl.Date, "%d/%m/%Y", strict=False),
81
+ # if someone literally writes out the month. smh
82
+ pl.col(col).str.strptime(pl.Date, "%B %d, %Y", strict=False),
83
+ # if someone sends an excel date we'll just reject it and call the cops on them
84
+
85
+ ).alias(output_date_name)
86
+
87
+
88
+ def save_raw_values(df_inp: pl.DataFrame, primary_key_col: str):
89
+ """ save raw values
90
+
91
+ Usage
92
+ -----
93
+ Converts a polars dataframe into a dataframe with all columns in a struct column.
94
+ It's good for saving raw outputs of data.
95
+
96
+ Parameters
97
+ ----------
98
+ df_inp: pl.DataFrame
99
+ a polars dataframe
100
+ primary_key_col: str
101
+ column name for the primary key (submission key, not person/case key)
102
+
103
+ Returns
104
+ -------
105
+ df: pl.DataFrame
106
+ a dataframe
107
+
108
+ Examples
109
+ --------
110
+ ```{python}
111
+ #| echo: false
112
+ {{< include "../_setup.qmd" >}}
113
+ ```
114
+ ```{python}
115
+ import polars as pl
116
+ from test_project import helpers
117
+
118
+ data = pl.DataFrame({
119
+ "lab_name": ["PHL", "MFT", "ELR","PHL"],
120
+ "first_name": ["Alice", "Bob", "Charlie", "Charlie"],
121
+ "last_name": ["Smith", "Johnson", "Williams", "Williams"],
122
+ "WA_ID": [1,2,4,4]
123
+ })
124
+
125
+ received_submissions_df = (
126
+ helpers.save_raw_values(df_inp=data,primary_key_col="WA_ID")
127
+ )
128
+
129
+ helpers.gt_style(df_inp=data)
130
+
131
+ ```
132
+
133
+ ```{python}
134
+ helpers.gt_style(df_inp=received_submissions_df)
135
+ ```
136
+
137
+ """
138
+
139
+ df = (
140
+ df_inp
141
+ .select([
142
+ # save the primary key
143
+ pl.col(primary_key_col).alias('submission_number'),
144
+
145
+ # internal create date
146
+ pl.lit(date.today()).alias("internal_create_date"),
147
+
148
+ # save a copy of all the original columns and put them into a struct column
149
+ pl.struct(pl.all()).alias("raw_inbound_submission")
150
+ ])
151
+ )
152
+
153
+ return df
154
+
155
+ def func():
156
+ return print("hi")
157
+
File without changes
@@ -0,0 +1,25 @@
1
+ """Check that basic features work.
2
+
3
+ Catch cases where e.g. files are missing so the import doesn't work. It is
4
+ recommended to check that e.g. assets are included."""
5
+
6
+ # tests/smoke_test.py
7
+ import sys
8
+
9
+ import water_bottle_please # replace with your actual package name
10
+ from water_bottle_please import helpers
11
+
12
+ def test_basic_import():
13
+ """Check that the package can be imported."""
14
+ assert water_bottle_please is not None
15
+
16
+ def test_basic_functionality():
17
+ """Optionally check one key function."""
18
+ result = helpers.date_format(col="test")
19
+ assert result is not None
20
+
21
+ if __name__ == "__main__":
22
+ test_basic_import()
23
+ test_basic_functionality()
24
+ print("Smoke test passed!")
25
+
@@ -0,0 +1,71 @@
1
+ import polars as pl
2
+ from polars.testing import assert_frame_equal as pl_assert_frame_equal
3
+ import pytest
4
+ from water_bottle_please import helpers
5
+
6
+
7
+ @pytest.fixture
8
+ def get_df():
9
+ """
10
+ Get the data
11
+ """
12
+ df = pl.DataFrame({'Name': [
13
+ 'Alice',
14
+ 'Bob',
15
+ 'Aritra',
16
+ 'idk',
17
+ 'long_date',
18
+ # 'long_dateT',
19
+ 'monthday',
20
+ 'slashes',
21
+ 'longslash'],
22
+ # 'Age': [25, 30, 35, 3, 39],
23
+ 'date': [
24
+ '2022-01-03',
25
+ '01-02-2020',
26
+ '44115',
27
+ None,
28
+ "2022-12-27 08:26:49",
29
+ # "2022-12-27T08:26:49",
30
+ '01/02/1995',
31
+ '2/3/2022',
32
+ '2/16/2022'
33
+ ]})
34
+ df_output = (
35
+ df
36
+ .with_columns(
37
+ output_date = helpers.date_format('date')
38
+ )
39
+ .select('output_date')
40
+ )
41
+
42
+ return df, df_output
43
+
44
+
45
+ # ---- test the function ---- #
46
+
47
+ # test with polars
48
+ def test_date_format_polars(get_df):
49
+ """
50
+ Test if the column names of the transformed dataframe
51
+ match the columns of the expected outputs
52
+ """
53
+ _, df_output = get_df
54
+
55
+ x = (
56
+ pl.DataFrame({'output_date': [
57
+ '2022-01-03',
58
+ '2020-01-02',
59
+ None,
60
+ None,
61
+ "2022-12-27",
62
+ # "2022-12-27",
63
+ '1995-01-02',
64
+ '2022-02-03',
65
+ '2022-02-16']})
66
+ .with_columns(
67
+ pl.col('output_date').cast(pl.Date)
68
+ )
69
+ )
70
+
71
+ pl_assert_frame_equal(df_output,x)
@@ -0,0 +1,23 @@
1
+ import pytest
2
+ import polars as pl
3
+ from datetime import date
4
+ from water_bottle_please import helpers # Update this with the actual import path
5
+
6
+ def test_save_raw_values():
7
+ # Sample input DataFrame
8
+ data = pl.DataFrame({
9
+ "lab_name": ["PHL", "MFT", "ELR", "PHL"],
10
+ "first_name": ["Alice", "Bob", "Charlie", "Charlie"],
11
+ "last_name": ["Smith", "Johnson", "Williams", "Williams"],
12
+ "WA_ID": [1, 2, 4, 4]
13
+ })
14
+
15
+ # Call the function
16
+ result_df = helpers.save_raw_values(df_inp=data, primary_key_col="WA_ID")
17
+
18
+ # Check that the "raw_inbound_submission" column is a struct
19
+ assert (pl.Struct in result_df.dtypes), "The 'raw_inbound_submission' column should be a struct"
20
+
21
+ # Check that the output DataFrame has the expected columns
22
+ expected_columns = ["submission_number", "internal_create_date", "raw_inbound_submission"]
23
+ assert result_df.columns == expected_columns, f"Expected columns {expected_columns}, but got {result_df.columns}"
@@ -0,0 +1,137 @@
1
+ version = 1
2
+ revision = 1
3
+ requires-python = ">=3.10"
4
+
5
+ [[package]]
6
+ name = "colorama"
7
+ version = "0.4.6"
8
+ source = { registry = "https://pypi.org/simple" }
9
+ sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
10
+ wheels = [
11
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
12
+ ]
13
+
14
+ [[package]]
15
+ name = "exceptiongroup"
16
+ version = "1.2.2"
17
+ source = { registry = "https://pypi.org/simple" }
18
+ sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 }
19
+ wheels = [
20
+ { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 },
21
+ ]
22
+
23
+ [[package]]
24
+ name = "iniconfig"
25
+ version = "2.0.0"
26
+ source = { registry = "https://pypi.org/simple" }
27
+ sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
28
+ wheels = [
29
+ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
30
+ ]
31
+
32
+ [[package]]
33
+ name = "packaging"
34
+ version = "24.2"
35
+ source = { registry = "https://pypi.org/simple" }
36
+ sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
37
+ wheels = [
38
+ { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
39
+ ]
40
+
41
+ [[package]]
42
+ name = "pluggy"
43
+ version = "1.5.0"
44
+ source = { registry = "https://pypi.org/simple" }
45
+ sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
46
+ wheels = [
47
+ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
48
+ ]
49
+
50
+ [[package]]
51
+ name = "polars"
52
+ version = "1.24.0"
53
+ source = { registry = "https://pypi.org/simple" }
54
+ sdist = { url = "https://files.pythonhosted.org/packages/bf/d3/453bcecbe14a5aba6be47c99d81f4e1f941d3de729d5e0ce5c7d527c05ed/polars-1.24.0.tar.gz", hash = "sha256:6e7553789495081c998f5e4ad4ebc7e19e970a9cc83326d40461564e85ad226d", size = 4446066 }
55
+ wheels = [
56
+ { url = "https://files.pythonhosted.org/packages/a9/3f/16f87d9ec4707d717a434bc54307506594522de99fdfe3d5d76233912c94/polars-1.24.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:563b99a6597fe77a3c89d478e4a6fb49c063f44ef84d4adefe490e14626e2f99", size = 33792674 },
57
+ { url = "https://files.pythonhosted.org/packages/35/cd/27353d0b9331d60a95f5708370441348d4a3af0f609961ceaaa3b583190f/polars-1.24.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:6ad64d938d60b7fda39b60892ef67bc6a9942e0c7170db593a65d019e8730b09", size = 30469541 },
58
+ { url = "https://files.pythonhosted.org/packages/a7/db/668d8328b1c3d8381023fc4ed905a88b93cca041c088f42a94dbd6822469/polars-1.24.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:331e737465b8d954bec51e6906bdc6e979a6ee52f97ffe5e8d0c10794a46bfd9", size = 34313540 },
59
+ { url = "https://files.pythonhosted.org/packages/12/16/f95207616b2e802c381459cf01f0d233daa98bdc4e394ec88044af9e927f/polars-1.24.0-cp39-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:3c6c774aebdd5cd601839594986648789352f72b8893f4b7e34224e75b060c8d", size = 31490266 },
60
+ { url = "https://files.pythonhosted.org/packages/8b/82/cb0512747ec5508a4f840a521feb27f28a81eac1aef6c92fc25643073579/polars-1.24.0-cp39-abi3-win_amd64.whl", hash = "sha256:a5a473ff44fe1b9e3e7a9013a9321efe841d858e89cf33d424e6f3fef3ea4d5e", size = 34678593 },
61
+ { url = "https://files.pythonhosted.org/packages/67/00/db3810803938467a215c1f161ff21ad6fef581d5ac1381ee2990d0180c19/polars-1.24.0-cp39-abi3-win_arm64.whl", hash = "sha256:5ea781ca8e0a39c3b677171dbd852e5fa2d5c53417b5fbd69d711b6044a49eaa", size = 30892251 },
62
+ ]
63
+
64
+ [[package]]
65
+ name = "pytest"
66
+ version = "8.3.5"
67
+ source = { registry = "https://pypi.org/simple" }
68
+ dependencies = [
69
+ { name = "colorama", marker = "sys_platform == 'win32'" },
70
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
71
+ { name = "iniconfig" },
72
+ { name = "packaging" },
73
+ { name = "pluggy" },
74
+ { name = "tomli", marker = "python_full_version < '3.11'" },
75
+ ]
76
+ sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 }
77
+ wheels = [
78
+ { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 },
79
+ ]
80
+
81
+ [[package]]
82
+ name = "tomli"
83
+ version = "2.2.1"
84
+ source = { registry = "https://pypi.org/simple" }
85
+ sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 }
86
+ wheels = [
87
+ { 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 },
88
+ { 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 },
89
+ { 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 },
90
+ { 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 },
91
+ { 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 },
92
+ { 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 },
93
+ { 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 },
94
+ { 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 },
95
+ { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 },
96
+ { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 },
97
+ { 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 },
98
+ { 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 },
99
+ { 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 },
100
+ { 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 },
101
+ { 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 },
102
+ { 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 },
103
+ { 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 },
104
+ { 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 },
105
+ { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 },
106
+ { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 },
107
+ { 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 },
108
+ { 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 },
109
+ { 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 },
110
+ { 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 },
111
+ { 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 },
112
+ { 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 },
113
+ { 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 },
114
+ { 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 },
115
+ { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 },
116
+ { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 },
117
+ { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 },
118
+ ]
119
+
120
+ [[package]]
121
+ name = "water-bottle"
122
+ version = "0.6.1"
123
+ source = { editable = "." }
124
+ dependencies = [
125
+ { name = "polars" },
126
+ ]
127
+
128
+ [package.dev-dependencies]
129
+ dev = [
130
+ { name = "pytest" },
131
+ ]
132
+
133
+ [package.metadata]
134
+ requires-dist = [{ name = "polars", specifier = ">=1.24.0" }]
135
+
136
+ [package.metadata.requires-dev]
137
+ dev = [{ name = "pytest", specifier = ">=8.3.5" }]