Button guide

Build a Flask app to run your Python script for colleagues? What it takes, and a ready-made alternative

If you only need a few colleagues to pass arguments and files to your Python script and download the results, don't build a Flask app. Use Button (https://button.bitgate.dev) to turn the script on your own computer into a web form; the Free plan is $0 for 1 published tool, 3 people and 100 runs per month, and Pro is $15/month for up to 50 tools and 250 people. Build the Flask or FastAPI wrapper only when you need a custom UI or you're shipping a public product.

What a minimal Flask wrapper takes

Flask and FastAPI are Python web frameworks, and the first route you write always looks quick. Then you remember a coworker is going to use it without you sitting next to them, which means writing routes and a form, handling uploads and temporary files, running the script safely, moving long jobs into the background instead of running them inside the request, putting a login in front of it, and hosting the app with HTTPS.

At minimum, the handler that calls your script has to save the uploaded file to a temporary folder, call subprocess.run with a list of arguments (never shell=True), and return what the script wrote out:

import subprocess
import tempfile
from pathlib import Path
from flask import Flask, request, send_file, abort
from werkzeug.utils import secure_filename

app = Flask(__name__)

@app.post("/run")
def run_script():
    upload = request.files["input_file"]
    label = request.form["label"]
    if label.startswith("-"):
        abort(400)

    with tempfile.TemporaryDirectory() as tmp:
        in_path = Path(tmp) / secure_filename(upload.filename)
        out_dir = Path(tmp) / "out"
        out_dir.mkdir()
        upload.save(in_path)

        subprocess.run(
            ["python3", "report.py", "--input", str(in_path), "--label", label, "--out", str(out_dir)],
            check=True,
            shell=False,
        )
        return send_file(out_dir / "result.csv", as_attachment=True)

That snippet is only the happy path for a fast script on a trusted network. To put it in front of colleagues, you still have the rest of the work:

  1. Build the form page and error states so people see what failed when the script exits non-zero.
  2. Move the subprocess out of the HTTP request into a background job if a run takes more than a short wait, and build a status page that polls until the output files are ready to download.
  3. Add a login so the form isn't open to anyone who finds the URL, and a way to revoke access when someone leaves.
  4. Put the app on a server with HTTPS, plus all the Python packages and local files your script already had on your own machine.

When to build the Flask or FastAPI app anyway

Write the Flask or FastAPI app when the form itself isn't the whole product. If you need a custom UI - interactive charts, multi-step screens that change based on database queries, a layout that matches the rest of a web product - a form runner won't do that. The same goes if you're building a public product for outside users, or if your machine can't be on and online when people need to run the job.

And if the script is just an internal utility where someone picks a file, enters a couple of values, hits a button and gets a file back? That's where maintaining a web app is dead weight.

Running the same script through Button

Button turns a script on your own computer into a web form your colleagues can run. It works with Python, Node, R, PowerShell, shell scripts and compiled binaries. Jobs run on your computer, never in Button's cloud, while Button's servers relay the form values and files between the browser and your machine.

It also keeps the same subprocess safety properties you'd write by hand. The runner on your computer only executes the exact command you approved on that computer, pinned by hash. Every form value is passed to your program as its own argument with no shell, and a value can't start with -. Fixed flags like --label stay part of the command you approve; colleagues only supply the values for the form fields. If you change a tool, people have to confirm the new version before running it.

Here is how you set a script up:

  1. Update your Python script so it takes its inputs as command-line arguments with argparse instead of hard-coded paths or interactive prompts. Take each input file path as an argument, and write the results as files into an output folder that is also passed as an argument.
  2. Sign in at button.bitgate.dev and install the Button runner on your computer (macOS, Linux or Windows) with the one-line command the site shows you - curl on macOS and Linux, PowerShell on Windows.
  3. Pick your program in the local setup page, describe its inputs (text, number, yes/no, choice, files) and publish. Button gives the tool its own page.
  4. Invite your colleagues. They open the link from their invite email, sign in with Google or a one-time email code, fill in the form, press Run and download the results. They install nothing and never pay.

Every run is logged with who ran it and when, and you can revoke access or set it to expire at any time.

Flask or FastAPI vs. Button

Piece of the job Flask or FastAPI Button
Form and file uploads You write the routes, HTML form and temporary file cleanup Built in: text, number, yes/no, choice and file inputs (up to 8 files and 100 MB per run, 25 MB per file)
Subprocess safety You wire up subprocess with an argument list and shell=False, and check for leading - Command is pinned by hash on your computer; each value is its own argument, no shell, and values can't start with -
Long-running scripts You add background jobs so requests don't time out Up to 30 minutes per run, one run at a time per computer, logs up to 1 MB per run
Login and access control You build authentication and user management Colleagues sign in with Google or a one-time email code; you can revoke access or set an expiry at any time
Hosting and environment You deploy and maintain a server with HTTPS and replicate your local dependencies Runs on your own computer (macOS, Linux or Windows) while it's on and online; 1 computer per workspace
Output file cleanup You write a cleanup job for old uploads and results Inputs deleted 24 hours after a run ends, outputs after 7 days
Cost Server hosting plus your time to build and maintain it Free: $0 for 1 tool, 3 people, 100 runs/month, 1 GB transfer/month. Pro: $15/month ($144/year, or $12/month) for 50 tools, 250 people, 5,000 runs/month, 50 GB transfer/month

FAQ

Where does my Python script actually run with Button?

It runs on your own computer, never in Button's cloud, while Button's servers relay the form values and files between the colleague's browser and your machine. Your computer has to be on and online when someone runs the tool, and the tool page shows Ready, Busy or Offline so colleagues know right away.

How are "people" and "runs" counted on the Free and Pro plans?

"People" counts the distinct colleagues you've invited, and you don't count toward that number. Runs per month (100 on Free, 5,000 on Pro) include your own runs, and the colleagues who run your tools never pay. If you downgrade later, nothing is deleted.

Can a colleague sneak flags or shell commands into my script through the form?

No. The runner only executes the exact command you approved on that computer (pinned by hash), passes every form value as its own argument without a shell, and blocks values that start with -. Fixed flags like --label are part of the command you approve, not something the form user types.

What if I get a new computer or want to use a different language?

Each workspace pairs with one computer at a time on every plan, and pairing a new computer replaces the previous one. You aren't stuck with Python either - any program works, including Node, R (using commandArgs()), PowerShell (using a param() block), shell scripts ($1 and $2) and compiled binaries.

Last updated

Put your script behind a form

Button turns a script on your computer into a web form. Colleagues sign in, fill it in, press Run and download the results. Free for 1 tool and 3 people.