360 Secure SkillHub
360 安全技能中心

rez-python-api

面向 Python 嵌入 Rez 的写法指引(Rez 3.4.0):用请求列表构造 ResolvedContext,读 success / resolved_packages / get_environ();支持 .rxt 序列化(save/load、buffer、dict)、Package/Variant 查询、config 与 RezError 异常,并可在上下文内执行命令。适合在脚本或服务中程序化解析环境并复用解析结果。

loonghao
loonghao
热度 13.56
最近更新 2026-10-02 04:56:48
分类:开发依赖:代码执行 / 读写文件应用场景:代码生成 / API 集成 / 调试排错 / DevOps ...

安装技能

复制下面的话术发给你的 Agent,它会通过 secure-skills 检索并安装该技能。

执行命令安装此 skill: npx secure-skills-cli add rez-python-api
Metadata
name
rez-python-api
description
Rez Python API — ResolvedContext construction and inspection, Package/Variant objects, programmatic resolves, .rxt context serialisation and deserialisation, running commands inside a context, and the module entry points (rez.resolved_context, rez.packages, rez.config, rez.pip, rez.suite, rez.bundle_context). Use when the user wants to drive rez from Python instead of the CLI, or asks what a context object actually holds. Covers Rez 3.4.0.

Rez Python API

One-sentence summary: ResolvedContext is the whole product — build one from a list of request strings, read the result off success, resolved_packages and get_environ(), persist it with save() / load(), and only open a shell through execute_shell() when you really need one.

Skill scope: embedding rez in Python. For the CLI equivalents see rez-cli; for solver internals see rez-resolve; for a context used as a deliverable artifact see rez-suites-bundles.

Before you embed rez

Two warnings straight from upstream docs/source/api.rst, and both change the design of a tool:

  • There are no compatibility guarantees between rez versions. Pin the rez you import, the same way the CLI lanes in this repository pin REZ_VERSION.
  • Config is read once, at first import. The Python API cannot reread configuration files or switch to a different REZ_CONFIG_FILE afterwards. A tool that has to change config mid-run must shell out to the CLI instead.

Confirm what you are actually about to import before you trust any of the code below:

rez-status

Module entry points

Module What it holds
rez.resolved_context ResolvedContext — a resolve, and everything you do with one
rez.packages Package, Variant, iter_packages, get_package, get_variant, get_latest_package_from_string
rez.config config — the merged, live settings object
rez.resolver, rez.solver Resolver, ResolverStatus, the solver itself
rez.suite Suite — see rez-suites-bundles
rez.bundle_context bundle_context — see rez-suites-bundles
rez.pip pip_install_package — see rez-pip-integration
rez.developer_package build a package from source in-process
rez.package_repository, rez.package_resources repositories and the resources they hand out
rez.version Version, VersionRange, Requirement, RequirementList, VersionError
rez.exceptions every rez error type; all of them derive from RezError
rez.wrapper the module behind the main console script

Constructing a context

from rez.resolved_context import ResolvedContext

# Requests are plain strings, exactly what you would pass to rez-env.
context = ResolvedContext(["foo-1", "bar"])

if not context.success:
    raise SystemExit(context.failure_description)

ResolvedContext(requests, ...) takes the request list positionally. The arguments worth knowing:

Argument Meaning
verbosity 0, 1 or 2 — the same levels rez-env -v gives you
timestamp epoch time; ignore packages released after it
building / testing resolve as if for rez-build / rez-test
caching None defers to the resolve_caching setting; False bypasses caches
package_paths override packages_path for this resolve only
package_filter a PackageFilterList; rez.package_filter.no_filter removes filtering
add_implicit_packages append the implicit_packages (default True)
max_fails / time_limit stop after N failures / after N seconds

Set verbosity=1 before reaching for a debugger: the solver trace is the same one rez-env -v prints.

Reading the result

context.status                    # ResolverStatus.solved | .failed | .aborted | .pending
context.success                   # True when status is solved
context.failure_description       # why it failed, when it did
context.requested_packages()      # [Requirement, ...] — the request; implicits only if include_implicit=True
context.resolved_packages         # [Variant, ...] — what the solver picked
context.resolved_ephemerals       # resolved ephemeral (implicit-ish) packages
context.get_resolve_as_exact_requests()   # ['foo==1.1.0', ...] — pin the resolve down
context.get_environ()             # dict: the environment the context would produce
context.get_tools()               # {tool_name: (Variant, [alias, ...])}
context.which("foo")              # absolute path to a tool, or None
context.get_resolved_package("foo")       # the Variant, or None
context.print_info(source_order=True)     # the rez-context --so view, on stdout

