I am thinking of trying to create a Python based web app to allow a user to generate and view my spirograph images. Going to take a bit of work, and possibly a hassle finding a suitable place to host the app.

None of my ISP services support Python web apps. I would need to upgrade and I don’t realy feeling like paying more just to do make this available on the web. It is afterall mostly a learning experience for me and possibly a treat for a few family and friends.

I am planning on using Flask. It has much in common with most web frameworks and seems robust enough for what I have in mind. But, I expect I will be facing a definite learning curve and a great deal of refactoring of my current code. I will likely write a new module or two for this particular app. I very much doubt I can directly refactor my current modules to get the web app working.

But for this post, I am going to look at getting things set up on my computer. And, perhaps look at running a simple page on some service. Currently thinking of using Google App Engine. Though PythonAnywhere might be an alternative option.

New Conda Environment

I will start by creating a new Python 3.10 miniconda environment. I am going to stick with Python 3.10, but will see about getting the latest version.

I will, first, update conda.

(base) PS R:\learn\py_play\sp_fa> conda update -n base -c defaults conda
Collecting package metadata (current_repodata.json): done
Solving environment: done

## Package Plan ##

  environment location: E:\appDev\Miniconda3

  added / updated specs:
    - conda


The following packages will be downloaded:

    package                    |            build
    ---------------------------|-----------------
    certifi-2022.9.24          |   py37haa95532_0         154 KB
    cffi-1.15.1                |   py37h2bbff1b_0         217 KB
    colorama-0.4.5             |   py37haa95532_0          28 KB
    conda-22.9.0               |   py37haa95532_0         881 KB
    conda-package-handling-1.9.0|   py37h8cc25b3_0         726 KB
    cryptography-38.0.1        |   py37h21b164f_0         989 KB
    idna-3.4                   |   py37haa95532_0          92 KB
    pip-22.2.2                 |   py37haa95532_0         2.3 MB
    pywin32-302                |   py37h2bbff1b_2         5.6 MB
    requests-2.28.1            |   py37haa95532_0          99 KB
    setuptools-63.4.1          |   py37haa95532_0         1.0 MB
    toolz-0.11.2               |     pyhd3eb1b0_0          49 KB
    tqdm-4.64.1                |   py37haa95532_0         143 KB
    urllib3-1.26.12            |   py37haa95532_0         184 KB
    ------------------------------------------------------------
                                           Total:        12.4 MB

The following NEW packages will be INSTALLED:

  colorama           pkgs/main/win-64::colorama-0.4.5-py37haa95532_0
  toolz              pkgs/main/noarch::toolz-0.11.2-pyhd3eb1b0_0

The following packages will be REMOVED:

  six-1.16.0-pyhd3eb1b0_1

The following packages will be UPDATED:

  ca-certificates                     2021.10.26-haa95532_4 --> 2022.07.19-haa95532_0
  certifi                          2021.10.8-py37haa95532_2 --> 2022.9.24-py37haa95532_0
  cffi                                1.15.0-py37h2bbff1b_1 --> 1.15.1-py37h2bbff1b_0
  conda                               4.11.0-py37haa95532_0 --> 22.9.0-py37haa95532_0
  conda-package-han~                   1.7.3-py37h8cc25b3_1 --> 1.9.0-py37h8cc25b3_0
  cryptography                        36.0.0-py37h21b164f_0 --> 38.0.1-py37h21b164f_0
  idna               pkgs/main/noarch::idna-3.3-pyhd3eb1b0~ --> pkgs/main/win-64::idna-3.4-py37haa95532_0
  openssl                                 1.1.1m-h2bbff1b_0 --> 1.1.1q-h2bbff1b_0
  pip                                 21.2.4-py37haa95532_0 --> 22.2.2-py37haa95532_0
  pywin32                                302-py37h827c3e9_1 --> 302-py37h2bbff1b_2
  requests           pkgs/main/noarch::requests-2.27.1-pyh~ --> pkgs/main/win-64::requests-2.28.1-py37haa95532_0
  setuptools                          58.0.4-py37haa95532_0 --> 63.4.1-py37haa95532_0
  sqlite                                  3.37.2-h2bbff1b_0 --> 3.39.3-h2bbff1b_0
  tqdm               pkgs/main/noarch::tqdm-4.62.3-pyhd3eb~ --> pkgs/main/win-64::tqdm-4.64.1-py37haa95532_0
  urllib3            pkgs/main/noarch::urllib3-1.26.8-pyhd~ --> pkgs/main/win-64::urllib3-1.26.12-py37haa95532_0


