Passed
Push — issue631-rest-api-language-det... ( 34c253...1cd800 )
by Osma
04:27
created

annif.create_app()   B

Complexity

Conditions 5

Size

Total Lines 32
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 19
nop 1
dl 0
loc 32
rs 8.9833
c 0
b 0
f 0
1
#!/usr/bin/env python3
2
3
from __future__ import annotations
4
5
import logging
6
import os
7
import os.path
8
from typing import TYPE_CHECKING
9
10
from flask import Flask
11
12
logging.basicConfig()
13
logger = logging.getLogger("annif")
14
logger.setLevel(level=logging.INFO)
15
16
import annif.backend  # noqa
17
18
if TYPE_CHECKING:
19
    from connexion.apps.flask import FlaskApp
20
21
22
def create_flask_app(config_name: str | None = None) -> Flask:
23
    """Create a Flask app to be used by the CLI."""
24
25
    _set_tensorflow_loglevel()
26
27
    app = Flask(__name__)
28
    config_name = _get_config_name(config_name)
29
    logger.debug(f"creating flask app with configuration {config_name}")
30
    app.config.from_object(config_name)
31
    app.config.from_envvar("ANNIF_SETTINGS", silent=True)
32
    return app
33
34
35
def create_cx_app(config_name: str | None = None) -> FlaskApp:
36
    """Create a Connexion app to be used for the API."""
37
    import connexion
38
    from connexion.datastructures import MediaTypeDict
39
    from connexion.middleware import MiddlewarePosition
40
    from connexion.validators import FormDataValidator, MultiPartFormDataValidator
41
    from starlette.middleware.cors import CORSMiddleware
42
43
    import annif.registry
44
    from annif.openapi.validation import CustomRequestBodyValidator
45
46
    specdir = os.path.join(os.path.dirname(__file__), "openapi")
47
    cxapp = connexion.FlaskApp(__name__, specification_dir=specdir)
48
    config_name = _get_config_name(config_name)
49
    logger.debug(f"creating connexion app with configuration {config_name}")
50
    cxapp.app.config.from_object(config_name)
51
    cxapp.app.config.from_envvar("ANNIF_SETTINGS", silent=True)
52
53
    validator_map = {
54
        "body": MediaTypeDict(
55
            {
56
                "*/*json": CustomRequestBodyValidator,
57
                "application/x-www-form-urlencoded": FormDataValidator,
58
                "multipart/form-data": MultiPartFormDataValidator,
59
            }
60
        ),
61
    }
62
    cxapp.add_api("annif.yaml", validator_map=validator_map)
63
64
    # add CORS support
65
    cxapp.add_middleware(
66
        CORSMiddleware,
67
        position=MiddlewarePosition.BEFORE_EXCEPTION,
68
        allow_origins=["*"],
69
    )
70
71
    if cxapp.app.config["INITIALIZE_PROJECTS"]:
72
        annif.registry.initialize_projects(cxapp.app)
73
        logger.info("finished initializing projects")
74
75
    # register the views via blueprints
76
    from annif.views import bp
77
78
    cxapp.app.register_blueprint(bp)
79
80
    # return the Connexion app
81
    return cxapp
82
83
84
create_app = create_cx_app  # Alias to allow starting directly with uvicorn run
85
86
87
def _get_config_name(config_name: str | None) -> str:
88
    if config_name is None:
89
        config_name = os.environ.get("ANNIF_CONFIG")
90
    if config_name is None:
91
        if os.environ.get("FLASK_RUN_FROM_CLI") == "true":  # pragma: no cover
92
            config_name = "annif.default_config.Config"
93
        else:
94
            config_name = "annif.default_config.ProductionConfig"  # pragma: no cover
95
    return config_name
96
97
98
def _set_tensorflow_loglevel():
99
    """Set TensorFlow log level based on Annif log level (--verbosity/-v
100
    option) using an environment variable. INFO messages by TF are shown only on
101
    DEBUG (or NOTSET) level of Annif."""
102
    annif_loglevel = logger.getEffectiveLevel()
103
    tf_loglevel_mapping = {
104
        0: "0",  # NOTSET
105
        10: "0",  # DEBUG
106
        20: "1",  # INFO
107
        30: "1",  # WARNING
108
        40: "2",  # ERROR
109
        50: "3",  # CRITICAL
110
    }
111
    tf_loglevel = tf_loglevel_mapping[annif_loglevel]
112
    os.environ.setdefault("TF_CPP_MIN_LOG_LEVEL", tf_loglevel)
113