Browse Source

Flask and Docker boilerplate.

Stefano Cossu 1 year ago
parent
commit
acf4bf7b3d
9 changed files with 248 additions and 0 deletions
  1. 132 0
      .gitignore
  2. 23 0
      Dockerfile
  3. 19 0
      entrypoint.sh
  4. 3 0
      requirements.txt
  5. 11 0
      transliterator/__init__.py
  6. 0 0
      transliterator/convert.py
  7. 44 0
      transliterator/rest_api.py
  8. 11 0
      uwsgi.ini
  9. 5 0
      wsgi.py

+ 132 - 0
.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

+ 23 - 0
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"]

+ 19 - 0
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

+ 3 - 0
requirements.txt

@@ -0,0 +1,3 @@
+flask
+pyyaml
+uwsgi

+ 11 - 0
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__)

+ 0 - 0
transliterator/convert.py


+ 44 - 0
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/<lang>")
+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/<script>/<lang>/<dir>", methods=["POST"])
+def transliterate(script, lang, dir):
+    in_txt = request.form["text"]
+    return (
+            f"TODO transliterate text {in_txt}, language {lang}, "
+            f"script {script}, direction {dir}")

+ 11 - 0
uwsgi.ini

@@ -0,0 +1,11 @@
+[uwsgi]
+manage-script-name = true
+wsgi-file = ./wsgi.py
+callable = app
+master = true
+thunder-lock = true
+logger = default file:/dev/stdout
+logger = errorlog file:/dev/stderr
+log-route = errorlog (HTTP/1.\d 50)
+uid = www
+gid = www

+ 5 - 0
wsgi.py

@@ -0,0 +1,5 @@
+from transliterator.rest_api import app
+
+
+if __name__ == "__main__":
+    app.run(host="0.0.0.0")