audit-labs/tutorials

Learn how to perform data analysis, scripting, automation, and more!

clone: git clone https://gitbay.org/audit-labs/tutorials.git

074964bb58c52719130815f70d75b3dbea0930e1

verified · cmc

author: Christian Cleberg <hello@cleberg.net> · 2026-08-23T01:48:08Z

Pin the notebook dependencies

The workflow executes every notebook on each push, which is the right check —
but it installed from a requirements.txt of >= floors, so what actually ran was
whatever those resolved to that day. A notebook that broke against a new pandas
would fail on an unrelated PR, and one that quietly changed behaviour would not
fail at all.

requirements.in is now the source and requirements.txt the compiled pin: 112
packages resolved against Python 3.10, the version the workflow uses.

environment.yml carries the same list loosely and can drift; it now says which
of the two CI believes.
 environment.yml  |   3 +
 requirements.in  |  10 ++
 requirements.txt | 362 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 365 insertions(+), 10 deletions(-)

diff --git a/environment.yml b/environment.yml
index 14382fa..f87c1e0 100644
--- a/environment.yml
+++ b/environment.yml
@@ -1,3 +1,6 @@
+# Conda convenience file. requirements.txt is what CI installs and is pinned;
+# this list is loose and can drift from it. Prefer requirements.txt when the two
+# disagree.
 name: audit-tutorials
 channels:
   - conda-forge
