diff --git a/CLAUDE.md b/CLAUDE.md index ef3751b5..f9efa95f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,7 +13,7 @@ uv sync --locked --dev # Install package in development mode with dev dependenci ### Code Quality ```bash -ur run ruff format . # Format code +uv run ruff format . # Format code uv run ruff format --check . # Check formatting without making changes uv run ruff check . # Lint code uv run mypy # Type checking @@ -21,8 +21,36 @@ uv run mypy # Type checking ### Testing +The SDK uses [nox](https://nox.thea.codes/) with [nox-uv](https://github.com/dantebben/nox-uv) for multi-version Python testing. This ensures compatibility across all supported Python versions (3.8-3.14). + +**Quick testing with the test script:** + +```bash +./scripts/test.sh # Run tests on all Python versions +./scripts/test.sh 3.12 # Run tests on Python 3.12 only +./scripts/test.sh 3.11 3.12 # Run tests on Python 3.11 and 3.12 +./scripts/test.sh --coverage # Run tests with coverage on all versions +./scripts/test.sh --ci # Run full CI checks (lint, type, tests) +./scripts/test.sh --fresh # Recreate virtual environments +./scripts/test.sh 3.12 -- -k "test_sso" -v # Pass pytest arguments +``` + +**Direct nox commands:** + +```bash +uv run nox # Run tests on all Python versions +uv run nox -s tests-3.12 # Run tests on specific Python version +uv run nox -s coverage # Run tests with coverage +uv run nox -s lint # Run linting +uv run nox -s typecheck # Run type checking +uv run nox -s ci # Run all CI checks +uv run nox -l # List all available sessions +``` + +**Single-version testing (faster for development):** + ```bash -uv run pytest # Run all tests +uv run pytest # Run all tests on current Python uv run pytest tests/test_sso.py # Run specific test file uv run pytest -k "test_name" # Run tests matching pattern uv run pytest --cov=workos # Run tests with coverage diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 00000000..e02a831c --- /dev/null +++ b/noxfile.py @@ -0,0 +1,72 @@ +"""Nox configuration for multi-version Python testing. + +This configuration uses nox-uv for fast, reproducible environment management +with uv's lockfile. Run `nox` to test against all supported Python versions, +or use `nox -s tests-3.12` to test a specific version. +""" + +from __future__ import annotations + +import nox +from nox_uv import session + +# Use uv as the default venv backend for speed +nox.options.default_venv_backend = "uv" + +# Reuse virtual environments by default for faster local iteration +nox.options.reuse_venv = "yes" + +# All Python versions supported by the SDK (must match CI matrix) +PYTHON_VERSIONS = ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + +# Default sessions to run +nox.options.sessions = ["tests"] + + +@session(python=PYTHON_VERSIONS, uv_groups=["test"]) +def tests(s: nox.Session) -> None: + """Run the test suite against all supported Python versions.""" + args = s.posargs or [] + s.run("pytest", *args) + + +@session(python=PYTHON_VERSIONS, uv_groups=["test"]) +def coverage(s: nox.Session) -> None: + """Run tests with coverage reporting.""" + s.run("pytest", "--cov=workos", "--cov-report=term-missing", *s.posargs) + + +@session(uv_only_groups=["lint"]) +def lint(s: nox.Session) -> None: + """Run linting with ruff.""" + s.run("ruff", "check", ".") + + +@session(uv_only_groups=["lint"]) +def format(s: nox.Session) -> None: + """Check code formatting with ruff.""" + s.run("ruff", "format", "--check", ".") + + +@session(uv_only_groups=["lint"]) +def format_fix(s: nox.Session) -> None: + """Apply code formatting with ruff.""" + s.run("ruff", "format", ".") + + +@session(uv_groups=["type_check"]) +def typecheck(s: nox.Session) -> None: + """Run type checking with mypy.""" + s.run("mypy") + + +@session(uv_groups=["test", "lint", "type_check"]) +def ci(s: nox.Session) -> None: + """Run all CI checks (format, lint, typecheck, tests) for a single Python version. + + This is useful for quick local validation before pushing. + """ + s.run("ruff", "format", "--check", ".") + s.run("ruff", "check", ".") + s.run("mypy") + s.run("pytest") diff --git a/pyproject.toml b/pyproject.toml index ddada57e..5a7aa200 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ dev = [ { include-group = "test" }, { include-group = "lint" }, { include-group = "type_check" }, + { include-group = "nox" }, ] test = [ "pytest==8.3.4", @@ -34,6 +35,10 @@ test = [ ] lint = ["ruff==0.14.5"] type_check = ["mypy==1.14.1"] +nox = [ + "nox>=2024.10.9 ; python_version >= '3.9'", + "nox-uv>=0.7.0 ; python_version >= '3.9'", +] [tool.mypy] diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 00000000..8b2ed6ac --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +# +# Usage: +# ./scripts/test.sh # Run tests on all Python versions +# ./scripts/test.sh 3.12 # Run tests on Python 3.12 only +# ./scripts/test.sh 3.11 3.12 # Run tests on Python 3.11 and 3.12 +# ./scripts/test.sh --coverage # Run tests with coverage on all versions +# ./scripts/test.sh --ci # Run full CI checks (lint, type, tests) +# ./scripts/test.sh --fresh # Recreate virtual environments +# +# Additional pytest arguments can be passed after --, e.g.: +# ./scripts/test.sh 3.12 -- -k "test_sso" -v + +set -e + +# Check if uv is available +if ! command -v uv &>/dev/null; then + echo "Error: uv is not installed or not in PATH" + echo "Install uv: https://docs.astral.sh/uv/getting-started/installation/" + exit 1 +fi + +# Parse arguments +PYTHON_VERSIONS=() +NOX_ARGS=() +PYTEST_ARGS=() +SESSION="tests" +FRESH=false +PARSING_PYTEST_ARGS=false + +for arg in "$@"; do + if [[ "$PARSING_PYTEST_ARGS" == true ]]; then + PYTEST_ARGS+=("$arg") + elif [[ "$arg" == "--" ]]; then + PARSING_PYTEST_ARGS=true + elif [[ "$arg" == "--coverage" ]]; then + SESSION="coverage" + elif [[ "$arg" == "--ci" ]]; then + SESSION="ci" + elif [[ "$arg" == "--fresh" ]]; then + FRESH=true + elif [[ "$arg" =~ ^3\.[0-9]+$ ]]; then + PYTHON_VERSIONS+=("$arg") + else + NOX_ARGS+=("$arg") + fi +done + +# Build the nox command +CMD=(uv run nox -s) + +if [[ ${#PYTHON_VERSIONS[@]} -gt 0 ]]; then + # Run specific Python versions + SESSIONS="" + for ver in "${PYTHON_VERSIONS[@]}"; do + if [[ -n "$SESSIONS" ]]; then + SESSIONS="$SESSIONS," + fi + SESSIONS="${SESSIONS}${SESSION}-${ver}" + done + CMD+=("$SESSIONS") +else + # Run all versions + CMD+=("$SESSION") +fi + +# Add fresh flag if requested +if [[ "$FRESH" == true ]]; then + CMD+=(--reuse-venv=never) +fi + +# Add any additional nox args +if [[ ${#NOX_ARGS[@]} -gt 0 ]]; then + CMD+=("${NOX_ARGS[@]}") +fi + +# Add pytest args if provided +if [[ ${#PYTEST_ARGS[@]} -gt 0 ]]; then + CMD+=(-- "${PYTEST_ARGS[@]}") +fi + +echo "Running: ${CMD[*]}" +exec "${CMD[@]}" diff --git a/uv.lock b/uv.lock index a38d646f..c08b0458 100644 --- a/uv.lock +++ b/uv.lock @@ -56,6 +56,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, ] +[[package]] +name = "argcomplete" +version = "3.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/61/0b9ae6399dd4a58d8c1b1dc5a27d6f2808023d0b5dd3104bb99f45a33ff6/argcomplete-3.6.3.tar.gz", hash = "sha256:62e8ed4fd6a45864acc8235409461b72c9a28ee785a2011cc5eb78318786c89c", size = 73754, upload-time = "2025-10-20T03:33:34.741Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/f5/9373290775639cb67a2fce7f629a1c240dce9f12fe927bc32b2736e16dfc/argcomplete-3.6.3-py3-none-any.whl", hash = "sha256:f5007b3a600ccac5d25bbce33089211dfd49eab4a7718da3f10e3082525a92ce", size = 43846, upload-time = "2025-10-20T03:33:33.021Z" }, +] + +[[package]] +name = "attrs" +version = "25.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, +] + [[package]] name = "certifi" version = "2025.11.12" @@ -252,6 +270,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.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "python_full_version >= '3.9' and sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321", size = 17162, upload-time = "2025-10-16T16:14:11.978Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/c1/e419ef3723a074172b68aaa89c9f3de486ed4c2399e2dbd8113a4fdcaf9e/colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c", size = 11743, upload-time = "2025-10-16T16:14:10.512Z" }, +] + [[package]] name = "coverage" version = "7.6.1" @@ -632,6 +662,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0d/c3/e90f4a4feae6410f914f8ebac129b9ae7a8c92eb60a638012dde42030a9d/cryptography-46.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6b5063083824e5509fdba180721d55909ffacccc8adbec85268b48439423d78c", size = 3438528, upload-time = "2025-10-15T23:18:26.227Z" }, ] +[[package]] +name = "dependency-groups" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging", marker = "python_full_version >= '3.9'" }, + { name = "tomli", marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/62/55/f054de99871e7beb81935dea8a10b90cd5ce42122b1c3081d5282fdb3621/dependency_groups-1.3.1.tar.gz", hash = "sha256:78078301090517fd938c19f64a53ce98c32834dfe0dee6b88004a569a6adfefd", size = 10093, upload-time = "2025-05-02T00:34:29.452Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/c7/d1ec24fb280caa5a79b6b950db565dab30210a66259d17d5bb2b3a9f878d/dependency_groups-1.3.1-py3-none-any.whl", hash = "sha256:51aeaa0dfad72430fcfb7bcdbefbd75f3792e5919563077f30bc0d73f4493030", size = 8664, upload-time = "2025-05-02T00:34:27.085Z" }, +] + +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + [[package]] name = "exceptiongroup" version = "1.3.0" @@ -645,6 +697,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, ] +[[package]] +name = "filelock" +version = "3.19.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.9.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, +] + +[[package]] +name = "filelock" +version = "3.20.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485, upload-time = "2026-01-09T17:55:05.421Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" }, +] + [[package]] name = "h11" version = "0.16.0" @@ -683,6 +759,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] +[[package]] +name = "humanize" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.9.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/98/1d/3062fcc89ee05a715c0b9bfe6490c00c576314f27ffee3a704122c6fd259/humanize-4.13.0.tar.gz", hash = "sha256:78f79e68f76f0b04d711c4e55d32bebef5be387148862cb1ef83d2b58e7935a0", size = 81884, upload-time = "2025-08-25T09:39:20.04Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/c7/316e7ca04d26695ef0635dc81683d628350810eb8e9b2299fc08ba49f366/humanize-4.13.0-py3-none-any.whl", hash = "sha256:b810820b31891813b1673e8fec7f1ed3312061eab2f26e3fa192c393d11ed25f", size = 128869, upload-time = "2025-08-25T09:39:18.54Z" }, +] + +[[package]] +name = "humanize" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/66/a3921783d54be8a6870ac4ccffcd15c4dc0dd7fcce51c6d63b8c63935276/humanize-4.15.0.tar.gz", hash = "sha256:1dd098483eb1c7ee8e32eb2e99ad1910baefa4b75c3aff3a82f4d78688993b10", size = 83599, upload-time = "2025-12-20T20:16:13.19Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/7b/bca5613a0c3b542420cf92bd5e5fb8ebd5435ce1011a091f66bb7693285e/humanize-4.15.0-py3-none-any.whl", hash = "sha256:b1186eb9f5a9749cd9cb8565aee77919dd7c8d076161cf44d70e59e3301e1769", size = 132203, upload-time = "2025-12-20T20:16:11.67Z" }, +] + [[package]] name = "idna" version = "3.11" @@ -777,6 +877,38 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, ] +[[package]] +name = "nox" +version = "2025.11.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "argcomplete", marker = "python_full_version >= '3.9'" }, + { name = "attrs", marker = "python_full_version >= '3.9'" }, + { name = "colorlog", marker = "python_full_version >= '3.9'" }, + { name = "dependency-groups", marker = "python_full_version >= '3.9'" }, + { name = "humanize", version = "4.13.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "humanize", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "packaging", marker = "python_full_version >= '3.9'" }, + { name = "tomli", marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, + { name = "virtualenv", marker = "python_full_version >= '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b4/a8/e169497599266d176832e2232c08557ffba97eef87bf8a18f9f918e0c6aa/nox-2025.11.12.tar.gz", hash = "sha256:3d317f9e61f49d6bde39cf2f59695bb4e1722960457eee3ae19dacfe03c07259", size = 4030561, upload-time = "2025-11-12T18:39:03.319Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/34/434c594e0125a16b05a7bedaea33e63c90abbfbe47e5729a735a8a8a90ea/nox-2025.11.12-py3-none-any.whl", hash = "sha256:707171f9f63bc685da9d00edd8c2ceec8405b8e38b5fb4e46114a860070ef0ff", size = 74447, upload-time = "2025-11-12T18:39:01.575Z" }, +] + +[[package]] +name = "nox-uv" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nox", marker = "python_full_version >= '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/a2/be2f4d0fd1632213eb5962c8e5ee27def06305403786e21fc3179c7e26e6/nox_uv-0.7.0.tar.gz", hash = "sha256:60ac21c16650f05ebb520d737cc2e838c7a49be2ad1dbcd7165ffd3675560991", size = 5077, upload-time = "2026-01-03T19:54:59.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/94/afa119b031c08e74a2e078260c5cdbb56b53949e5d2a0158215e8c265e12/nox_uv-0.7.0-py3-none-any.whl", hash = "sha256:51f9bb68ca6d721706f6372f0dec8ebfcc00408c2172b2bfeb6a6c06d9e3ab38", size = 5408, upload-time = "2026-01-03T19:54:57.798Z" }, +] + [[package]] name = "packaging" version = "25.0" @@ -786,6 +918,30 @@ 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" }, ] +[[package]] +name = "platformdirs" +version = "4.4.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.9.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.5.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, +] + [[package]] name = "pluggy" version = "1.5.0" @@ -1305,6 +1461,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, ] +[[package]] +name = "virtualenv" +version = "20.36.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib", marker = "python_full_version >= '3.9'" }, + { name = "filelock", version = "3.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "filelock", version = "3.20.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "platformdirs", version = "4.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/a3/4d310fa5f00863544e1d0f4de93bddec248499ccf97d4791bc3122c9d4f3/virtualenv-20.36.1.tar.gz", hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba", size = 6032239, upload-time = "2026-01-09T18:21:01.296Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/2a/dc2228b2888f51192c7dc766106cd475f1b768c10caaf9727659726f7391/virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f", size = 6008258, upload-time = "2026-01-09T18:20:59.425Z" }, +] + [[package]] name = "workos" version = "5.38.1" @@ -1321,6 +1494,8 @@ dependencies = [ [package.dev-dependencies] dev = [ { name = "mypy" }, + { name = "nox", marker = "python_full_version >= '3.9'" }, + { name = "nox-uv", marker = "python_full_version >= '3.9'" }, { name = "pytest" }, { name = "pytest-asyncio" }, { name = "pytest-cov" }, @@ -1330,6 +1505,10 @@ dev = [ lint = [ { name = "ruff" }, ] +nox = [ + { name = "nox", marker = "python_full_version >= '3.9'" }, + { name = "nox-uv", marker = "python_full_version >= '3.9'" }, +] test = [ { name = "pytest" }, { name = "pytest-asyncio" }, @@ -1352,6 +1531,8 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ { name = "mypy", specifier = "==1.14.1" }, + { name = "nox", marker = "python_full_version >= '3.9'", specifier = ">=2024.10.9" }, + { name = "nox-uv", marker = "python_full_version >= '3.9'", specifier = ">=0.7.0" }, { name = "pytest", specifier = "==8.3.4" }, { name = "pytest-asyncio", specifier = "==0.23.8" }, { name = "pytest-cov", specifier = "==5.0.0" }, @@ -1359,6 +1540,10 @@ dev = [ { name = "six", specifier = "==1.17.0" }, ] lint = [{ name = "ruff", specifier = "==0.14.5" }] +nox = [ + { name = "nox", marker = "python_full_version >= '3.9'", specifier = ">=2024.10.9" }, + { name = "nox-uv", marker = "python_full_version >= '3.9'", specifier = ">=0.7.0" }, +] test = [ { name = "pytest", specifier = "==8.3.4" }, { name = "pytest-asyncio", specifier = "==0.23.8" },