Proceed ([y]/n)? y


Downloading and Extracting Packages
setuptools-63.4.1    | 1.0 MB    | ############################################################################ | 100%
urllib3-1.26.12      | 184 KB    | ############################################################################ | 100%
pywin32-302          | 5.6 MB    | ############################################################################ | 100%
conda-package-handli | 726 KB    | ############################################################################ | 100%
toolz-0.11.2         | 49 KB     | ############################################################################ | 100%
colorama-0.4.5       | 28 KB     | ############################################################################ | 100%
cffi-1.15.1          | 217 KB    | ############################################################################ | 100%
conda-22.9.0         | 881 KB    | ############################################################################ | 100%
tqdm-4.64.1          | 143 KB    | ############################################################################ | 100%
certifi-2022.9.24    | 154 KB    | ############################################################################ | 100%
cryptography-38.0.1  | 989 KB    | ############################################################################ | 100%
pip-22.2.2           | 2.3 MB    | ############################################################################ | 100%
requests-2.28.1      | 99 KB     | ############################################################################ | 100%
idna-3.4             | 92 KB     | ############################################################################ | 100%
Preparing transaction: done
Verifying transaction: done
Executing transaction: done

And, the new environment.

(base) PS R:\learn\py_play\sp_fa> conda create -n flk_3.10 python=3.10
Collecting package metadata (current_repodata.json): done
Solving environment: done

## Package Plan ##

  environment location: E:\appDev\Miniconda3\envs\flk_3.10

  added / updated specs:
    - python=3.10


The following packages will be downloaded:

    package                    |            build
    ---------------------------|-----------------
    certifi-2022.9.24          |  py310haa95532_0         155 KB
    pip-22.2.2                 |  py310haa95532_0         2.4 MB
    python-3.10.6              |       hbb2ffb3_1        13.8 MB
    sqlite-3.39.3              |       h2bbff1b_0         804 KB
    tzdata-2022e               |       h04d1e81_0         107 KB
    xz-5.2.6                   |       h8cc25b3_0         240 KB
    zlib-1.2.13                |       h8cc25b3_0         113 KB
    ------------------------------------------------------------
                                           Total:        17.6 MB

The following NEW packages will be INSTALLED:

  bzip2              pkgs/main/win-64::bzip2-1.0.8-he774522_0
  ca-certificates    pkgs/main/win-64::ca-certificates-2022.07.19-haa95532_0
  certifi            pkgs/main/win-64::certifi-2022.9.24-py310haa95532_0
  libffi             pkgs/main/win-64::libffi-3.4.2-hd77b12b_4
  openssl            pkgs/main/win-64::openssl-1.1.1q-h2bbff1b_0
  pip                pkgs/main/win-64::pip-22.2.2-py310haa95532_0
  python             pkgs/main/win-64::python-3.10.6-hbb2ffb3_1
  setuptools         pkgs/main/win-64::setuptools-63.4.1-py310haa95532_0
  sqlite             pkgs/main/win-64::sqlite-3.39.3-h2bbff1b_0
  tk                 pkgs/main/win-64::tk-8.6.12-h2bbff1b_0
  tzdata             pkgs/main/noarch::tzdata-2022e-h04d1e81_0
  vc                 pkgs/main/win-64::vc-14.2-h21ff451_1
  vs2015_runtime     pkgs/main/win-64::vs2015_runtime-14.27.29016-h5e58377_2
  wincertstore       pkgs/main/win-64::wincertstore-0.2-py310haa95532_2
  xz                 pkgs/main/win-64::xz-5.2.6-h8cc25b3_0
  zlib               pkgs/main/win-64::zlib-1.2.13-h8cc25b3_0


Proceed ([y]/n)? y


Downloading and Extracting Packages
python-3.10.6        | 13.8 MB   | ############################################################################ | 100%
sqlite-3.39.3        | 804 KB    | ############################################################################ | 100%
zlib-1.2.13          | 113 KB    | ############################################################################ | 100%
pip-22.2.2           | 2.4 MB    | ############################################################################ | 100%
tzdata-2022e         | 107 KB    | ############################################################################ | 100%
certifi-2022.9.24    | 155 KB    | ############################################################################ | 100%
xz-5.2.6             | 240 KB    | ############################################################################ | 100%
Preparing transaction: done
Verifying transaction: done
Executing transaction: done
#
# To activate this environment, use
#
#     $ conda activate flk_3.10
#
# To deactivate an active environment, use
#
#     $ conda deactivate

