--- name: dspy-patterns description: "Use when writing DSPY. sqlor, Python bools, IN lists." --- # DSPY File Writing Patterns ## Python vs JSON boolean Use Python `True`/`False`, not JSON `true`/`false`: ```python {"autoplay": True} # CORRECT {"autoplay": true} # NameError ``` ## sqlor %% escaping ``` "WHERE x LIKE '%%pattern%%'" → SQL: WHERE x LIKE '%pattern%' ``` ## sqlor IN lists Some sqlor versions fail on `${ids}$` list expansion. Use manual SQL: ```python id_list = ','.join(["'" + str(x) + "'" for x in ids]) sql = "WHERE id IN (" + id_list + ")" ``` ## URL in widgets Use `entire_url()` for full URLs: ```python media_url = entire_url(safe_url(path)) # "/idfile/44/file.mp4" → "https://host/idfile/44/file.mp4" ``` ## Debug silent failures Replace `except Exception: pass` with: ```python except Exception as e: return {"widgettype":"Text","options":{"text":f"ERR:{e}"}} ``` ## f-string 禁止(CRITICAL) DSPY 文件在受限的 `exec()` 命名空间中执行。**f-string 在 dict 字面量中会导致语法错误**: ```python # ❌ SyntaxError: '{' was never closed return {"widgettype": "Text", "options": {"otext": f'算力池: {n} 个'}} # ❌ unittest string literal return {'widgettype': 'Text', 'options': {'otext': f'存储: {total}GB'}} # ✅ 使用字符串拼接 return {"widgettype": "Text", "options": {"otext": '算力池: ' + str(n) + ' 个'}} ``` 错误日志特征:`except=unterminated string literal (detected at line X)` ## 返回 FileResponse 提供文件下载/媒体流 DSPY 可直接返回 aiohttp `FileResponse` 服务任意文件系统路径,因为 `BaseProcessor.handle()` 检查 `isinstance(self.content, StreamResponse)` 直接返回,绕过 JSON 序列化: ```python import os from urllib.parse import quote from aiohttp.web_fileresponse import FileResponse full_path = ws_dir + '/' + file_id # 任意绝对路径,不必在 FileStorage # 路径穿越校验 real_ws = os.path.realpath(ws_dir) real_full = os.path.realpath(full_path) if not real_full.startswith(real_ws + os.sep): return {"widgettype": "Message", "options": {"title": "错误", "message": "非法路径"}} headers = {} if download: filename = os.path.basename(full_path) headers['Content-Disposition'] = 'attachment; filename="%s"; filename*=UTF-8\'\'%s' % (filename, quote(filename)) return FileResponse(full_path, headers=headers) ``` - **无 `download` 参数** → 流式返回,`FileResponse` 自动探测 MIME。`VideoPlayer`/`AudioPlayer`/`Image` 的 `url` 直接指向此 DSPY(`entire_url("/module/api/file.dspy") + "?id=" + quote(rel_path)`)。 - **`download=1`** → `Content-Disposition: attachment` 触发浏览器下载(office/pdf 下载后本地应用打开)。 - 对比 `idfile`(只服务 FileStorage)的优势:可服务任意目录(如项目工作区),不受 `FileStorage.realPath` 的 root 限制。