Passed
Pull Request — main (#702)
by Juho
06:19 queued 03:07
created

annif.create_cx_app()   A

Complexity

Conditions 2

Size

Total Lines 41
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 22
nop 1
dl 0
loc 41
rs 9.352
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.middleware import MiddlewarePosition
39
    from starlette.middleware.cors import CORSMiddleware
40
41
    # from flask_cors import CORS  # TODO Use CORSMiddleware
42
    import annif.registry
43
44
    # from annif.openapi.validation import CustomRequestBodyValidator  # TODO Re-enable
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": CustomRequestBodyValidator,
55
    # }
56
    cxapp.add_api("annif.yaml")  # validator_map=validator_map)
57
58
    # add CORS support
59
    cxapp.add_middleware(
60
        CORSMiddleware,
61
        position=MiddlewarePosition.BEFORE_EXCEPTION,
62
        allow_origins=["*"],
63
    )
64
65
    if cxapp.app.config["INITIALIZE_PROJECTS"]:
66
        annif.registry.initialize_projects(cxapp.app)
67
        logger.info("finished initializing projects")
68
69
    # register the views via blueprints
70
    from annif.views import bp
71
72
    cxapp.app.register_blueprint(bp)
73
74
    # return the Connexion app
75
    return cxapp
76
77
78
create_app = create_cx_app  # Alias to allow starting directly with uvicorn run
79
80
81
def _get_config_name(config_name: str | None) -> str:
82
    if config_name is None:
83
        config_name = os.environ.get("ANNIF_CONFIG")
84
    if config_name is None:
85
        if os.environ.get("FLASK_RUN_FROM_CLI") == "true":  # pragma: no cover
86
            config_name = "annif.default_config.Config"
87
        else:
88
            config_name = "annif.default_config.ProductionConfig"  # pragma: no cover
89
    return config_name
90
91
92
def _set_tensorflow_loglevel():
93
    """Set TensorFlow log level based on Annif log level (--verbosity/-v
94
    option) using an environment variable. INFO messages by TF are shown only on
95
    DEBUG (or NOTSET) level of Annif."""
96
    annif_loglevel = logger.getEffectiveLevel()
97
    tf_loglevel_mapping = {
98
        0: "0",  # NOTSET
99
        10: "0",  # DEBUG
100
        20: "1",  # INFO
101
        30: "1",  # WARNING
102
        40: "2",  # ERROR
103
        50: "3",  # CRITICAL
104
    }
105
    tf_loglevel = tf_loglevel_mapping[annif_loglevel]
106
    os.environ.setdefault("TF_CPP_MIN_LOG_LEVEL", tf_loglevel)
107