关于 fcode-python
fcode-python 是面向 Factorial Code 的 Python 3.13 开发指南,供在该平台编写流程的开发者使用。内容涵盖入口约定、平台 API、数据存储及运行注意事项。
主要功能
- 01Python 3.13 流程约定
- 02Factorial Code 上下文与模块 API
- 03数据存储和密钥处理指南
- 04日志与错误处理实践
- 05依赖包、邮件和返回值
适用场景
- 01开始编写 Factorial Code Python 流程
- 02持久化结构化数据或凭据
- 03通过共享日志追踪故障
- 04从流程发送邮件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ factorialco/ factorial-code-skills --skill fcode-python安装后在 Claude Code 中输入/fcode-python确认已加载。
详细介绍
Factorial Code — Python
在 Factorial Code 上运行的 Python 编写指南。运行时为
Python 3.13。关于平台模型(进程、模块、数据存储),请参阅
fcode-core-concepts。
| 方面 | 指南 |
|---|---|
| 运行时 | Python 3.13 |
| 进程入口文件 | main.py |
| 入口点 | def main() |
| 参数 | fcode.context.parameters |
| 变量 | os.getenv("X") 或 fcode.env.X |
| 导入模块 | fcode.import_module("module-slug") |
注意事项
- 将
main()定义为入口点,但绝不要自行调用它 — Factorial Code 会调用它。 fcode.import_module()的名称必须是硬编码的字符串字面量,不能 使用变量:fcode.import_module("shopify-client")✅,fcode.import_module(name)❌。- 绝不要为
fcode.i18n设置别名 — 必须直接调用(fcode.i18n("key")✅,t = fcode.i18n❌):使用别名调用会在运行时抛出 "i18n is disabled"。 请参阅fcode-i18n。 - 数据存储仅保存字符串/数字 — 在
set前对对象使用json.dumps, 在get后使用json.loads。 - 对保存在数据存储中的机密进行加密 — 将
True作为set的第三个 参数传入(fcode.datastore.set(key, token, True));get会自动解密。 普通的set会以明文保存值。 - 使用 snake_case,不要使用 camelCase;遵循 PEP 8;在适当时添加类型提示。
- 将主流程封装在
try/except中,通过fcode-logs(参见“日志记录”)记录 捕获的错误及其上下文,并抛出可采取行动的错误。不要依赖全局变量保存状态 — 应通过参数传递状态,或通过返回值返回状态。 - 绝不要硬编码或记录机密 — 从
os.getenv读取。
进程模板
def main():
parameters = fcode.context.parameters
# Your code here
return { "message": "Success!" }
辅助工具
import os
# Execution / process / schedule metadata
execution_id = fcode.execution.id
process_id = fcode.execution.process.id
schedule_id = fcode.execution.schedule.id # when run from a schedule
timezone = fcode.execution.timezone
# Workspace (team) metadata
team_slug = fcode.team.slug
# Environment variables (secrets/config)
api_key = os.getenv("API_KEY") # or fcode.env.API_KEY
# Import a Factorial Code module (hardcoded name only)
client = fcode.import_module("module-name")
client_v1 = fcode.import_module("module-name", "v1.0.0") # pinned version tag or alias
# Run another process
fcode.processes.run("process-identifier", options)
# Translations (workspace locales — see fcode-i18n)
greeting = fcode.i18n("greetings.hello", {"name": "Ada"}) # %{name} filled in
fcode.i18n("greetings.hello", {"name": "Ada"}, {"locale": "es"}) # another locale, this lookup only (value may be dynamic)
locale = fcode.i18n.locale # the execution's locale
日志记录
通过共享的 fcode-logs 模块记录日志 — 日志级别由工作区继承并统一控制。
该模块读取团队变量 LOG_LEVEL
(debug | info | warn | error,默认值为 info),并将日志转发给 print(
debug/info 输出到 stdout,warn/error 输出到 stderr),因此调用处看起来就像普通的 print:
log = fcode.import_module("fcode-logs") # import once; reuse `log` everywhere, including `except`
log.info("sync started", process_slug) # stdout when LOG_LEVEL ≤ info
log.debug(request_payload) # stdout only when LOG_LEVEL=debug
log.warn("token missing — skipping") # stderr when LOG_LEVEL ≤ warn
log.error("sync failed", str(err)) # stderr — always emitted
记录详细日志 — 日志策略(在 info 级别记录开始/结束、外部调用和决策;
在 debug 级别记录负载;始终在 except 中重新抛出前记录包含上下文的日志)
详见 fcode-core-concepts §通用规则。在本地或开发工作区中设置
LOG_LEVEL=debug,以跟踪完整运行过程;生产环境保持为 info。
绝不要记录机密。每次执行最多保留 500 行日志 / 500 KB — 后续条目
将被丢弃(fcode-core-concepts › 计划与限制)。
依赖项
外部 pip 包会自动安装 — 只需 import 即可。当包名与导入名不同时,使用
@add-package 声明:
# @add-package requests
import requests
父工作区提供的包在此处已安装 — 不要重复声明(参见 fcode-core-concepts)。
数据存储与存储
import json, os
# Datastore (strings/numbers only)
fcode.datastore.set("key", "value")
fcode.datastore.set("key", json.dumps({ "name": "John", "age": 30 }))
value = fcode.datastore.get("key")
fcode.datastore.delete("key")
# Encrypted at rest with a key owned by the workspace — for tokens, credentials,
# personal data. The third argument must be a bool; get() is transparent.
# Keys (entry names) are never encrypted.
fcode.datastore.set("oauth.token", access_token, True)
token = fcode.datastore.get("oauth.token")
# Storage (files)
local_path = os.path.join(os.environ.get("TMP_DATA_DIR"), "localfile.txt")
with open(local_path, "rb") as f:
obj = fcode.storage.upload("path/myfile.txt", f)
objects = fcode.storage.list()
content = fcode.storage.download("path/myfile.txt")
with open(local_path, "wb") as f:
f.write(content)
# Form file params arrive as "fcode.storage://…" references — strip the
# prefix before download; see fcode-forms.
# Signed download URL — { "url", "expiresAt" }. A real HTTPS link in the
# cloud, a file:// URL locally (same shape, no special-casing).
signed = fcode.storage.create_signed_url("path/myfile.txt")
fcode.storage.delete("path/myfile.txt")
本地磁盘: 将临时文件写入 os.environ.get("TMP_DATA_DIR") 下。
变量、计划任务与 OAuth
在运行时读取/写入团队变量并管理进程计划任务 — 仅限你自己的团队,无需 API 令牌(与数据存储/存储类似):
# Team variables (config/secrets)
# Default is SENSITIVE: fcode.variables.set(key, value) creates a sensitive
# (masked, immutable-sensitivity) variable. Pass sensitive=False for plain
# config values.
fcode.variables.set("API_KEY", "secret") # sensitive by default
fcode.variables.set("BASE_URL", "https://api.acme.com", sensitive=False) # required for non-secret config
v = fcode.variables.get("API_KEY") # TeamVariable (has resolving_team_slug) or None
all_vars = fcode.variables.list() # includes variables inherited from parents
fcode.variables.delete("API_KEY") # no-op on an inherited variable
# Schedules (cron or one-off date_time) for a process
schedule = fcode.schedule.create(
"my-process",
cron="0 0 6 * * SUN", # or: date_time="2026-04-24T12:30:00.000"
parameters={"foo": "bar"},
allow_concurrent_executions=False, # optional
)
schedules = fcode.schedule.list(process_id=fcode.execution.process.id)
current = fcode.schedule.get(schedule["id"])
fcode.schedule.update(schedule["id"], cron="0 0 7 * * SUN")
fcode.schedule.pause(schedule["id"])
fcode.schedule.resume(schedule["id"])
fcode.schedule.delete(schedule["id"])
# delete every schedule for a process (pass the process UUID)
fcode.schedule.delete_for_process(fcode.execution.process.id)
# Start a third-party OAuth authorization. The platform holds the state, the PKCE
# verifier and the one redirect_uri registered with the provider, and invokes
# `on_complete` when the provider redirects back -- so never build an authorization
# URL or a state by hand, and never expose a callback webhook.
flow = fcode.oauth.start(
authorize_url="https://login.example.com/authorize",
client_id=fcode.env.PROVIDER_CLIENT_ID,
scope=["openid", "offline_access"],
on_complete="oauth-callback", # the process that receives the code
data={"companyId": company_id}, # carried back untouched; never a secret
extra_params={"nonce": nonce}, # optional, provider-specific
pkce=True, # default
)
flow.authorization_url # hand this to the form's oauth widget
on_complete 会收到 code、codeVerifier、redirectUri、data 和
state,这些值位于 fcode.context.parameters 中(如果提供方拒绝请求,则会收到
error / errorDescription)。在令牌交换中重放 redirectUri — 提供方会逐字节比较。
请参阅 fcode-examples references/oauth-connect.md。
fcode.variables.set/delete 只会在服务端持久化;在同一次运行中,它们不会反映到
fcode.env 中(fcode.env 是启动时获取的快照)。
继承的变量
list()/get() 会包含从父工作区继承的变量(模型见
fcode-core-concepts);继承的变量带有 resolving_team_slug,用于标识
其所有者。对继承的键调用 set() 会在此工作区创建覆盖值——这是
在此处更改该值的唯一方式;对其调用 delete() 则会静默地不执行任何操作,
因此卸载过程绝不会删除父级的凭据(如果必须报告实际删除的内容,请检查
resolving_team_slug)。fcode.env.set_env_var / del_env_var 的行为相同。
发送电子邮件
使用内置的 fcode.send_mail 发送电子邮件——无需配置 SMTP(模型见
fcode-core-concepts):
info = fcode.send_mail(
to="[email protected]", # string or list[str]
subject="Report ready",
text="Plain-text body", # provide text, html, or both
html="<b>HTML body</b>",
)
# info => { "messageId", "accepted", "rejected" }
From地址由平台固定;你传入的from会被忽略。- 默认情况下,每次执行最多可以发送 3 封电子邮件;达到上限后,后续调用会抛出异常。
- 在本地(
fcode run)运行时没有管理器,因此电子邮件会被记录,而不会发送。 - 可接受用途——哪些人可以作为收件人、正文可以包含哪些内容——属于
平台规则;违反规则会阻止发布。请参阅
fcode-core-concepts§发送电子邮件。
返回值
# Standard
return { "message": "Success!" }
# Custom HTTP status (webhooks)
return { "status": 404, "body": { "message": "Not found" }, "headers": { "Content-Type": "application/json" } }
# Transient (not persisted in execution results)
return { "transient": True, "data": sensitive_data }
# Synchronous UI trigger button inside Factorial (see fcode-ui-triggers).
# Returned unchanged to the page; signal a failure with an error, not in the body.
return { "synced": 42 }
返回值上限为 500 KB——结果超出上限会导致执行失败;请将结果存入存储空间,
并返回引用(fcode-core-concepts › 计划与限制)。