summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/filter.py
blob: 6b98225ab12b4e8c7831459a996bdd0452645aed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#
# Copyright (C) 2022 Garmin Ltd. or its subsidiaries
#
# SPDX-License-Identifier: GPL-2.0-only
#

import json
import builtins

# A select initial set of builtins that are supported in filter expressions
ALLOWED_BUILTINS = (
    "all",
    "any",
    "bin",
    "bool",
    "chr",
    "enumerate",
    "eval",
    "float",
    "format",
    "hex",
    "int",
    "len",
    "map",
    "max",
    "min",
    "oct",
    "ord",
    "pow",
    "str",
    "sum",
)

FILTERS = {k: getattr(builtins, k) for k in ALLOWED_BUILTINS}


def apply_filters(val, expressions, *, default_globals=True, extra_globals={}):
    if default_globals:
        g = FILTERS.copy()
    else:
        g = {}

    g.update(extra_globals)

    # Purposely blank out __builtins__ which prevents users from
    # calling any normal builtin python functions
    g["__builtins__"] = {}

    for e in expressions:
        if not e:
            continue

        # Set val as a local so it can be cleared out while keeping the globals
        l = {"v": val}

        val = eval(e, g, l)

    return val


def eval_expression(val, expression, *, default_globals=True, extra_globals={}):
    return apply_filters(
        val,
        [e.strip() for e in expression.split("\n")],
        default_globals=default_globals,
        extra_globals=extra_globals,
    )


def filter_proc(*, name=None):
    """
    Decorator to mark a function as a filter that can be called in
    `apply_filters`. The `name` argument can be used to specify an alternate
    name for the function if the actual name is not desired.
    """

    def inner(func):
        global FILTERS
        nonlocal name
        if name is None:
            name = func.__name__
        FILTERS[name] = func
        return func

    return inner


@filter_proc()
def suffix(val, suffix):
    return " ".join(v + suffix for v in val.split())


@filter_proc()
def prefix(val, prefix):
    return " ".join(prefix + v for v in val.split())


@filter_proc()
def sort(val):
    return " ".join(sorted(val.split()))


@filter_proc()
def remove(val, remove, sep=None):
    if isinstance(remove, str):
        remove = remove.split(sep)
    new = [i for i in val.split(sep) if not i in remove]

    if not sep:
        return " ".join(new)
    return sep.join(new)


@filter_proc()
def from_json(val):
    return json.loads(val)