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\
.
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.
Installation failed. It apparently doesn’t work with Python environments.
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
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.
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
- Flask: Quickstart
- Google Cloud App Engine documentation
- Writing a Basic Web Service for App Engine
- PythonAnywhere
- Python Web Applications: Deploy Your Script as a Flask App
- How To Use Templates in a Flask Application
- The Art of Routing in Flask
- Flask Tutorial: Routes