loading...
Cover image for Hello World From Flask With Hylang (using Docker)

Hello World From Flask With Hylang (using Docker)

sophiabrandt profile image Sophia Brandt Originally published at rockyourcode.com on ・5 min read

(image by @mamihery)

Why Hy?

My second programming language was a Lisp. I fell in love with the expressiveness of Lisps.

But most Lisps are obscure. The ecosystem is not suited for beginners.

One of younger Lisps, Clojure, was my first foray into the world of web-development.

I failed spectacularly because I didn't know how web-develoment worked, and the Clojure ecosystem assumes too much previous knowledge.

Other languages, e.g., Racket, are more approachable, but are still too unpopular to have a big community.

Let's face it, Lisps are no mainstream programming languages.

Python is beginner-friendly. It has a big ecosystem suited for web development, machine-learning and more.

Enter Hy, a Lisp dialect that transforms into the Python abstract syntax tree.

You can use all the goodness of Python, but with a Lisp.

Every Python package can be used with Hy, and vice versa.

Let's create a simple "Hello World" web app with Flask, Hy and Docker.

(Flask is a lightweight web application framework for Python.)

Installation

You'll need Docker.

I also use a Makefile to run the Docker commands, but that's optional.

I'm on a Linux computer. MacOs should work, too. For Windows, I recommend setting up WSL 2.

Create a new folder called hy-flask on your computer.

(All the code is available on GitHub.)

Dockerfile

Inside the hy-flask folder, we'll create a Dockerfile with the following content:

# base image
FROM python:3.8.4-buster

# create user
ARG USER_ID=1000
ENV USER_ID $USER_ID
ARG GROUP_ID=1000
ENV GROUP_ID $GROUP_ID

# add non-root user and give permissions to workdir
RUN groupadd --gid $GROUP_ID user && \
          adduser user --ingroup user --gecos '' --disabled-password --uid $USER_ID && \
          mkdir -p /usr/src/app && \
          chown -R user:user /usr/src/app

# virtualenv
ENV VIRTUAL_ENV=/opt/venv
RUN python3 -m venv $VIRTUAL_ENV
ENV PATH="$VIRTUAL_ENV/bin:$PATH"

# set working directory
WORKDIR /usr/src/app

# install system dependencies
RUN apt-get update && apt-get clean

# add and install requirements
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt

# switch to non-root user
USER user

# disables lag in stdout/stderr output
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1
# Path
ENV PATH="/opt/venv/bin:$PATH"

# run
CMD ["python", "-m", "shim"]

We start from a Debian Python image. Then we create a non-root user.

Next, install the Python packages into an isolated virtual environment.

The Docker container will start a Python program called shim.py. I will come back to that later.

Let's first create a requirements.txt file with all dependencies inside our main folder (hy-flask):

Flask==1.1.2
hy==0.19.0

Our First Flask Application

Here is a minimal "Hello, world!" example with Python:

from flask import Flask
app = Flask(__name__)


@app.route('/')
def hello_world():
    return 'Hello, World!'

if __name__ == '__main__':
    app.run()

Now the same with Hy.

Create a folder called app within your project's directory.
Make a new file called app.hy (not .py).

#!/usr/bin/env hy
(import [flask [Flask]])
(setv app (Flask "__main__"))

(with-decorator (app.route "/")
    (defn index []
        "Hello, world!"))

We import Flask, then we bind the variable app with setv.

with-decorator is used to wrap a function with another, the same as Python.

We also define a function with defn. The function takes no parameters ([]) and returns "Hello, world!".

The app won't run yet.

Our Docker container runs a start script called shim.py. The Python program is a work-around.

Why do we need that?

We want to tell the Flask app to run on host 0.0.0.0. Per default, the Flask server runs on localhost and you cannot see it outside of your network.

Docker creates a separate network which we can forward to outside the container. We need to run the Flask application on an externally visible server.

In Python, this would look like this:

# previous code

if __name__ == '__main__':
    app.run(host=0.0.0.0)

Let's create the shim. Make a new Python file called shim.py inside the app folder with the following content:

import hy
from app import app

if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0")

(This code is from Paul Tagliamonte.)

Build And Run Docker

We can now build the Docker container. Run the following command inside the terminal in the main project folder (hy-flask):

docker build -t hy-flask .

After you've build the container, you can run it:

docker run --rm -it -v "${PWD}/app":/usr/scr/app -p 5000:5000 hy-flask

This runs an interactive (-it) Docker container which will be removed (--rm) after you've stopped it.

The -v flag binds a volume. For development, we mount our local app folder into the container.

Now we don't have to rebuild the container even if we change the code.

We also publish the port 5000. Now we can see the app on our computer.

Go to http://localhost:5000 and admire your Hy Flask application!

Optional:

Create a Makefile with the following content (inside the main folder):

all:
    @echo "Usage: build or run"

build:
    docker build -t hy-flask .

run:
    docker run --rm -it -v "${PWD}/app":/usr/scr/app -p 5000:5000 hy-flask

You can build the container with make build. Run the container with make run.

You can see all the code on GitHub.

Done

That's it! All you need to do to get a Docker container running with Hy and Flask.

The Python shim threw a wrench into the spanner, but other than that, the translation from Python to Hy is straightforward.

Further Reading

Posted on by:

sophiabrandt profile

Sophia Brandt

@sophiabrandt

tax officer who codes in her spare time https://www.rockyourcode.com 🎸🎢

Discussion

markdown guide