deploy-pythonanywhere

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Deploy to PythonAnywhere

部署到PythonAnywhere

Automate Flask deployment to PA free tier via MCP tools. Only one step needs user's hands: install CLI tools in Bash console. Rest is agent-driven.
通过MCP工具将Flask应用自动化部署到PA免费层级。仅需用户手动完成一步:在Bash控制台安装CLI工具。其余步骤均由Agent自动完成。

Pre-flight

部署前准备

Gather before starting:
  1. App directory — local path containing app.py, templates/, etc.
  2. App entry point — the Flask
    app
    import (e.g.
    from app import app
    ).
  3. External CLI tools — any binary the app shells out to (e.g.
    officecli
    ). Agent cannot install these.
  4. Python version — PA system Python (3.10+); no virtualenv needed — flask/openpyxl available in site-packages.
Gate: Items 1-3 confirmed. If app needs CLI tools, tell user to install them in PA Bash console BEFORE agent starts.
开始前请准备好以下内容:
  1. 应用目录 — 包含app.py、templates/等文件的本地路径。
  2. 应用入口 — Flask
    app
    的导入语句(例如
    from app import app
    )。
  3. 外部CLI工具 — 应用调用的任何二进制工具(例如
    officecli
    )。Agent无法安装这些工具。
  4. Python版本 — PA系统自带的Python(3.10+);无需虚拟环境——flask/openpyxl已预装在site-packages中。
检查点: 确认条目1-3已准备完毕。如果应用需要CLI工具,请告知用户在Agent启动前,先在PA的Bash控制台安装这些工具。

User steps (do these first, then hand off to agent)

用户操作步骤(先完成这些,再交由Agent处理)

User opens PA Bash console and runs what applies:
bash
undefined
用户打开PA的Bash控制台,运行对应命令:
bash
undefined

1. pip install extras (if requirements.txt has packages beyond system site-packages)

1. 安装额外依赖(如果requirements.txt包含系统site-packages之外的包)

pip install --user -r /home/<username>/<app-name>/requirements.txt
pip install --user -r /home/<username>/<app-name>/requirements.txt

2. CLI tools (if app shells out to any)

2. 安装CLI工具(如果应用需要调用外部CLI)

curl -fsSL <install-url> | bash

No virtualenv needed — system Python has flask/openpyxl. Only run these if app needs extra packages or CLI tools.

**Gate:** User confirms `pip install` succeeded (if needed) + CLI tool `--version` works. Then agent takes over.
curl -fsSL <install-url> | bash

无需虚拟环境——系统Python已预装flask/openpyxl。仅当应用需要额外包或CLI工具时才运行上述命令。

**检查点:** 用户确认`pip install`执行成功(若需要),且CLI工具的`--version`命令可正常运行。之后由Agent接管后续操作。

Agent steps (fully automated via MCP)

Agent操作步骤(通过MCP完全自动化)

Step 1 — Upload code

步骤1 — 上传代码

python
upload_directory(
    local_dir_path="<local-app-dir>",
    remote_dir_path="/home/<username>/<app-name>"
)
Delete stale cache:
python
delete_path("/home/<username>/<app-name>/__pycache__")
Gate:
tree("/home/<username>/<app-name>")
shows all expected files; no
__pycache__
.
python
upload_directory(
    local_dir_path="<local-app-dir>",
    remote_dir_path="/home/<username>/<app-name>"
)
删除过期缓存:
python
delete_path("/home/<username>/<app-name>/__pycache__")
检查点:
tree("/home/<username>/<app-name>")
显示所有预期文件;无
__pycache__
目录。

Step 2 — Create WSGI entry point

步骤2 — 创建WSGI入口文件

Upload WSGI file via
upload_text_file
:
python
upload_text_file(
    dest_path="/home/<username>/<app-name>/flask_app.py",
    content="""
import os, sys
os.environ["PATH"] = os.path.expanduser("~/.local/bin") + os.pathsep + os.environ.get("PATH", "")
sys.path.insert(0, os.path.expanduser("~/<app-name>"))
from app import app as application
"""
)
Key rules:
  • PATH line mandatory if app uses CLI in
    ~/.local/bin
  • sys.path.insert
    before import
  • as application
    is what uWSGI expects
Gate:
read_file_or_directory
confirms flask_app.py exists.
通过
upload_text_file
上传WSGI文件:
python
upload_text_file(
    dest_path="/home/<username>/<app-name>/flask_app.py",
    content="""
import os, sys
os.environ["PATH"] = os.path.expanduser("~/.local/bin") + os.pathsep + os.environ.get("PATH", "")
sys.path.insert(0, os.path.expanduser("~/<app-name>"))
from app import app as application
"""
)
关键规则:
  • 如果应用使用
    ~/.local/bin
    中的CLI工具,PATH行必须保留
  • 导入语句前需添加
    sys.path.insert
  • uWSGI要求必须使用
    as application
    命名
检查点:
read_file_or_directory
确认flask_app.py已存在。

Step 3 — Create webapp

步骤3 — 创建Web应用

python
create_webapp(
    domain="<username>.pythonanywhere.com",
    python_version="3.12",
    virtualenv_path="",          # empty — use system Python
    project_path="/home/<username>/<app-name>"
)
If webapp already exists, skip to Step 4.
Gate:
get_webapp_info
returns webapp with correct
source_directory
.
python
create_webapp(
    domain="<username>.pythonanywhere.com",
    python_version="3.12",
    virtualenv_path="",          # 留空——使用系统Python
    project_path="/home/<username>/<app-name>"
)
如果Web应用已存在,直接跳至步骤4。
检查点:
get_webapp_info
返回的Web应用信息中
source_directory
正确。

