diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index ff261ba..62c2d13 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,7 +3,6 @@ FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} USER vscode -RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.44.0" RYE_INSTALL_OPTION="--yes" bash -ENV PATH=/home/vscode/.rye/shims:$PATH +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ RUN echo "[[ -d .venv ]] && source .venv/bin/activate || export PATH=\$PATH" >> /home/vscode/.bashrc diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c17fdc1..e01283d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -7,7 +7,7 @@ "context": ".." }, - "postStartCommand": "rye sync --all-features", + "postStartCommand": "uv sync --all-extras", "customizations": { "vscode": { @@ -20,7 +20,7 @@ "python.defaultInterpreterPath": ".venv/bin/python", "python.typeChecking": "basic", "terminal.integrated.env.linux": { - "PATH": "/home/vscode/.rye/shims:${env:PATH}" + "PATH": "${env:PATH}" } } } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d32fa1..799f168 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,16 +21,13 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install Rye - run: | - curl -sSf https://rye.astral.sh/get | bash - echo "$HOME/.rye/shims" >> $GITHUB_PATH - env: - RYE_VERSION: '0.44.0' - RYE_INSTALL_OPTION: '--yes' + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + version: '0.8.11' - name: Install dependencies - run: rye sync --all-features + run: uv sync --all-extras - name: Run lints run: ./scripts/lint @@ -46,19 +43,16 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install Rye - run: | - curl -sSf https://rye.astral.sh/get | bash - echo "$HOME/.rye/shims" >> $GITHUB_PATH - env: - RYE_VERSION: '0.44.0' - RYE_INSTALL_OPTION: '--yes' + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + version: '0.8.11' - name: Install dependencies - run: rye sync --all-features + run: uv sync --all-extras - name: Run build - run: rye build + run: uv build - name: Get GitHub OIDC Token if: github.repository == 'stainless-sdks/replicate-client-python' @@ -83,13 +77,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install Rye - run: | - curl -sSf https://rye.astral.sh/get | bash - echo "$HOME/.rye/shims" >> $GITHUB_PATH - env: - RYE_VERSION: '0.44.0' - RYE_INSTALL_OPTION: '--yes' + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + version: '0.8.11' - name: Bootstrap run: ./scripts/bootstrap diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index effe4f0..a9a4e2b 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -16,13 +16,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install Rye - run: | - curl -sSf https://rye.astral.sh/get | bash - echo "$HOME/.rye/shims" >> $GITHUB_PATH - env: - RYE_VERSION: '0.44.0' - RYE_INSTALL_OPTION: '--yes' + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + version: '0.8.11' - name: Publish to PyPI run: | diff --git a/Brewfile b/Brewfile index 492ca37..c43041c 100644 --- a/Brewfile +++ b/Brewfile @@ -1,2 +1,2 @@ -brew "rye" +brew "uv" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a84fcdc..01ee07d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,32 +1,32 @@ ## Setting up the environment -### With Rye +### With `uv` -We use [Rye](https://rye.astral.sh/) to manage dependencies because it will automatically provision a Python environment with the expected Python version. To set it up, run: +We use [uv](https://docs.astral.sh/uv/) to manage dependencies because it will automatically provision a Python environment with the expected Python version. To set it up, run: ```sh $ ./scripts/bootstrap ``` -Or [install Rye manually](https://rye.astral.sh/guide/installation/) and run: +Or [install uv manually](https://docs.astral.sh/uv/getting-started/installation/) and run: ```sh -$ rye sync --all-features +$ uv sync --all-extras ``` -You can then run scripts using `rye run python script.py` or by activating the virtual environment: +You can then run scripts using `uv run python script.py` or by manually activating the virtual environment: ```sh -# Activate the virtual environment - https://docs.python.org/3/library/venv.html#how-venvs-work +# manually activate - https://docs.python.org/3/library/venv.html#how-venvs-work $ source .venv/bin/activate -# now you can omit the `rye run` prefix +# now you can omit the `uv run` prefix $ python script.py ``` -### Without Rye +### Without `uv` -Alternatively if you don't want to install `Rye`, you can stick with the standard `pip` setup by ensuring you have the Python version specified in `.python-version`, create a virtual environment however you desire and then install dependencies using this command: +Alternatively if you don't want to install `uv`, you can stick with the standard `pip` setup by ensuring you have the Python version specified in `.python-version`, create a virtual environment however you desire and then install dependencies using this command: ```sh $ pip install -r requirements-dev.lock @@ -45,7 +45,7 @@ All files in the `examples/` directory are not modified by the generator and can ```py # add an example to examples/.py -#!/usr/bin/env -S rye run python +#!/usr/bin/env -S uv run python … ``` @@ -72,7 +72,7 @@ Building this package will create two files in the `dist/` directory, a `.tar.gz To create a distributable version of the library, all you have to do is run this command: ```sh -$ rye build +$ uv build # or $ python -m build ``` diff --git a/bin/publish-pypi b/bin/publish-pypi index 826054e..e72ca2f 100644 --- a/bin/publish-pypi +++ b/bin/publish-pypi @@ -1,6 +1,7 @@ #!/usr/bin/env bash set -eux +rm -rf dist mkdir -p dist -rye build --clean -rye publish --yes --token=$PYPI_TOKEN +uv build +uv publish --token=$PYPI_TOKEN diff --git a/noxfile.py b/noxfile.py deleted file mode 100644 index 53bca7f..0000000 --- a/noxfile.py +++ /dev/null @@ -1,9 +0,0 @@ -import nox - - -@nox.session(reuse_venv=True, name="test-pydantic-v1") -def test_pydantic_v1(session: nox.Session) -> None: - session.install("-r", "requirements-dev.lock") - session.install("pydantic<2") - - session.run("pytest", "--showlocals", "--ignore=tests/functional", *session.posargs) diff --git a/pyproject.toml b/pyproject.toml index e6c0000..55881ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,10 +41,19 @@ Repository = "https://github.com/replicate/replicate-python-beta" [project.optional-dependencies] aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] -[tool.rye] +[tool.uv] managed = true -# version pins are in requirements-dev.lock -dev-dependencies = [ +required-version = ">=0.5.0" +conflicts = [ + [ + { group = "pydantic-v1" }, + { group = "pydantic-v2" }, + ], +] + +[dependency-groups] +# version pins are in uv.lock +dev = [ "pyright==1.1.399", "mypy", "respx", @@ -52,41 +61,18 @@ dev-dependencies = [ "pytest-asyncio", "ruff", "time-machine", - "nox", "dirty-equals>=0.6.0", "importlib-metadata>=6.7.0", "rich>=13.7.1", "pytest-xdist>=3.6.1", ] - -[tool.rye.scripts] -format = { chain = [ - "format:ruff", - "format:docs", - "fix:ruff", - # run formatting again to fix any inconsistencies when imports are stripped - "format:ruff", -]} -"format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md" -"format:ruff" = "ruff format" - -"lint" = { chain = [ - "check:ruff", - "typecheck", - "check:importable", -]} -"check:ruff" = "ruff check ." -"fix:ruff" = "ruff check --fix ." - -"check:importable" = "python -c 'import replicate'" - -typecheck = { chain = [ - "typecheck:pyright", - "typecheck:mypy" -]} -"typecheck:pyright" = "pyright" -"typecheck:verify-types" = "pyright --verifytypes replicate --ignoreexternal" -"typecheck:mypy" = "mypy ." +pydantic-v1 = [ + "pydantic>=1.9.0,<2", +] +pydantic-v2 = [ + "pydantic~=2.0 ; python_full_version < '3.14'", + "pydantic~=2.12 ; python_full_version >= '3.14'", +] [build-system] requires = ["hatchling==1.26.3", "hatch-fancy-pypi-readme"] diff --git a/requirements-dev.lock b/requirements-dev.lock index 7fbcdb3..a6e00b9 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -1,138 +1,50 @@ -# generated by rye -# use `rye lock` or `rye sync` to update this lockfile -# -# last locked with the following flags: -# pre: false -# features: [] -# all-features: true -# with-sources: false -# generate-hashes: false -# universal: false - --e file:. -aiohappyeyeballs==2.6.1 - # via aiohttp -aiohttp==3.12.8 - # via httpx-aiohttp - # via replicate -aiosignal==1.3.2 - # via aiohttp -annotated-types==0.6.0 - # via pydantic -anyio==4.4.0 - # via httpx - # via replicate -argcomplete==3.1.2 - # via nox -async-timeout==5.0.1 - # via aiohttp -attrs==25.3.0 - # via aiohttp -certifi==2023.7.22 - # via httpcore - # via httpx -colorlog==6.7.0 - # via nox -dirty-equals==0.6.0 -distlib==0.3.7 - # via virtualenv -distro==1.8.0 - # via replicate -exceptiongroup==1.2.2 - # via anyio - # via pytest -execnet==2.1.1 - # via pytest-xdist -filelock==3.12.4 - # via virtualenv -frozenlist==1.6.2 - # via aiohttp - # via aiosignal +# This file was autogenerated by uv via the following command: +# uv export -o requirements-dev.lock --no-hashes +-e . +annotated-types==0.7.0 ; extra == 'group-9-replicate-pydantic-v2' +anyio==4.11.0 +backports-asyncio-runner==1.2.0 ; python_full_version < '3.11' or (extra == 'group-9-replicate-pydantic-v1' and extra == 'group-9-replicate-pydantic-v2') +certifi==2025.11.12 +colorama==0.4.6 ; sys_platform == 'win32' or (extra == 'group-9-replicate-pydantic-v1' and extra == 'group-9-replicate-pydantic-v2') +dirty-equals==0.11 +distro==1.9.0 +exceptiongroup==1.3.1 ; python_full_version < '3.11' or (extra == 'group-9-replicate-pydantic-v1' and extra == 'group-9-replicate-pydantic-v2') +execnet==2.1.2 h11==0.16.0 - # via httpcore httpcore==1.0.9 - # via httpx httpx==0.28.1 - # via httpx-aiohttp - # via replicate - # via respx -httpx-aiohttp==0.1.9 - # via replicate -idna==3.4 - # via anyio - # via httpx - # via yarl -importlib-metadata==7.0.0 -iniconfig==2.0.0 - # via pytest -markdown-it-py==3.0.0 - # via rich +idna==3.11 +importlib-metadata==8.7.0 +iniconfig==2.1.0 ; python_full_version < '3.10' or (extra == 'group-9-replicate-pydantic-v1' and extra == 'group-9-replicate-pydantic-v2') +iniconfig==2.3.0 ; python_full_version >= '3.10' or (extra == 'group-9-replicate-pydantic-v1' and extra == 'group-9-replicate-pydantic-v2') +markdown-it-py==3.0.0 ; python_full_version < '3.10' or (extra == 'group-9-replicate-pydantic-v1' and extra == 'group-9-replicate-pydantic-v2') +markdown-it-py==4.0.0 ; python_full_version >= '3.10' or (extra == 'group-9-replicate-pydantic-v1' and extra == 'group-9-replicate-pydantic-v2') mdurl==0.1.2 - # via markdown-it-py -multidict==6.4.4 - # via aiohttp - # via yarl -mypy==1.14.1 -mypy-extensions==1.0.0 - # via mypy -nodeenv==1.8.0 - # via pyright -nox==2023.4.22 +mypy==1.18.2 +mypy-extensions==1.1.0 +nodeenv==1.9.1 packaging==25.0 - # via nox - # via pytest - # via replicate -platformdirs==3.11.0 - # via virtualenv -pluggy==1.5.0 - # via pytest -propcache==0.3.1 - # via aiohttp - # via yarl -pydantic==2.11.9 - # via replicate -pydantic-core==2.33.2 - # via pydantic -pygments==2.18.0 - # via rich +pathspec==0.12.1 +pluggy==1.6.0 +pydantic==1.10.24 ; extra == 'group-9-replicate-pydantic-v1' or extra != 'group-9-replicate-pydantic-v2' +pydantic==2.12.4 ; extra == 'group-9-replicate-pydantic-v2' +pydantic-core==2.41.5 ; extra == 'group-9-replicate-pydantic-v2' +pygments==2.19.2 pyright==1.1.399 -pytest==8.3.3 - # via pytest-asyncio - # via pytest-xdist -pytest-asyncio==0.24.0 -pytest-xdist==3.7.0 -python-dateutil==2.8.2 - # via time-machine -pytz==2023.3.post1 - # via dirty-equals +pytest==8.4.2 ; python_full_version < '3.10' or (extra == 'group-9-replicate-pydantic-v1' and extra == 'group-9-replicate-pydantic-v2') +pytest==9.0.1 ; python_full_version >= '3.10' or (extra == 'group-9-replicate-pydantic-v1' and extra == 'group-9-replicate-pydantic-v2') +pytest-asyncio==1.2.0 ; python_full_version < '3.10' or (extra == 'group-9-replicate-pydantic-v1' and extra == 'group-9-replicate-pydantic-v2') +pytest-asyncio==1.3.0 ; python_full_version >= '3.10' or (extra == 'group-9-replicate-pydantic-v1' and extra == 'group-9-replicate-pydantic-v2') +pytest-xdist==3.8.0 +python-dateutil==2.9.0.post0 ; python_full_version < '3.10' or (extra == 'group-9-replicate-pydantic-v1' and extra == 'group-9-replicate-pydantic-v2') respx==0.22.0 -rich==13.7.1 -ruff==0.9.4 -setuptools==68.2.2 - # via nodeenv -six==1.16.0 - # via python-dateutil -sniffio==1.3.0 - # via anyio - # via replicate -time-machine==2.9.0 -tomli==2.0.2 - # via mypy - # via pytest -typing-extensions==4.12.2 - # via anyio - # via multidict - # via mypy - # via pydantic - # via pydantic-core - # via pyright - # via replicate - # via typing-inspection -typing-inspection==0.4.1 - # via pydantic -virtualenv==20.24.5 - # via nox -yarl==1.20.0 - # via aiohttp -zipp==3.17.0 - # via importlib-metadata +rich==14.2.0 +ruff==0.14.6 +six==1.17.0 ; python_full_version < '3.10' or (extra == 'group-9-replicate-pydantic-v1' and extra == 'group-9-replicate-pydantic-v2') +sniffio==1.3.1 +time-machine==2.19.0 ; python_full_version < '3.10' or (extra == 'group-9-replicate-pydantic-v1' and extra == 'group-9-replicate-pydantic-v2') +time-machine==3.1.0 ; python_full_version >= '3.10' or (extra == 'group-9-replicate-pydantic-v1' and extra == 'group-9-replicate-pydantic-v2') +tomli==2.3.0 ; python_full_version < '3.11' or (extra == 'group-9-replicate-pydantic-v1' and extra == 'group-9-replicate-pydantic-v2') +typing-extensions==4.15.0 +typing-inspection==0.4.2 ; extra == 'group-9-replicate-pydantic-v2' +zipp==3.23.0 diff --git a/scripts/bootstrap b/scripts/bootstrap index b430fee..4638ec6 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -19,9 +19,12 @@ if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] } fi -echo "==> Installing Python dependencies…" +echo "==> Installing Python…" +uv python install -# experimental uv support makes installations significantly faster -rye config --set-bool behavior.use-uv=true +echo "==> Installing Python dependencies…" +uv sync --all-extras -rye sync --all-features +echo "==> Exporting Python dependencies…" +# note: `--no-hashes` is required because of https://github.com/pypa/pip/issues/4995 +uv export -o requirements-dev.lock --no-hashes diff --git a/scripts/format b/scripts/format index 667ec2d..1d2f9c6 100755 --- a/scripts/format +++ b/scripts/format @@ -4,5 +4,11 @@ set -e cd "$(dirname "$0")/.." -echo "==> Running formatters" -rye run format +echo "==> Running ruff" +uv run ruff format +uv run ruff check --fix . +# run formatting again to fix any inconsistencies when imports are stripped +uv run ruff format + +echo "==> Formatting docs" +uv run python scripts/utils/ruffen-docs.py README.md api.md diff --git a/scripts/lint b/scripts/lint index cce2174..0c4d2d7 100755 --- a/scripts/lint +++ b/scripts/lint @@ -4,8 +4,14 @@ set -e cd "$(dirname "$0")/.." -echo "==> Running lints" -rye run lint +echo "==> Running ruff" +uv run ruff check . + +echo "==> Running pyright" +uv run pyright + +echo "==> Running mypy" +uv run mypy . echo "==> Making sure it imports" -rye run python -c 'import replicate' +uv run python -c 'import replicate' diff --git a/scripts/test b/scripts/test index dbeda2d..a93b8a2 100755 --- a/scripts/test +++ b/scripts/test @@ -54,8 +54,26 @@ fi export DEFER_PYDANTIC_BUILD=false -echo "==> Running tests" -rye run pytest "$@" +function run_tests() { + echo "==> Running tests with Pydantic v2" + uv run --isolated --all-extras pytest "$@" -echo "==> Running Pydantic v1 tests" -rye run nox -s test-pydantic-v1 -- "$@" + # Pydantic v1 does not support Python 3.14, skip these tests + if [[ "$UV_PYTHON" != "3.14" ]]; then + echo "==> Running tests with Pydantic v1" + uv run --isolated --all-extras --group=pydantic-v1 pytest "$@" + fi +} + +# If UV_PYTHON is already set in the environment, just run the command once +if [[ -n "$UV_PYTHON" ]]; then + run_tests "$@" +else + # If UV_PYTHON is not set, run the command for min and max versions + + echo "==> Running tests for Python 3.9" + UV_PYTHON=3.9 run_tests "$@" + + echo "==> Running tests for Python 3.14" + UV_PYTHON=3.14 run_tests "$@" +fi