Skip to main content

Python Quickstart

In this quick start guide, we will write our first script in Python. Windmill provides a Python 3.11 environment.

Scripts are the basic building blocks in Windmill. They can be run as standalone apps, chained together to create Flows.

Here is a simple example of a script built in Python with Windmill.

In Windmill, Scripts consist of three parts:

  • Metadata: information about the Script such as its path, description, and author, as well as configuration.
  • Logic: the code.
  • UI: a UI autogenerated from the script signature, that can be customized.

For more information about how Scripts and Flows are represented within Windmill, also see the OpenFlow Spec.

In this quick start guide, we'll create a Script that greets the operator running it. From the Home page, click "+Script". This will take you to the first step of script creation: metadata.

New script


  • Path is the Script's unique identifier that consist of the script's owner, and the script's name. The owner can be either a user, or a group (folder). This defines the permissions on Windmill: selecting user will keep the script private to the selected account, while selecting group will make it available to all users of the given group. Let's save this script under your path, and call it hello_world_python.
  • Summary (optional) is a short, human-readable summary of the Script. It will be displayed across Windmill. If omitted, the UI will use the path by default. Let's use "Greet the user by name".
  • Language the language of the script. Windmill supports TypeScript, Python, Go, Bash and SQL. Let's pick Python!
  • Advanced gives you access to more options, such as creating specialized scripts and saving the script as a template. We won't go into this in this quickstart.

Now hit "Next" in the top right corner, and let's build our Hello World!


Windmill gives you an online editor to work on your Scripts. The left-side is the editor itself. The right-side lets you preview the UI that Windmill will generate from the Script's signature - this will be visible to the users of the Script. You can easily preview that UI, provide input values, and test your script there.

Editor for python

As we picked python for this example, Windmill provided some python boilerplate. Let's take a look:

import os
import wmill

# You can import any package from PyPI, even if the assistant complains

Use Cmd/Ctrl + S to autoformat the code. Reset content in the bar to start from a clean template.
The client is used to interact with Windmill itself through its standard API.
One can explore the methods available through autocompletion of `wmill.XXX`.

def main(no_default: str,
name = "Nicolas Bourbaki",
age = 42,
obj: dict = {"even": "dicts"},
l: list = ["or", "lists!"],
file_: bytes = bytes(0)):
"""A main function is required for the script to be able to accept arguments.
Types are recommended."""
print(f"Hello World and a warm welcome especially to {name}")
print("and its acolytes..", age, obj, l, len(file_))
# retrieve variables, including secrets by querying the windmill platform.
# secret fetching is audited by windmill.

secret = wmill.get_variable("f/examples/secret")
secret = "No secret yet at f/examples/secret !"

print(f"The variable at `f/examples/secret`: {secret}")

# Get last state of this script execution by the same trigger/user
last_state = wmill.get_state()
new_state = {"foo": 42} if last_state is None else last_state
new_state["foo"] += 1

# fetch reserved variables as environment variables
user = os.environ.get("WM_USERNAME")
# the return value is then parsed and can be retrieved by other scripts conveniently
return {"splitted": name.split(), "user": user, "state": new_state}

In Windmill, scripts need to have a main function that will be the script's entrypoint. There are a few important things to note about the main.

  • The main arguments are used for generating 1. the input spec of the Script, and 2. the frontend that you see when running the Script as a standalone app.
  • Type annotations are used to generate the UI form, and help pre-validate inputs. While not mandatory, they are highly recommended. You can customize the UI in later steps (but not change the input type!).

Also take a look at the import statement. The import wmill imports the Windmill client, that is needed for example to access variables or resources. We won't go into that here.

Back to our Hello World. We can clear up unused import statements, change the main to take in the user's name. Let's also return the name, maybe we can use this later if we use this Script within a Flow and need to pass it on.

def main(name:str):
print("Hello world. Oh, it's you {}? Greetings!".format(name))
return name

Look at the UI preview on the right: it was updated to match the input signature. Run a preview (Ctrl + Enter) to verify everything works. Now let's go to the last step: the "Advanced" settings.

Advanced settings

In this step, you can:

  • Add a Description to the Script. This is where you can give instructions to users on how to run your Script. It supports markdown!
  • Customize the Script Arguments. The UI is generated from the Script's main function signature, but you can add additional constraints here. For example, we could use the Format add a regex here to make sure users are providing a name with only alphanumeric characters: ^[A-Za-z0-9]*$. Let's still allow numbers in case you're some tech billionaire's kid.

Advanced settings for Python script

We're done! Save your script. Note that Scripts are versioned in Windmill, and each script version is uniquely identified by a hash.


Now let's look at what users of this script will do. Click on the Run button to load this script. You'll see the user input form we defined earlier.

Fill in the input field, then hit "Run". You should see a run view, as well as your logs. All script runs are also available in the Runs menu on the left.

Run hello world in Python

You can also chose to run your script from the CLI with the pre-made Command-Line Interface call.

What's next?

This script is a minimal working example, but there's a few more steps we need in a real-world use case: