Dependencies
There is no requirements file to keep. Waiso reads the imports of your code, finds the package each one needs and installs it on the worker before main runs. For Python you can pin a version, add a package the code loads by name or swap the package behind an import in the Dependencies panel — and publishing freezes the exact versions, so a new upstream release never changes a published script.
- See how a Python import becomes a PyPI package (
yaml→ PyYAML) - Pin, add and override packages in the Dependencies panel
- Know what publishing locks and how to update it
- Read the four install errors and fix them
Imports are the dependency list
Each language is read its own way. Python imports are read with Python’s own parser at any depth — inside functions, try blocks and conditionals too — and the standard library and your own project scripts are left out. TypeScript and JavaScript imports (import … from, import(), require()) are installed with Bun. C and C++ install nothing: they use the system headers that ship with the worker.
import yaml # installs the PyPI package PyYAML
def main(config: str = "name: nightly\nretries: 3\n"):
data = yaml.safe_load(config)
return {"name": data["name"], "retries": data["retries"]}{
"name": "nightly",
"retries": 3
}The first run of a new set of packages shows the install in Logs; after that the worker reuses its cache and the run starts at once. An import that is not in the code is not installed — there is nothing to declare for packages your code already imports.
Python: from import to package
Many packages are imported by a name that is not the one you install. Waiso keeps a map of those — about 150 entries, the longest matching module path wins — and anything not in the map is tried by its own name.
| You write | Waiso installs |
|---|---|
import yaml | PyYAML |
from PIL import Image | Pillow |
from bs4 import BeautifulSoup | beautifulsoup4 |
import cv2 | opencv-python-headless |
from sklearn import … | scikit-learn |
from dateutil import parser | python-dateutil |
import jwt | PyJWT |
from google.cloud import storage | google-cloud-storage (by rule) |
def main(width: int = 64, height: int = 64):
# An import inside a function is found too: Pillow is installed.
from PIL import Image
img = Image.new("RGB", (width, height), "white")
return {"size": list(img.size), "mode": img.mode}The Dependencies panel
A Python script has a Dependencies tab in the editor’s side panel, with a count of its packages. Every row is one import and the package it installs, wearing a chip: detected (found in the code), declared (saved in the panel), hint (a legacy comment) and, once published, locked. An import the map does not know shows No package with this name and a Declare package button; a run still tries it under its own name.
- Pin a version
Click Change the package for this import on a detected row, type a specifier in Version —
==2.31.0or>=2,<3; empty means the latest — and Save. The row turns declared. - Add a package the code does not import
Add package declares one by name — for a plugin a library loads by itself, or for Extras such as
requests[socks]. - Swap the package behind an import
On a detected row, the same button lets you name another package for that import — for example the full
opencv-pythoninstead of the headless build.
Old comment hints
Older scripts may carry whole-line comments such as # @requests@2.31.0. They still work, with their version — but only that exact form: a comment on its own line, #, @, the package and an optional @version. When the panel finds them it says Packages are declared in code comments and offers Move to the panel, so they are saved with the script instead.
Publishing locks the versions
A draft run resolves the packages — the newest versions your declarations allow — and records what it installed. Publish freezes that resolution as the script’s lock: every package, the ones pulled in by others included, with its exact version. The published version then always installs exactly ==version of each, without resolving again, so schedules, the API and flows keep running what you tested.
To take newer versions, press Update dependencies at the foot of the panel: the next draft run resolves fresh, and the next publish freezes that. Until then the published version keeps its lock.
TypeScript and JavaScript
Every bare package an import names is installed with bun install when the script runs — its latest version, the first time a given code runs on a worker; the same code reuses that install afterwards. Scoped packages (@scope/pkg) work; a subpath (lodash/fp) installs its base package. Relative paths are your own project scripts, and node: and bun: modules are built in.
In the editor, each import line wears a pill with its state — installing, installed or failed — and clicking it opens a version picker. The version you pick is written as a trailing comment, import _ from 'lodash' // @4.17.21, which sets the version the editor installs for types and completions.
When an install fails
A Python run whose packages cannot be installed fails before main starts, with one of four codes. The error names the package — and the import and line that asked for it — and pip’s own lines are in Logs. When the editor’s own install of a package fails, the Problems tab shows it on the import line too.
| Code | Meaning | Fix |
|---|---|---|
dependency_not_found | No package by that name on the index — usually an import whose install name differs | Name the right package for the import in the panel |
dependency_version_not_found | The package exists, but no version matches the specifier, or two requirements conflict | Loosen or fix the version |
dependency_index_unreachable | The worker could not reach the package index | Run again; if it repeats, ask your administrator |
dependency_build_failed | pip found the package but could not build or install it | Pick a version that ships a wheel |
Could not install the package for `import sklern` (tried `sklern`). Declare the package explicitly: open Dependencies and add the package that provides `sklern`
Limits
- Scripts have no network access; only the worker’s package step reaches the index, and an administrator may block or allow-list package names.
- A TypeScript side-effect import with no binding (
import 'pkg') is not installed — import something from it. - The Dependencies panel and the lock are Python only; C and C++ cannot install packages.
- At most 100 declarations per script.