resolved_packages yields Variant objects, not Package objects. A Package is a family; a Variant is one resolved member of it, with .name, .version, .qualified_name, .root, .requires, .tools and .data on it. Reach for get_resolved_package(name) when you want one package out of a resolve, and get_environ() when you want the environment without running anything.

Context files (.rxt)

A context serialises to a .rxt file, which is how a resolve becomes something you can hand to another process, another machine, or another day:

context.save("context.rxt")                # write it
same = ResolvedContext.load("context.rxt") # read it back

# Or keep it in memory:
from io import StringIO
buf = StringIO()
context.write_to_buffer(buf)
buf.seek(0)
again = ResolvedContext.read_from_buffer(buf)

# Or move it through your own dict/JSON pipeline:
data = context.to_dict()
rebuilt = ResolvedContext.from_dict(data)

save() / load() and write_to_buffer() / read_from_buffer() round-trip the resolve, not the environment — loading is cheap, and no package payload is re-read. to_dict() carries the metadata (status, package_requests, resolved_packages, failure_description, serialize_version, rez_version, ...) rather than a stable public schema; treat it as rez's own format, not as an API you own.

validate() re-checks the context and raises ResolvedContextError when it no longer holds; call it before trusting a context that has been sitting on disk.

Running things inside a context

# One command, non-interactive, with the context's environment:
proc = context.execute_command(["foo", "--version"])
print(proc.returncode)

# The environment as shell source, without starting a shell:
code = context.get_shell_code()

# An interactive shell — blocks, so only do this in a real terminal:
context.execute_shell(block=True)

Prefer execute_command() and get_environ() in automation. execute_shell() inherits the terminal, so it is the wrong call in a service or a test; get_shell_code() takes a style of OutputStyle.file (writable into a script) or OutputStyle.eval (sourceable in one shot).

Packages and variants

from rez.packages import iter_packages, get_package, get_latest_package_from_string

for package in iter_packages("foo"):          # every version of one family
    print(package.version)

package = get_package("foo", "1.1.0")         # one exact package, or None
latest = get_latest_package_from_string("foo")

get_latest_package_from_string accepts the same spellings a request does, so it is also the cheapest way to turn a user-supplied string into a concrete package. iter_packages walks the search path; both take paths= when you need to point them at a specific repository.

Configuration

from rez.config import config

config.packages_path
config.local_packages_path

config is the merged settings object, so an attribute read is the answer to "what took effect", not "what is in the file". It is read at import — see the warning at the top.

Errors

Every rez exception derives from RezError (rez.exceptions), so one except RezError catches the library. The ones you are most likely to handle:

Exception Raised by
ResolveError a resolve that cannot be performed
PackageNotFoundError / PackageFamilyNotFoundError a package that is not there
PackageRequestError a malformed request string
ResolvedContextError validate() on a context that no longer holds
SuiteError suite operations — see rez-suites-bundles
ContextBundleError bundling — see rez-suites-bundles

Cross-check against the CLI

When an API result looks wrong, reproduce it with the CLI before you debug your own code — the CLI is the reference implementation and it reads the config fresh:

rez-env foo -o context.rxt --no-cache   # ResolvedContext(..., caching=False)
rez-context context.rxt --so            # context.print_info(source_order=True)

rez-context --so prints exactly the requested packages / resolved packages split that context.print_info(source_order=True) prints, and -v on rez-env is the same trace you get from verbosity=1.

Gotchas

Symptom Cause
Config changes ignored rez was already imported; the API cannot reread config. Use the CLI
resolved_packages order surprises you it is solver order; only print_info() sorts, and source_order=True skips that sort
Attribute missing after an upgrade no cross-version API guarantees — pin rez
A resolve differs from rez-env compare package_paths, add_implicit_packages and caching
execute_shell() hangs in CI it wants a terminal; use execute_command()

Agent workflow

  1. Import rez once, at the top of the process; never rely on rereading config afterwards.
  2. Build one ResolvedContext, then read success and failure_description before anything else.
  3. Inspect with resolved_packages / get_environ() / get_tools() instead of opening a shell.
  4. Persist with save() when another process needs the resolve; validate() it before trusting it.
  5. When the API and the CLI disagree, believe the CLI and diff the arguments above.