(base) PS R:\learn\py_play\sp_fa> conda activate flk_3.10
(flk_3.10) PS R:\learn\py_play\sp_fa>

git

Next, I’ll make sure git is setup and doing its thing. I created a new repository on github, tooOld2Code/flk_app.git

(flk_3.10) PS R:\learn\py_play\sp_fa> git init
Initialized empty Git repository in R:/learn/py_play/sp_fa/.git/

git remote add origin git@github.com:tooOld2Code/flk_app.git

I added a .gitignore and README.md. Then pushed initial commit.

(flk_3.10) PS R:\learn\py_play\sp_fa> git add .gitignore
(flk_3.10) PS R:\learn\py_play\sp_fa> git add README.md
(flk_3.10) PS R:\learn\py_play\sp_fa> git cm "initial repository commit" -m "just .gitignore and README.md"
[master (root-commit) 761dced] initial repository commit
 2 files changed, 40 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 README.md
 git branch -m master main
git push --set-upstream origin main
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 16 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 842 bytes | 421.00 KiB/s, done.
Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:tooOld2Code/flk_app.git
 * [new branch]      main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.

Install Flask and Test

Install

Install is pretty basic. Make sure you have the correct conda environment.

(flk_3.10) PS R:\learn\py_play\sp_fa> conda install flask
Collecting package metadata (current_repodata.json): done
Solving environment: done

## Package Plan ##

  environment location: E:\appDev\Miniconda3\envs\flk_3.10

  added / updated specs:
    - flask


The following packages will be downloaded:

    package                    |            build
    ---------------------------|-----------------
    ca-certificates-2022.10.11 |       haa95532_0         125 KB
    click-8.0.4                |  py310haa95532_0         157 KB
    colorama-0.4.5             |  py310haa95532_0          28 KB
    dataclasses-0.8            |     pyh6d0b6a4_7           8 KB
    flask-2.1.3                |  py310haa95532_0         150 KB
    itsdangerous-2.0.1         |     pyhd3eb1b0_0          18 KB
    jinja2-3.0.3               |     pyhd3eb1b0_0         106 KB
    markupsafe-2.1.1           |  py310h2bbff1b_0          26 KB
    werkzeug-2.0.3             |     pyhd3eb1b0_0         221 KB
    ------------------------------------------------------------
                                           Total:         839 KB

The following NEW packages will be INSTALLED:

  click              pkgs/main/win-64::click-8.0.4-py310haa95532_0 None
  colorama           pkgs/main/win-64::colorama-0.4.5-py310haa95532_0 None
  dataclasses        pkgs/main/noarch::dataclasses-0.8-pyh6d0b6a4_7 None
  flask              pkgs/main/win-64::flask-2.1.3-py310haa95532_0 None
  itsdangerous       pkgs/main/noarch::itsdangerous-2.0.1-pyhd3eb1b0_0 None
  jinja2             pkgs/main/noarch::jinja2-3.0.3-pyhd3eb1b0_0 None
  markupsafe         pkgs/main/win-64::markupsafe-2.1.1-py310h2bbff1b_0 None
  werkzeug           pkgs/main/noarch::werkzeug-2.0.3-pyhd3eb1b0_0 None

The following packages will be UPDATED:

  ca-certificates                     2022.07.19-haa95532_0 --> 2022.10.11-haa95532_0 None


Proceed ([y]/n)? y


Downloading and Extracting Packages
jinja2-3.0.3         | 106 KB    | ############################################################################ | 100%
click-8.0.4          | 157 KB    | ############################################################################ | 100%
markupsafe-2.1.1     | 26 KB     | ############################################################################ | 100%
colorama-0.4.5       | 28 KB     | ############################################################################ | 100%
flask-2.1.3          | 150 KB    | ############################################################################ | 100%
werkzeug-2.0.3       | 221 KB    | ############################################################################ | 100%
dataclasses-0.8      | 8 KB      | ############################################################################ | 100%
itsdangerous-2.0.1   | 18 KB     | ############################################################################ | 100%
ca-certificates-2022 | 125 KB    | ############################################################################ | 100%
Preparing transaction: done
Verifying transaction: done
Executing transaction: done
Retrieving notices: ...working... done

