From acf4bf7b3d9912f63340c960da89e4368b0823d4 Mon Sep 17 00:00:00 2001 From: Stefano Cossu Date: Tue, 12 Jul 2022 20:31:43 -0700 Subject: [PATCH] Flask and Docker boilerplate. --- .gitignore | 132 +++++++++++++++++++++++++++++++++++++ Dockerfile | 23 +++++++ entrypoint.sh | 19 ++++++ requirements.txt | 3 + transliterator/__init__.py | 11 ++++ transliterator/convert.py | 0 transliterator/rest_api.py | 44 +++++++++++++ uwsgi.ini | 11 ++++ wsgi.py | 5 ++ 9 files changed, 248 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 entrypoint.sh create mode 100644 requirements.txt create mode 100644 transliterator/__init__.py create mode 100644 transliterator/convert.py create mode 100644 transliterator/rest_api.py create mode 100644 uwsgi.ini create mode 100644 wsgi.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..be29689 --- /dev/null +++ b/.gitignore @@ -0,0 +1,132 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +#eclipse +.project +.settings +.pydevproject +.classpath +#checking in Main.py for Stefano to test otmm +gcis/Main.py + +#PyDev +.DS_Store + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +.static_storage/ +.media/ +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# Sphinx API docs. +docs/apidoc/* + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# Pytest +.pytest_cache/ + +# CTags +/tags +tags.lock +tags.temp + +# Local experimental stuff. +/sandbox + +# Backup files. +*.bk diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c65f01b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM python:3.9-alpine3.15 + +RUN apk add --no-cache -t buildtools build-base +RUN apk add --no-cache linux-headers + +ENV _workroot "/usr/local/transliterator/src" + +WORKDIR ${_workroot} +COPY requirements.txt ./ +RUN pip install -r requirements.txt +COPY entrypoint.sh uwsgi.ini wsgi.py ./ +COPY transliterator ./transliterator/ +RUN chmod +x ./entrypoint.sh + +RUN addgroup -S www && adduser -S www -G www +RUN chown -R www:www ${_workroot} . + +# Remove development packages. +RUN apk del buildtools + +EXPOSE 8000 + +ENTRYPOINT ["./entrypoint.sh"] diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..ab666ef --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +export PYTHONPATH=$PYTHONPATH:. +export WEBAPP_PIDFILE="/run/transliterator_webapp.pid" +export FLASK_APP="transliterator.rest_api" +if [ "${TXL_APP_MODE}" == "development" ]; then + export FLASK_ENV="development" +else + export FLASK_ENV="production" +fi + +host=${TXL_WEBAPP_HOST:-"0.0.0.0"} +port=${TXL_WEBAPP_PORT:-"8000"} + +if [ "${FLASK_ENV}" == "development" ]; then + exec flask run -h $host -p $port +else + exec uwsgi --uid www --ini ./uwsgi.ini --http "${host}:${port}" $@ +fi diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..2ee39cb --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +flask +pyyaml +uwsgi diff --git a/transliterator/__init__.py b/transliterator/__init__.py new file mode 100644 index 0000000..3986e8a --- /dev/null +++ b/transliterator/__init__.py @@ -0,0 +1,11 @@ +import logging + +from os import environ, path + + +APP_ROOT = path.dirname(path.realpath(__file__)) + +logging.basicConfig( + # filename=environ.get("TXL_LOGFILE", "/dev/stdout"), + level=environ.get("TXL_LOGLEVEL", logging.INFO)) +logger = logging.getLogger(__name__) diff --git a/transliterator/convert.py b/transliterator/convert.py new file mode 100644 index 0000000..e69de29 diff --git a/transliterator/rest_api.py b/transliterator/rest_api.py new file mode 100644 index 0000000..583a27f --- /dev/null +++ b/transliterator/rest_api.py @@ -0,0 +1,44 @@ +from os import environ + +from flask import Flask, request + + +def create_app(): + app = Flask(__name__) + app.config.update({ + "ENV": environ.get("TXL_APP_MODE", "production"), + "SECRET_KEY": environ["TXL_FLASK_SECRET"], + "USE_X_SENDFILE": True, + "JSON_AS_ASCII": False, + "JSONIFY_PRETTYPRINT_REGULAR": True, + }) + + return app + + +app = create_app() + + +@app.route("/health", methods=["GET"]) +def health_check(): + return "I'm alive!\n" + + +@app.route("/languages", methods=["GET"]) +def list_languages(): + return "TODO list of supported languages goes here." + + +@app.route("/scripts") +@app.route("/scripts/") +def list_scripts(lang=None): + lang_str = f"for {lang}" if lang else "for all languages" + return f"TODO list of supported scripts {lang_str} go here." + + +@app.route("/trans/