diff --git a/requirements.in b/requirements.in
new file mode 100644
index 0000000..6e4ac1e
--- /dev/null
+++ b/requirements.in
@@ -0,0 +1,10 @@
+pandas>=2.3.3
+numpy>=1.26
+matplotlib>=3.10
+seaborn>=0.13
+openpyxl
+jupyterlab
+ipykernel
+nbconvert
+nbclient
+papermill
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 6e4ac1e..d46f7d1 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,10 +1,352 @@
-pandas>=2.3.3
-numpy>=1.26
-matplotlib>=3.10
-seaborn>=0.13
-openpyxl
-jupyterlab
-ipykernel
-nbconvert
-nbclient
-papermill
\ No newline at end of file
+# This file was autogenerated by uv via the following command:
+#    uv pip compile requirements.in -o requirements.txt --python-version 3.10
+anyio==4.14.2
+    # via
+    #   httpx
+    #   jupyter-server
+appnope==1.0.0
+    # via ipykernel
+argon2-cffi==25.1.0
+    # via jupyter-server
+argon2-cffi-bindings==26.1.0
+    # via argon2-cffi
+arrow==1.4.0
+    # via isoduration
+asttokens==3.0.2
+    # via stack-data
+async-lru==2.3.0
+    # via jupyterlab
+attrs==26.1.0
+    # via
+    #   jsonschema
+    #   referencing
+babel==2.18.0
+    # via jupyterlab-server
+beautifulsoup4==4.15.0
+    # via nbconvert
+bleach==6.4.0
+    # via nbconvert
+certifi==2026.7.22
+    # via
+    #   httpcore
+    #   httpx
+    #   requests
+cffi==2.1.1
+    # via argon2-cffi-bindings
+charset-normalizer==3.5.1
+    # via requests
+click==8.4.2
+    # via papermill
+comm==0.2.3
+    # via ipykernel
+contourpy==1.3.2
+    # via matplotlib
+cycler==0.12.1
+    # via matplotlib
+debugpy==1.8.21
+    # via ipykernel
+decorator==5.3.1
+    # via ipython
+defusedxml==0.7.1
+    # via nbconvert
+entrypoints==0.4
+    # via papermill
+et-xmlfile==2.0.0
+    # via openpyxl
+exceptiongroup==1.3.1
+    # via
+    #   anyio
+    #   ipython
+executing==2.2.1
+    # via stack-data
+fastjsonschema==2.22.2
+    # via nbformat
+fonttools==4.63.0
+    # via matplotlib
+fqdn==1.5.1
+    # via jsonschema
+h11==0.16.0
+    # via httpcore
+httpcore==1.0.9
+    # via httpx
+httpx==0.28.1
+    # via jupyterlab
+idna==3.19
+    # via
+    #   anyio
+    #   httpx
+    #   jsonschema
+    #   requests
+ipykernel==7.3.0
+    # via
+    #   -r requirements.in
+    #   jupyterlab
+ipython==8.39.0
+    # via ipykernel
+isoduration==20.11.0
+    # via jsonschema
+jedi==0.20.0
+    # via ipython
+jinja2==3.1.6
+    # via
+    #   jupyter-server
+    #   jupyterlab
+    #   jupyterlab-server
+    #   nbconvert
+json5==0.15.0
+    # via jupyterlab-server
+jsonpointer==3.1.1
+    # via jsonschema
+jsonschema==4.26.0
+    # via
+    #   jupyter-events
+    #   jupyterlab-server
+    #   nbformat
+jsonschema-specifications==2025.9.1
+    # via jsonschema
+jupyter-builder==1.2.2
+    # via jupyterlab
+jupyter-client==8.9.1
+    # via
+    #   ipykernel
+    #   jupyter-server
+    #   nbclient
+jupyter-core==5.9.1
+    # via
+    #   ipykernel
+    #   jupyter-builder
+    #   jupyter-client
+    #   jupyter-server
+    #   jupyterlab
+    #   nbclient
+    #   nbconvert
+    #   nbformat
+jupyter-events==0.12.1
+    # via jupyter-server
+jupyter-lsp==2.3.1
+    # via jupyterlab
+jupyter-server==2.20.0
+    # via
+    #   jupyter-lsp
+    #   jupyterlab
+    #   jupyterlab-server
+    #   notebook-shim
+jupyter-server-terminals==0.5.4
+    # via jupyter-server
+jupyterlab==4.6.3
+    # via -r requirements.in
+jupyterlab-pygments==0.3.0
+    # via nbconvert
+jupyterlab-server==2.28.0
+    # via jupyterlab
+kiwisolver==1.5.0
+    # via matplotlib
+lark==1.3.1
+    # via rfc3987-syntax
+markupsafe==3.0.3
+    # via
+    #   jinja2
+    #   nbconvert
+matplotlib==3.10.9
+    # via
+    #   -r requirements.in
+    #   seaborn
+matplotlib-inline==0.2.2
+    # via
+    #   ipykernel
+    #   ipython
+mistune==3.3.4
+    # via nbconvert
+nbclient==0.11.0
+    # via
+    #   -r requirements.in
+    #   nbconvert
+    #   papermill
+nbconvert==7.17.1
+    # via
+    #   -r requirements.in
+    #   jupyter-server
+nbformat==5.11.1
+    # via
+    #   jupyter-server
+    #   nbclient
+    #   nbconvert
+    #   papermill
+nest-asyncio2==1.7.2
+    # via ipykernel
+notebook-shim==0.2.4
+    # via jupyterlab
+numpy==2.2.6
+    # via
+    #   -r requirements.in
+    #   contourpy
+    #   matplotlib
+    #   pandas
+    #   seaborn
+openpyxl==3.1.5
+    # via -r requirements.in
+overrides==7.7.0
+    # via jupyter-server
+packaging==26.3
+    # via
+    #   ipykernel
+    #   jupyter-events
+    #   jupyter-server
+    #   jupyterlab
+    #   jupyterlab-server
+    #   matplotlib
+    #   nbconvert
+pandas==2.3.3
+    # via
+    #   -r requirements.in
+    #   seaborn
+pandocfilters==1.5.1
+    # via nbconvert
+papermill==2.7.0
+    # via -r requirements.in
+parso==0.8.7
+    # via jedi
+pexpect==4.9.0
+    # via ipython
+pillow==12.3.0
+    # via matplotlib
+platformdirs==4.11.3
+    # via jupyter-core
+prometheus-client==0.26.0
+    # via jupyter-server
+prompt-toolkit==3.0.53
+    # via ipython
+psutil==7.2.2
+    # via ipykernel
+ptyprocess==0.7.0
+    # via
+    #   pexpect
+    #   terminado
+pure-eval==0.2.3
+    # via stack-data
+pycparser==3.0
+    # via cffi
+pygments==2.21.0
+    # via
+    #   ipython
+    #   nbconvert
+pyparsing==3.3.2
+    # via matplotlib
+python-dateutil==2.9.0.post0
+    # via
+    #   arrow
+    #   jupyter-client
+    #   matplotlib
+    #   pandas
+python-json-logger==4.2.0
+    # via jupyter-events
+pytz==2026.3.post1
+    # via pandas
+pyyaml==6.0.3
+    # via
+    #   jupyter-events
+    #   papermill
+pyzmq==27.2.0
+    # via
+    #   ipykernel
+    #   jupyter-client
+    #   jupyter-server
+referencing==0.37.0
+    # via
+    #   jsonschema
+    #   jsonschema-specifications
+    #   jupyter-events
+requests==2.34.2
+    # via
+    #   jupyterlab-server
+    #   papermill
+rfc3339-validator==0.1.4
+    # via
+    #   jsonschema
+    #   jupyter-events
+rfc3986-validator==0.1.1
+    # via
+    #   jsonschema
+    #   jupyter-events
+rfc3987-syntax==1.1.0
+    # via jsonschema
+rpds-py==0.30.0
+    # via
+    #   jsonschema
+    #   referencing
+seaborn==0.13.2
+    # via -r requirements.in
+send2trash==2.1.0
+    # via jupyter-server
+six==1.17.0
+    # via
+    #   python-dateutil
+    #   rfc3339-validator
+soupsieve==2.9.2
+    # via beautifulsoup4
+stack-data==0.6.3
+    # via ipython
+tenacity==9.1.4
+    # via papermill
+terminado==0.18.1
+    # via
+    #   jupyter-server
+    #   jupyter-server-terminals
+tinycss2==1.5.1
+    # via bleach
+tomli==2.4.1
+    # via
+    #   jupyter-builder
+    #   jupyterlab
+tornado==6.5.8
+    # via
+    #   ipykernel
+    #   jupyter-client
+    #   jupyter-server
+    #   jupyterlab
+    #   terminado
+tqdm==4.70.0
+    # via papermill
+traitlets==5.16.1
+    # via
+    #   ipykernel
+    #   ipython
+    #   jupyter-builder
+    #   jupyter-client
+    #   jupyter-core
+    #   jupyter-events
+    #   jupyter-server
+    #   jupyterlab
+    #   matplotlib-inline
+    #   nbclient
+    #   nbconvert
+    #   nbformat
+typing-extensions==4.16.0
+    # via
+    #   anyio
+    #   async-lru
+    #   beautifulsoup4
+    #   exceptiongroup
+    #   ipython
+    #   jupyter-client
+    #   jupyterlab
+    #   mistune
+    #   referencing
+tzdata==2026.3
+    # via
+    #   arrow
+    #   pandas
+uri-template==1.3.0
+    # via jsonschema
+urllib3==2.7.0
+    # via requests
+wcwidth==0.8.2
+    # via prompt-toolkit
+webcolors==25.10.0
+    # via jsonschema
+webencodings==0.6.1
+    # via
+    #   bleach
+    #   tinycss2
+websocket-client==1.9.0
+    # via jupyter-server