I also decided to install Watchdog. Apparently Flask can use it to improve reloading of the development site.

(flk_3.10) PS R:\learn\py_play\sp_fa> conda install watchdog
Collecting package metadata (current_repodata.json): done
Solving environment: done

## Package Plan ##

  environment location: E:\appDev\Miniconda3\envs\flk_3.10

  added / updated specs:
    - watchdog


The following packages will be downloaded:

    package                    |            build
    ---------------------------|-----------------
    pyyaml-6.0                 |  py310h2bbff1b_0         147 KB
    watchdog-2.1.6             |  py310haa95532_0         113 KB
    yaml-0.2.5                 |       he774522_0          62 KB
    ------------------------------------------------------------
                                           Total:         321 KB

The following NEW packages will be INSTALLED:

  pyyaml             pkgs/main/win-64::pyyaml-6.0-py310h2bbff1b_0 None
  watchdog           pkgs/main/win-64::watchdog-2.1.6-py310haa95532_0 None
  yaml               pkgs/main/win-64::yaml-0.2.5-he774522_0 None


Proceed ([y]/n)? y


Downloading and Extracting Packages
watchdog-2.1.6       | 113 KB    | ############################################################################ | 100%
yaml-0.2.5           | 62 KB     | ############################################################################ | 100%
pyyaml-6.0           | 147 KB    | ############################################################################ | 100%
Preparing transaction: done
Verifying transaction: done
Executing transaction: done
Retrieving notices: ...working... done

Do note: for Flask to use Watchdog, you have to run the Flask development server in debug mode.

Test

A simple test script in app.py. You may recall if we are running app.py, rather than importing it, its __name__ is ‘main’.

from flask import Flask

app = Flask(__name__)

# set route to home/index page
@app.route('/')
def hello():

  return "<h1>Hello World!</h1>" \
         "\nThis is my introduction to Flask!"


if __name__ == '__main__':
  # turn on debug mode so watchdog is used by dev server
  # threaded=True is default
  app.run(debug=True)

Ran python app.py in my conda environment. And, got the following in the browser at localhost:5000\.

screen shot of browser window showing test app output

Additional Files

If we are going to run a Flask app on Google App Engine, and I expect elsewhere, we need a couple additional files.

requirements.txt

flask==2.1.3

app.yaml

runtime: python310

Committed the above additions.

Goggle App Engine

Okay, have decided to give this a try.

Install gcloud CLI

Start by installing the gcloud CLI.

screen shot of gcloud installer window

Installation failed. It apparently doesn’t work with Python environments.

screen shot of gcloud installer window showing cause of failure

So, I installed with the bundled Python. Don’t know what will happen when working in my conda environment, but…

Pretty quick setting up a cloud account. But, took a lot of time trying to start a cloud project. Problem was I was logging in with my telus.net account. And Google assumed I was a Telus employee. Things sorted themselves out when I logged in with a gmail account.

Then I initialized the gcloud CLI.

PS R:\learn\py_play\sp_fa> gcloud init --skip-diagnostics
Welcome! This command will take you through the configuration of gcloud.

Your current configuration has been set to: [default]

You must log in to continue. Would you like to log in (Y/n)?  y

Your browser has been opened to visit:

    https://accounts.google.com/o/oauth2/auth?***

You are logged in as: [***@gmail.com].

Pick cloud project to use:
 [1] ***
 [2] ***
 [3] ***
 [4] ***
 [5] ***
 [6] ***
 [7] Enter a project ID
 [8] Create a new project
Please enter numeric choice or text value (must exactly match list item):  5

Your current project has been set to: [***].

Not setting default zone/region (this feature makes it easier to use
[gcloud compute] by setting an appropriate default value for the
--zone and --region flag).
See https://cloud.google.com/compute/docs/gcloud-compute section on how to set
default compute region and zone manually. If you would like [gcloud init] to be
able to do this for you the next time you run it, make sure the
Compute Engine API is enabled for your project on the
https://console.developers.google.com/apis page.

