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

annif.util.DuplicateFilter.filter()   A

Complexity

Conditions 2

Size

Total Lines 6
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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