Step 4 — Point WSGI to our file

步骤4 — 将WSGI指向我们的文件

python
patch_webapp(
    domain="<username>.pythonanywhere.com",
    data={"source_directory": "/home/<username>/<app-name>"}
)
Note: PA's WSGI file path is set at creation. If it doesn't match flask_app.py, user must set it in Web tab manually (no API for WSGI path).
Gate:
get_webapp_info
shows correct
source_directory
.
python
patch_webapp(
    domain="<username>.pythonanywhere.com",
    data={"source_directory": "/home/<username>/<app-name>"}
)
注意:PA的WSGI文件路径在创建Web应用时设置。如果该路径与上传的flask_app.py不匹配,用户必须在Web标签页手动修改(暂无API可修改WSGI路径)。
检查点:
get_webapp_info
显示的
source_directory
正确。

Step 5 — Reload

步骤5 — 重载Web应用

python
reload_webapp(domain="<username>.pythonanywhere.com")
Gate:
curl GET https://<user>.pythonanywhere.com/
returns 200.
python
reload_webapp(domain="<username>.pythonanywhere.com")
检查点:
curl GET https://<user>.pythonanywhere.com/
返回状态码200。

Step 6 — E2E test

步骤6 — 端到端测试

  1. Homepage:
    curl GET
    → 200.
  2. Upload real file:
    bash
    curl -s -w "\nHTTP:%{http_code} TIME:%{time_total}" \
      --max-time 300 \
      -X POST -F "file=@<source-file>" \
      "https://<user>.pythonanywhere.com/<endpoint>" \
      -o /tmp/pa_result.xlsx
  3. Verify output: correct row count, correct totals vs known-good reference.
Gate: HTTP 200, output validates. If 400/500, read error log:
python
read_file_or_directory("/var/log/<user>.pythonanywhere.com.error.log")
Diagnose + fix + reload + retest.
  1. 首页测试:
    curl GET
    → 返回状态码200。
  2. 上传真实文件:
    bash
    curl -s -w "\nHTTP:%{http_code} TIME:%{time_total}" \
      --max-time 300 \
      -X POST -F "file=@<source-file>" \
      "https://<user>.pythonanywhere.com/<endpoint>" \
      -o /tmp/pa_result.xlsx
  3. 验证输出:行数正确,总计与已知正确的参考值一致。
检查点: 返回HTTP 200,输出验证通过。如果返回400/500,读取错误日志:
python
read_file_or_directory("/var/log/<user>.pythonanywhere.com.error.log")
排查问题并修复 → 重载Web应用 → 重新测试。

Gotchas (learned the hard way)

注意事项(经验总结)

TemporaryDirectory
race.
uWSGI lazy streaming +
TemporaryDirectory.__exit__
=
OSError: [Errno 39] Directory not empty
. Fix: manual
tempfile.mkdtemp()
+
shutil.rmtree(ignore_errors=True)
in
finally
,
send_file(io.BytesIO(result_bytes))
OUTSIDE try/finally.
Stale
__pycache__
.
Python serves cached
.pyc
after source changes. Every deploy:
delete_path(__pycache__)
+
reload_webapp
. Symptom: old errors persist in error log.
send_file
needs file-like.
Raw
bytes
AttributeError
. Wrap in
io.BytesIO()
.
Free tier sleep. 20-40s cold start after idle. Normal.
No virtualenv needed. PA system Python has flask/openpyxl in site-packages. Don't create one — wastes time.
WSGI path. PA sets this at webapp creation and shows it in Web tab. If it doesn't match the uploaded WSGI file, user must manually update in Web tab config (no API).
TemporaryDirectory
竞争问题。
uWSGI延迟流处理 +
TemporaryDirectory.__exit__
会导致
OSError: [Errno 39] Directory not empty
。修复方案:手动使用
tempfile.mkdtemp()
+ 在
finally
中调用
shutil.rmtree(ignore_errors=True)
,并在try/finally块外使用
send_file(io.BytesIO(result_bytes))
过期的
__pycache__
源代码修改后,Python仍会加载缓存的
.pyc
文件。每次部署时:执行
delete_path(__pycache__)
+
reload_webapp
。症状:错误日志中仍存在旧错误。
send_file
需要类文件对象。
直接传入
bytes
会导致
AttributeError
。需用
io.BytesIO()
包装。
免费层级休眠机制。 闲置后冷启动需要20-40秒,属于正常现象。
无需虚拟环境。 PA系统Python已在site-packages中预装flask/openpyxl。无需创建虚拟环境——纯属浪费时间。
WSGI路径。 PA在创建Web应用时设置该路径,并在Web标签页显示。如果该路径与上传的WSGI文件不匹配,用户必须在Web标签页配置中手动更新(暂无API支持)。

Deploy update (code change only)

部署更新(仅代码变更)

When code changes but webapp already exists:
  1. upload_directory
    new code
  2. delete_path(__pycache__)
  3. reload_webapp
  4. E2E test
Skip Steps 2-4 (webapp + WSGI already configured).
当代码变更但Web应用已存在时:
  1. upload_directory
    上传新代码
  2. delete_path(__pycache__)
    删除缓存
  3. reload_webapp
    重载Web应用
  4. 执行端到端测试
跳过步骤2-4(Web应用和WSGI已配置完成)。