Created a default .boto configuration file at [C:\Users\***\.boto]. See this file and
[https://cloud.google.com/storage/docs/gsutil/commands/config] for more
information about configuring Google Cloud Storage.
Your Google Cloud SDK is configured and ready to use!

* Commands that require authentication will use barkncats@gmail.com by default
* Commands will reference project `***` by default
Run `gcloud help config` to learn how to change individual settings

This gcloud configuration is called [default]. You can create additional configurations if you work with multiple accounts and/or projects.
Run `gcloud topic configurations` to learn more.

Some things to try next:

* Run `gcloud --help` to see the Cloud Platform services you can interact with. And run `gcloud help COMMAND` to get help on any gcloud command.
* Run `gcloud topic --help` to learn about advanced features of the SDK like arg files and output formatting
* Run `gcloud cheat-sheet` to see a roster of go-to `gcloud` commands.

Of course, when I tried to deploy the app, it failed. No billing account. I had created it under the previous e-mail address. Stupid bloody system. Created new account.

Attempted to deploy app.

PS R:\learn\py_play\sp_fa> gcloud app deploy
Services to deploy:

descriptor:                  [R:\learn\py_play\sp_fa\app.yaml]
source:                      [R:\learn\py_play\sp_fa]
target project:              [***]
target service:              [default]
target version:              [20221027t164614]
target url:                  [https://***.uw.r.appspot.com]
target service account:      [App Engine default service account]


Do you want to continue (Y/n)?  y

Beginning deployment of service [default]...
Created .gcloudignore file. See `gcloud topic gcloudignore` for details.
#============================================================#
#= Uploading 8 files to Google Cloud Storage                =#
#============================================================#
File upload done.
Updating service [default]...failed.
ERROR: (gcloud.app.deploy) Error Response: [7] Access Not Configured. Cloud Build has not been used in project rek-test-366823 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/cloudbuild.googleapis.com/overview?project=*** then retry. If you enabled this API recently, wait a few minutes for the actionP

Will likely have to make use of that .gcloudignore file.

But when I went to the page I got a 502 error message. Took a bit of looking about, but it seems the app engine is expecting a file called main.py not app.py. Likely need to add something to the app.yaml file if I want to use a different name. Will look into that when time permits. So changed the file name and ran another deploy.

PS R:\learn\py_play\sp_fa> gcloud app deploy
Services to deploy:

descriptor:                  [R:\learn\py_play\sp_fa\app.yaml]
source:                      [R:\learn\py_play\sp_fa]
target project:              [***]
target service:              [default]
target version:              [20221027t171207]
target url:                  [https://***.uw.r.appspot.com]
target service account:      [App Engine default service account]


Do you want to continue (Y/n)?  y

Beginning deployment of service [default]...
#============================================================#
#= Uploading 0 files to Google Cloud Storage                =#
#============================================================#
File upload done.
Updating service [default]...done.
Setting traffic split for service [default]...done.
Deployed service [default] to [https://***.uw.r.appspot.com]

You can stream logs from the command line by running:
  $ gcloud app logs tail -s default

To view your application in the web browser run:
  $ gcloud app browse
screen shot of deployed app in browser window

Rendering the output of hello() as text. Let’s see if we can fix that.

Templates

I am going to use a template or two to generate the page. You know, practice, practice, practice…

So need to add a templates directory. I will use a base html template, and then a specific hello.html template.

base.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>{% block title %} {% endblock %} - FlaskApp</title>
</head>
<body>
  {% block content %} {% endblock %}
</body>
</html>

hello.html

{% extends 'base.html' %}

{% block title %}Hello{% endblock %}

{% block content %}
  <h1>Hello from BaRK 'n' Cats!</h1>
  <p>This is my introduction to Flask!</p>
{% endblock %}

And, need to modify main.py. Add another include and change hello() to render a template rather than returning text.

from flask import Flask, render_template


app = Flask(__name__)

# set route to home/index page
@app.route('/')
def hello():
  return render_template('hello.html')


if __name__ == '__main__':
  # turn on debug mode so watchdog is used by dev server
  # threaded=True is default
  app.run(debug=True)

And, on the dev server and on the web app (after a fresh deploy) the text is properly displayed as HTML. And, the source for both dev and production now looks like this.

screen shot of deployed apps source html

Done

Despite some frustrating moments, I am pleased to get that test Flask app working in dev and on Google App Service. Now the real work can begin.

Until next time, enjoy your coding time. But do remember to take some time for family, friends and your health.

Resources