{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Bring your own wheels\n", "\n", "Some functions need Python packages that are not baked into the runtime image. If a package is **private or internal**, or simply not part of the image, you can **build a wheel for it yourself, upload it with your function, and install it from your entrypoint** at run time.\n", "\n", "This guide shows the full loop:\n", "\n", "1. See what the runtime image already ships, so you don't bring something you don't need.\n", "2. Check your requirements against that set.\n", "3. Upload your wheels.\n", "4. Install them from your entrypoint and import them.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Discover what the image already ships\n", "\n", "`dependencies_versions()` is read-only discovery of the dependencies baked into the runtime image. Anything listed here is already available to your function \u2014 you do not need to bring it yourself.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qiskit_serverless import IBMServerlessClient\n", "\n", "client = IBMServerlessClient()\n", "client.dependencies_versions()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Check your requirements\n", "\n", "`dependency_report()` diffs a list of requirements against what the image ships. Each requirement is reported as:\n", "\n", "- **baked** \u2014 already in the image; don't bring it.\n", "- **bring** \u2014 not in the image; upload a wheel.\n", "- **clash** \u2014 the image ships a different version than you asked for.\n", "\n", "This is a client-side diff; it uploads nothing.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "report = client.dependency_report([\"numpy>=2\", \"mypkg\", \"jax<0.5\"])\n", "print(report)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Build and upload your wheels\n", "\n", "Build a wheel for each package you need to bring (for example with `python -m build --wheel`, or `pip wheel`). Because installation happens offline with `--no-deps` (see below), **each wheel must contain everything it needs** that is not already baked into the image.\n", "\n", "Then upload the wheels for a function with `add_wheels()`. It accepts paths or globs and only accepts `.whl` files.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qiskit_serverless import QiskitFunction\n", "\n", "function = QiskitFunction(\n", " title=\"my-function\",\n", " entrypoint=\"function_with_wheels.py\",\n", " working_dir=\"./source_files/\",\n", ")\n", "\n", "client.add_wheels(function, [\"dist/mypkg-1.0-*.whl\"])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. Install the wheels from your entrypoint\n", "\n", "Call `install_uploaded_wheels()` at the top of your entrypoint, before importing the uploaded packages. It installs every uploaded wheel offline and makes it importable for the rest of the run.\n", "\n", "```python\n", "from qiskit_serverless import install_uploaded_wheels, save_result\n", "\n", "install_uploaded_wheels()\n", "\n", "import mypkg # now importable\n", "\n", "save_result({\"mypkg_version\": mypkg.__version__})\n", "```\n", "\n", "Under the hood this runs `pip install --no-index --no-deps --find-links /function_user_data/wheels --target /tmp/site` and prepends the target to `sys.path`. `add_wheels()` uploads into that `wheels` directory, and the helper reads from it. `--no-index` keeps the install fully offline; `--no-deps` is required, because otherwise the offline install would try to satisfy packages already baked into the image (numpy, qiskit, ...) from your wheelhouse and fail.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Notes and trade-offs\n", "\n", "- Wheels are installed **on every execution**, which adds start-up time.\n", "- Because you build the wheels, anything needing compilation or lacking a prebuilt wheel is up to you.\n", "- This is intended for private or internal packages. If a package would be broadly useful, consider requesting that it be added to the runtime image instead.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "function = client.upload(function)\n", "job = function.run()\n", "print(job.result())" ] } ], "metadata": { "kernelspec": { "display_name": "Python (qiskit_serverless test venv)", "language": "python", "name": "qiskit_serverless_test_venv" }, "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }