Passed
Push — issue784-fix-nn-ensemle-model-... ( 1c04a1...62e26f )
by Juho
06:20
created

annif.util.get_keras_model_metadata()   A

Complexity

Conditions 4

Size

Total Lines 10
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nop 1
dl 0
loc 10
rs 9.95
c 0
b 0
f 0
1
"""Utility functions for Annif"""
2
3
from __future__ import annotations
4
5
import glob
6
import logging
7
import os
8
import os.path
9
import tempfile
10
import zipfile
11
from typing import Any, Callable
12
13
from annif import logger
14
15
16
class DuplicateFilter(logging.Filter):
17
    """Filter out log messages that have already been displayed."""
18
19
    def __init__(self) -> None:
20
        super().__init__()
21
        self.logged = set()
22
23
    def filter(self, record: logging.LogRecord) -> bool:
24
        current_log = hash((record.module, record.levelno, record.msg, record.args))
25
        if current_log not in self.logged:
26
            self.logged.add(current_log)
27
            return True
28
        return False
29
30
31
def atomic_save(
32
    obj: Any, dirname: str, filename: str, method: Callable | None = None
33
) -> None:
34
    """Save the given object (which must have a .save() method, unless the
35
    method parameter is given) into the given directory with the given
36
    filename, using a temporary file and renaming the temporary file to the
37
    final name."""
38
39
    prefix, suffix = os.path.splitext(filename)
40
    prefix = "tmp-" + prefix
41
    tempfd, tempfilename = tempfile.mkstemp(prefix=prefix, suffix=suffix, dir=dirname)
42
    os.close(tempfd)
43
    logger.debug("saving %s to temporary file %s", str(obj)[:90], tempfilename)
44
    if method is not None:
45
        method(obj, tempfilename)
46
    else:
47
        obj.save(tempfilename)
48
    for fn in glob.glob(tempfilename + "*"):
49
        newname = fn.replace(tempfilename, os.path.join(dirname, filename))
50
        logger.debug("renaming temporary file %s to %s", fn, newname)
51
        os.rename(fn, newname)
52
53
54
def cleanup_uri(uri: str) -> str:
55
    """remove angle brackets from a URI, if any"""
56
    if uri.startswith("<") and uri.endswith(">"):
57
        return uri[1:-1]
58
    return uri
59
60
61
def parse_sources(sourcedef: str) -> list[tuple[str, float]]:
62
    """parse a source definition such as 'src1:1.0,src2' into a sequence of
63
    tuples (src_id, weight)"""
64
65
    sources = []
66
    totalweight = 0.0
67
    for srcdef in sourcedef.strip().split(","):
68
        srcval = srcdef.strip().split(":")
69
        src_id = srcval[0]
70
        if len(srcval) > 1:
71
            weight = float(srcval[1])
72
        else:
73
            weight = 1.0
74
        sources.append((src_id, weight))
75
        totalweight += weight
76
    return [(srcid, weight / totalweight) for srcid, weight in sources]
77
78
79
def parse_args(param_string: str) -> tuple[list, dict]:
80
    """Parse a string of comma separated arguments such as '42,43,key=abc' into
81
    a list of positional args [42, 43] and a dict of keyword args {key: abc}"""
82
83
    if not param_string:
84
        return [], {}
85
    posargs = []
86
    kwargs = {}
87
    param_strings = param_string.split(",")
88
    for p_string in param_strings:
89
        parts = p_string.split("=")
90
        if len(parts) == 1:
91
            posargs.append(p_string)
92
        elif len(parts) == 2:
93
            kwargs[parts[0]] = parts[1]
94
    return posargs, kwargs
95
96
97
def boolean(val: Any) -> bool:
98
    """Convert the given value to a boolean True/False value, if it isn't already.
99
    True values are '1', 'yes', 'true', and 'on' (case insensitive), everything
100
    else is False."""
101
102
    return str(val).lower() in ("1", "yes", "true", "on")
103
104
105
def identity(x: Any) -> Any:
106
    """Identity function: return the given argument unchanged"""
107
    return x
108
109
110
def metric_code(metric):
111
    """Convert a human-readable metric name into an alphanumeric string"""
112
    return metric.translate(metric.maketrans(" ", "_", "()"))
113