diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index daa2cfa..17e7ed6 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -28,12 +28,9 @@ jobs: with: python-version: ${{ matrix.python-version }} enable-cache: true - - - name: Install the project - run: uv sync --locked --all-extras --dev - name: Run linter - run: uv run ruff check + run: uvx nox -s lint actionlint: name: actionlint runs-on: ubuntu-latest diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 1eb7593..300bb02 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -28,9 +28,6 @@ jobs: with: python-version: ${{ matrix.python-version }} enable-cache: true - - - name: Install the project - run: uv sync --locked --all-extras --dev - name: Run type checker - run: uv run mypy leanframe tests \ No newline at end of file + run: uvx nox -s mypy \ No newline at end of file diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index 5c08c4e..3c3d275 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -16,6 +16,10 @@ jobs: - "3.11" - "3.12" - "3.13" + - "3.14" + ibis-version: + - "11" + - "12" steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 @@ -27,9 +31,8 @@ jobs: with: python-version: ${{ matrix.python-version }} enable-cache: true - - - name: Install the project - run: uv sync --locked --all-extras --dev - name: Run tests - run: uv run pytest tests/unit + env: + NOX_SESSION: "unit-${{ matrix.python-version }}(ibis='${{ matrix.ibis-version }}')" + run: uvx nox -s "$NOX_SESSION" diff --git a/.gitignore b/.gitignore index ec50297..d805cb8 100644 --- a/.gitignore +++ b/.gitignore @@ -178,3 +178,14 @@ scratch/ vault/ .python-version *scratch* + +# Devenv +.devenv* +devenv.local.nix +devenv.local.yaml + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 0000000..6433214 --- /dev/null +++ b/devenv.lock @@ -0,0 +1,117 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1792414651, + "narHash": "sha256-2KKD5s7ZHwhFToFLAxRtezSIYevuo8Tpvl41E7LgfPU=", + "owner": "cachix", + "repo": "devenv", + "rev": "ff0d7e76dc83bef8eb4f83f7985ff2d792234440", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "nixpkgs": { + "inputs": { + "nixpkgs-src": "nixpkgs-src" + }, + "locked": { + "lastModified": 1791812882, + "narHash": "sha256-4AdYH2HoD21mys5lk9NoDcM2Z7iPeEUjxJeGE3Pw278=", + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "760b85220489f69855e6df78a6d1dad5873f8bf6", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "nixpkgs-python": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1792505437, + "narHash": "sha256-e/5WngU/ROQs3GlAl0KgvuXRcNmnvkuWM6cM8lTFt1s=", + "owner": "cachix", + "repo": "nixpkgs-python", + "rev": "bea6c222d16b6b8b8d0f658b69e447266acd5461", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "nixpkgs-python", + "type": "github" + } + }, + "nixpkgs-src": { + "flake": false, + "locked": { + "lastModified": 1791487292, + "narHash": "sha256-tqM7xDEsaeK1p6VvRygeZ9LkQRpUUkbRT02BHv6hnTM=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "8edc0c72e3a38faf5434e40d1f19431125fe4b30", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1782847189, + "narHash": "sha256-twXPFqFsrrY5r28Zh7Homgcp2gUMBgQ6WDS98Q/3xFI=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b6018f87da91d19d0ab4cf979885689b469cdd41", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "nixpkgs": "nixpkgs", + "nixpkgs-python": "nixpkgs-python" + } + } + }, + "root": "root", + "version": 7 +} \ No newline at end of file diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 0000000..7e43a79 --- /dev/null +++ b/devenv.nix @@ -0,0 +1,51 @@ +{ + pkgs, + lib, + config, + inputs, + ... +}: + +let + nixLibraryPath = lib.makeLibraryPath [ + pkgs.stdenv.cc.cc.lib + pkgs.zlib + ]; + pythonEnvClean = '' + export LD_LIBRARY_PATH="${nixLibraryPath}" + unset PYTHONPATH _PYTHON_HOST_PLATFORM _PYTHON_SYSCONFIGDATA_NAME + ''; +in +{ + # https://devenv.sh/basics/ + env = { + GREET = "devenv"; + UV_PYTHON_PREFERENCE = "only-system"; + UV_PYTHON_DOWNLOADS = "never"; + }; + + # https://devenv.sh/packages/ + packages = [ + pkgs.git + inputs.nixpkgs-python.packages.${pkgs.stdenv.system}."3.11" + inputs.nixpkgs-python.packages.${pkgs.stdenv.system}."3.12" + inputs.nixpkgs-python.packages.${pkgs.stdenv.system}."3.13" + inputs.nixpkgs-python.packages.${pkgs.stdenv.system}."3.14" + + pkgs.nox + pkgs.uv + ]; + + # https://devenv.sh/basics/ + enterShell = '' + ${pythonEnvClean} + ''; + + # https://devenv.sh/tests/ + enterTest = '' + ${pythonEnvClean} + nox + ''; + + # See full reference at https://devenv.sh/reference/options/ +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 0000000..0376ccd --- /dev/null +++ b/devenv.yaml @@ -0,0 +1,20 @@ +# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json +inputs: + nixpkgs: + url: github:cachix/devenv-nixpkgs/rolling + nixpkgs-python: + url: github:cachix/nixpkgs-python + +# If you're using non-OSS software, you can set allow_unfree to true. +# allow_unfree: true + +# If you're not willing to allow unsupported packages: +# allow_unsupported_system: false + +# If you're willing to use a package that's vulnerable +# permitted_insecure_packages: +# - "openssl-1.1.1w" + +# If you have more than one devenv you can merge them +#imports: +# - ./backend diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000..c167634 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,82 @@ +# Copyright 2026 LeanFrame Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import os + +import nox + +DEFAULT_PYTHON_VERSION = "3.13" +UNIT_TEST_PYTHON_VERSIONS = ["3.11", "3.12", "3.13", "3.14"] +IBIS_VERSIONS = ["11", "12"] + +nox.options.default_venv_backend = "uv" +nox.options.sessions = ["lint", "mypy", "unit"] + +# Ensure host pyenv shared libraries or Nix Python hook variables do not +# interfere with the Nix-provided Python interpreters used by uv and nox. +for _var in ("PYTHONPATH", "_PYTHON_HOST_PLATFORM", "_PYTHON_SYSCONFIGDATA_NAME"): + os.environ.pop(_var, None) +if "LD_LIBRARY_PATH" in os.environ: + os.environ["LD_LIBRARY_PATH"] = os.pathsep.join( + p + for p in os.environ["LD_LIBRARY_PATH"].split(os.pathsep) + if p and ".pyenv" not in p + ) + + +def _sync_env(session: nox.Session) -> None: + """Sync locked dependencies into the session's virtual environment.""" + python_arg = ( + f"--python={session.python}" + if isinstance(session.python, str) + else f"--python={session.virtualenv.location}" + ) + session.run_install( + "uv", + "sync", + "--locked", + "--all-extras", + "--dev", + python_arg, + env={"UV_PROJECT_ENVIRONMENT": session.virtualenv.location}, + ) + + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def lint(session: nox.Session) -> None: + """Run ruff linter.""" + _sync_env(session) + session.run("ruff", "check", *session.posargs) + + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def mypy(session: nox.Session) -> None: + """Run mypy static type checker.""" + _sync_env(session) + target_paths = session.posargs if session.posargs else ["leanframe", "tests"] + session.run("mypy", *target_paths) + + +@nox.session(python=UNIT_TEST_PYTHON_VERSIONS) +@nox.parametrize("ibis", IBIS_VERSIONS) +def unit(session: nox.Session, ibis: str) -> None: + """Run unit tests against multiple Python and Ibis versions.""" + _sync_env(session) + session.install(f"ibis-framework[bigquery,duckdb]=={ibis}.*") + test_paths = ( + session.posargs if session.posargs else [os.path.join("tests", "unit")] + ) + session.run("pytest", *test_paths) diff --git a/pyproject.toml b/pyproject.toml index 372a320..38dff09 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ requires-python = ">=3.11" dependencies = [ "pandas >=2.2.0", "pyarrow >=15.0.2", - "ibis-framework>=11.0.0,<12.0.0", + "ibis-framework>=11.0.0,<13.0.0", "pyopenssl>=26.1.0", ] @@ -16,14 +16,15 @@ dev = [ "ibis-framework[bigquery,duckdb]>=10.6.0", "ipython>=8.37.0", "mypy>=1.17.1", + "nox>=2025.2.9", "pandas-stubs>=2.3.0.250703", "pytest>=8.4.1", - "ruff>=0.12.4", + "ruff>=0.12.4,<1.0.0", "twine>=6.2.0", ] [build-system] -requires = ["uv_build>=0.11.2,<0.12.0"] +requires = ["uv_build>=0.11.2,<0.13.0"] build-backend = "uv_build" [tool.uv.build-backend] diff --git a/uv.lock b/uv.lock index c0a85a6..050ea91 100644 --- a/uv.lock +++ b/uv.lock @@ -1,13 +1,23 @@ version = 1 -revision = 3 +revision = 5 requires-python = ">=3.11" resolution-markers = [ - "python_full_version >= '3.14'", + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", "python_full_version < '3.12'", ] +[[package]] +name = "argcomplete" +version = "3.7.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/6f/5a73f04007ca950701765949209f068da628bd11f9c2da287278ce91e0ee/argcomplete-3.7.2.tar.gz", hash = "sha256:aad8b69a0b9969edb62db0d1752354c0d50717b10e0cbb00e2a958381b9fc6b9", size = 74473, upload-time = "2026-08-06T04:53:21.662Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/bd/551ee6af426af84ca33e02622be722925c196608e9127d731ef17c47f06e/argcomplete-3.7.2-py3-none-any.whl", hash = "sha256:6029205678bdd9c1c728a155f5f9ecf5812393f969eef58807641a2bc2aa5b19", size = 43294, upload-time = "2026-08-06T04:53:20.246Z" }, +] + [[package]] name = "asttokens" version = "3.0.1" @@ -26,6 +36,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/72/da/8916af0a074d24354d685fe4178a52d3fafd07b62e6f81124fdeac15594d/atpublic-6.0.2-py3-none-any.whl", hash = "sha256:156cfd3854e580ebfa596094a018fe15e4f3fa5bade74b39c3dabb54f12d6565", size = 6423, upload-time = "2025-09-24T18:30:15.214Z" }, ] +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + [[package]] name = "backports-tarfile" version = "1.2.0" @@ -212,6 +231,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "colorlog" +version = "6.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/55/ba79756cb90c8d69d599d57785398ac87bba7b19c80e87f4e8a562197c93/colorlog-6.12.0.tar.gz", hash = "sha256:2a7924c1dadf18b22a0eb8b06d1c7b01d5341707ec1641eb6fcc4fde0c3e8e5f", size = 18151, upload-time = "2026-07-23T13:40:40.71Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/19/0b6647bf5e331521e55d2b63bfbdc210bd9cd605189273f03614a05f702d/colorlog-6.12.0-py3-none-any.whl", hash = "sha256:30d392604e9110045a2c2aeefc27d7a017abbab63f3a8aee594eac0801df784e", size = 12239, upload-time = "2026-07-23T13:40:39.562Z" }, +] + [[package]] name = "cryptography" version = "47.0.0" @@ -295,6 +326,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, ] +[[package]] +name = "dependency-groups" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/16/65e61d8e837e0d70a99a0d4dd76e2af53357c3a03ed1db8ad9b5786d7f25/dependency_groups-1.3.2.tar.gz", hash = "sha256:c81831f43828dbc3987ee247eb198241a0152b8e7ceab10977cc3808eb388ac7", size = 13309, upload-time = "2026-08-22T02:43:30.137Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/e4/da5f2f176050efcfb9222a76a3e81c41f69f775be8e563b76ca4292a134f/dependency_groups-1.3.2-py3-none-any.whl", hash = "sha256:8c337f7b92445823b0d0ea507f611390c58dcd2112be00aebcd738a2ca7a1c64", size = 9802, upload-time = "2026-08-22T02:43:28.853Z" }, +] + +[[package]] +name = "distlib" +version = "0.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/bc/d36966de7304047a1001a7559fbf266c464bb5d8cc85464536ef8c9bcc32/distlib-0.4.4.tar.gz", hash = "sha256:ca4175b4ac7f71223cc30ed7248c45d2b38c2439d8ee6e498ab67ed63cf3508d", size = 621304, upload-time = "2026-10-16T13:56:34.203Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/2b/238f02a4927fa2e27aa995371d240a468b52e42eae44a678434aff736b38/distlib-0.4.4-py2.py3-none-any.whl", hash = "sha256:cf09d55b1e85a89576b036aeb71550bfd68320f3f21761ca329eadadad918638", size = 472099, upload-time = "2026-10-16T13:56:32.053Z" }, +] + [[package]] name = "docutils" version = "0.22.4" @@ -345,6 +397,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, ] +[[package]] +name = "filelock" +version = "4.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/28/3893324bcadf8f89b1616637468d766af3f625008c0735bf704fdfa86617/filelock-4.2.4.tar.gz", hash = "sha256:c6d8663535e1650ee430507b4ffbac7b464dd64b7647ea0112a9f406499d163d", size = 652444, upload-time = "2026-10-19T01:57:51.001Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/1b/5e74289a1878e666bb4b65fe5805a04cafcac2aa5f519ef1ead67ef91c56/filelock-4.2.4-py3-none-any.whl", hash = "sha256:301241438c57936ef93f4243a94f2559022268bfcf9b375a9a72eb0f8db8358e", size = 162475, upload-time = "2026-10-19T01:57:49.419Z" }, +] + [[package]] name = "google-api-core" version = "2.30.3" @@ -559,6 +620,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/80/58cd2dfc19a07d022abe44bde7c365627f6c7cb6f692ada6c65ca437d09a/grpcio_status-1.80.0-py3-none-any.whl", hash = "sha256:4b56990363af50dbf2c2ebb80f1967185c07d87aa25aa2bea45ddb75fc181dbe", size = 14638, upload-time = "2026-03-30T08:54:01.569Z" }, ] +[[package]] +name = "humanize" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/ea/13a1ef3c12d12662905801495283530251918b70d62d368f1d2e0272c70d/humanize-4.16.0.tar.gz", hash = "sha256:7dc2244a2f84a4bfb1d36c37bac80cd78e35cdc5c119206d87b018e1445f3a3f", size = 89515, upload-time = "2026-06-30T16:17:29.859Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/aa/0b7365d30fed43e7a3449aba1fe20a0a7174d9cf13e282af4e69ac825441/humanize-4.16.0-py3-none-any.whl", hash = "sha256:353eb2f34c09d098b2880eee8bef21832eae6d174f48c5762fff7e5fcb74d01d", size = 137209, upload-time = "2026-06-30T16:17:28.36Z" }, +] + [[package]] name = "ibis-framework" version = "11.0.0" @@ -626,7 +696,7 @@ name = "importlib-metadata" version = "9.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp", marker = "python_full_version < '3.12'" }, + { name = "zipp" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a9/01/15bb152d77b21318514a96f43af312635eb2500c96b55398d020c93d86ea/importlib_metadata-9.0.0.tar.gz", hash = "sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc", size = 56405, upload-time = "2026-03-20T06:42:56.999Z" } wheels = [ @@ -767,6 +837,7 @@ dev = [ { name = "ibis-framework", extra = ["bigquery", "duckdb"] }, { name = "ipython" }, { name = "mypy" }, + { name = "nox" }, { name = "pandas-stubs" }, { name = "pytest" }, { name = "ruff" }, @@ -775,7 +846,7 @@ dev = [ [package.metadata] requires-dist = [ - { name = "ibis-framework", specifier = ">=11.0.0,<12.0.0" }, + { name = "ibis-framework", specifier = ">=11.0.0,<13.0.0" }, { name = "pandas", specifier = ">=2.2.0" }, { name = "pyarrow", specifier = ">=15.0.2" }, { name = "pyopenssl", specifier = ">=26.1.0" }, @@ -786,9 +857,10 @@ dev = [ { name = "ibis-framework", extras = ["bigquery", "duckdb"], specifier = ">=10.6.0" }, { name = "ipython", specifier = ">=8.37.0" }, { name = "mypy", specifier = ">=1.17.1" }, + { name = "nox", specifier = ">=2025.2.9" }, { name = "pandas-stubs", specifier = ">=2.3.0.250703" }, { name = "pytest", specifier = ">=8.4.1" }, - { name = "ruff", specifier = ">=0.12.4" }, + { name = "ruff", specifier = ">=0.12.4,<1.0.0" }, { name = "twine", specifier = ">=6.2.0" }, ] @@ -915,6 +987,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/80/7c/19cd0671d1ba2762fb388fc149697d20d0568ccfeef833b11280a619e526/nh3-0.3.5-cp38-abi3-win_arm64.whl", hash = "sha256:8f85285700a18e9f3fc5bff41fe573fa84f81542ef13b48a89f9fecca0474d3b", size = 611069, upload-time = "2026-04-25T10:44:14.934Z" }, ] +[[package]] +name = "nox" +version = "2026.8.17" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "argcomplete" }, + { name = "attrs" }, + { name = "colorlog" }, + { name = "dependency-groups" }, + { name = "humanize" }, + { name = "packaging" }, + { name = "platformdirs" }, + { name = "python-discovery" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/65/4cef8ae8f6dbcb5753b202e46791277f1ea0b4a0650d1a6cb940c468b143/nox-2026.8.17.tar.gz", hash = "sha256:8d9c69c9b996a59db1eb2c6968deaebc2edbc55317d205981bbc4a37351d3f2e", size = 4076047, upload-time = "2026-08-18T03:09:57.078Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/71/e0829a0500646070e98bc4442afa2917bfe5b6f1289511d8de853ae86497/nox-2026.8.17-py3-none-any.whl", hash = "sha256:a96a5286007cbc0d1eb1930e85738668f6722adba1ffaa48287296a96963086e", size = 94839, upload-time = "2026-08-18T03:09:55.684Z" }, +] + [[package]] name = "numpy" version = "2.3.5" @@ -1007,11 +1099,11 @@ wheels = [ [[package]] name = "packaging" -version = "25.0" +version = "26.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, ] [[package]] @@ -1143,6 +1235,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, ] +[[package]] +name = "platformdirs" +version = "4.13.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/e6/d0abbb256b44e6a05748c6942511ff67db8dbb068de0c35b24bf1b395b09/platformdirs-4.13.5.tar.gz", hash = "sha256:05831adf87d392760f4b5c3a0a95579ab5a10b18b9b9bd43680dcb47544788a0", size = 62362, upload-time = "2026-10-19T01:02:11.778Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/8a/1d8cb3842909dc193a73835adedf412e32e18f8ffd59f43983949622fe6f/platformdirs-4.13.5-py3-none-any.whl", hash = "sha256:53d3ec4280b815c1e0b02edad7f9ee8fda1ba9c0865dd9c2e6e63849ce82fc1e", size = 32779, upload-time = "2026-10-19T01:02:10.17Z" }, +] + [[package]] name = "pluggy" version = "1.6.0" @@ -1390,6 +1491,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] +[[package]] +name = "python-discovery" +version = "1.6.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/ee/cf608372c88ce2c136ee13c69ab6f667dbc0d0c1176af188b4ddc8853e66/python_discovery-1.6.4.tar.gz", hash = "sha256:bc70d31f0ec5de11eda13d4d64c16e24b3506a65984684f2b6c98cb83b2ea9df", size = 89883, upload-time = "2026-10-19T01:45:14.855Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/91/1aa70d3dfcab0dbcf88a2cbc3bce7f7c69562287a90216e617eaa4937e25/python_discovery-1.6.4-py3-none-any.whl", hash = "sha256:530179cc51a1816d512b15635b3dc931295ae12efb183783ad31190a5d425d2b", size = 39385, upload-time = "2026-10-19T01:45:13.668Z" }, +] + [[package]] name = "pytz" version = "2025.2" @@ -1638,6 +1751,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] +[[package]] +name = "virtualenv" +version = "21.14.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "packaging" }, + { name = "platformdirs" }, + { name = "python-discovery" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7f/be/4d1d59d309db76b5808fcfd78bd5996f4e7241366691c38fc331a3136398/virtualenv-21.14.11.tar.gz", hash = "sha256:593052d8477ea8c55a842387ce111bd08c72618f0c1133c302cafe335e570a57", size = 5503091, upload-time = "2026-10-18T07:06:34.716Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/6b/d85da8982b41190eafcb9df91dff9d83fc6989e98af4b5be4c8f85510c95/virtualenv-21.14.11-py3-none-any.whl", hash = "sha256:b319f8590a84e5de5f8f2c803ecd2237e9bc0ffa7c1e875e3169c107cecb7454", size = 5495268, upload-time = "2026-10-18T07:06:32.719Z" }, +] + [[package]] name = "wcwidth" version = "0.2.14"