Python packages
List the packages at the top of a Python workflow. They're built once, when you deploy, and every run of that version has them.
What you'll build
repo-stars, a Python workflow that checks its payload with pydantic and reads a GitHub repository's star count with httpx. You deploy it with one call, wait for its packages to build, and run it.
- List the packages
A
# /// scriptblock at the top of the file. - Deploy and wait for the build
A new set of packages builds once, usually within a minute.
- Run it
Start a run with a payload, then read its result.
Write the workflow
dependencies lists package names, each with optional extras and version specifiers, such as pydantic>=2 or httpx[http2]==0.28.*. URLs, paths and environment markers are refused. A payload that doesn't match the pydantic model is refused before a run starts.
# /// script
# dependencies = ["httpx", "pydantic>=2"]
# ///
import httpx
from pydantic import BaseModel
from shiftlabs import workflow
class Repo(BaseModel):
repo: str
@workflow(payload=Repo)
async def repo_stars(ctx):
repo = Repo.model_validate(ctx.payload).repo
async def fetch_stars():
async with httpx.AsyncClient() as http:
r = await http.get(f"https://api.github.com/repos/{repo}")
r.raise_for_status()
return r.json()["stargazers_count"]
stars = await ctx.step.run("fetch stars", fetch_stars)
return {"repo": repo, "stars": stars}
Use async HTTP clients such as httpx's AsyncClient. Requests go through the egress gateway like fetch, so they count toward your outbound requests. A file next to the workflow, such as helpers.py, can be imported too; list packages only in the workflow file's own block.
Deploy and wait
Send the file with content-type: text/x-python. If the same set of packages was built before, the deploy goes live at once and answers with the version. A new set answers 202 with a deployId; poll it until status is live or failed.
# Answers 202 {"status": "building", "deployId", "packages", "poll"}, or,
# for packages built before, {"versionHash", "sizeBytes", "warnings", …}.
curl -X PUT https://api.shiftlabs.org/v1/workflows/repo-stars \
-H "authorization: Bearer $SHIFTLABS_API_KEY" \
-H "content-type: text/x-python" \
--data-binary @repo_stars.py
# Answers {"id", "workflow", "status", "packages", "result", …}: status is
# building, live or failed, and result is the deploy's answer once it ends.
curl https://api.shiftlabs.org/v1/deploys/<deployId> \
-H "authorization: Bearer $SHIFTLABS_API_KEY"
- A version keeps the packages it was deployed with. Deploying the same code with the same
dependenciesreuses the build. To get the newest versions the ranges allow, deploy with?upgrade=1. - Pure-Python packages work as published. A compiled one, such as numpy, needs a build for the Python runtime shiftlabs runs. Without one, the build fails and
resultsays why. - A workflow lists at most 30 packages. A workspace can start 20 new builds in 24 hours and have 5 deploys waiting on builds; past either, the deploy gets
429withRetry-After. See Rate limits. - A failed build is tried again only after 10 minutes, so deploying the same set at once again answers the same failure.
Run it and check it worked
# Answers 202 {"runId", "versionHash"}.
curl -X POST https://api.shiftlabs.org/v1/workflows/repo-stars/runs \
-H "authorization: Bearer $SHIFTLABS_API_KEY" \
-H "content-type: application/json" \
-d '{"repo": "python/cpython"}'
# Answers {"run", "instance"}: run.status is completed once it's done, and
# instance.output is {"repo": "python/cpython", "stars": …}.
curl https://api.shiftlabs.org/v1/runs/<runId> \
-H "authorization: Bearer $SHIFTLABS_API_KEY"
# A payload the model refuses: 400 {"error": "payload does not match the
# schema", "detail"}, and no run.
curl -X POST https://api.shiftlabs.org/v1/workflows/repo-stars/runs \
-H "authorization: Bearer $SHIFTLABS_API_KEY" \
-H "content-type: application/json" \
-d '{"name": "cpython"}'
GitHub allows 60 unauthenticated requests an hour from one IP address. For more, put a token in a secret for api.github.com and send f"Bearer {ctx.secrets.GITHUB_TOKEN}" as the authorization header. The gateway puts the real token in place of the stand-in.
Reference
- Python packages
Package rules, and modules beside the workflow file.
- Files and npm packages
The same for TypeScript, with
package.jsonandpackage-lock.json. - Payload schemas
pydantic models and JSON Schema, and what a refused payload gets.
- Python editor types
The
shiftlabspackage on PyPI, for your editor and tests.