File: util.py

package info (click to toggle)
firefox 147.0.2-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,683,484 kB
  • sloc: cpp: 7,607,246; javascript: 6,533,185; ansic: 3,775,227; python: 1,415,393; xml: 634,561; asm: 438,951; java: 186,241; sh: 62,752; makefile: 18,079; objc: 13,092; perl: 12,808; yacc: 4,583; cs: 3,846; pascal: 3,448; lex: 1,720; ruby: 1,003; php: 436; lisp: 258; awk: 247; sql: 66; sed: 54; csh: 10; exp: 6
file content (130 lines) | stat: -rw-r--r-- 4,483 bytes parent folder | download | duplicates (2)
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

"""
Utility functions for the glean_parser-based code generator
"""
import copy
from hashlib import sha1

from glean_parser import util


def generate_ping_ids(objs):
    """
    Return a lookup function for ping IDs per ping name.

    :param objs: A tree of objects as returned from `parser.parse_objects`.
    """

    if "pings" not in objs:

        def no_ping_ids_for_you():
            assert False

        return no_ping_ids_for_you

    # Ping ID 0 is reserved (but unused) right now.
    ping_id = 1

    ping_id_mapping = {}
    for ping_name in objs["pings"].keys():
        ping_id_mapping[ping_name] = ping_id
        ping_id += 1

    return lambda ping_name: ping_id_mapping[ping_name]


def generate_metric_ids(objs, options):
    """
    Return a lookup function for metric IDs per metric object.

    :param objs: A tree of metrics as returned from `parser.parse_objects`.
    """

    # Mapping from a tuple of (category name, metric name) to the metric's numeric ID
    metric_id_mapping = {}

    if options.get("is_local_build"):
        # Metric ID 0 is reserved (but unused) right now.
        metric_ids = {0}

        for category_name, metrics in objs.items():
            if category_name == "tags":
                continue
            for metric in metrics.values():
                metric_id = (
                    int(sha1(str.encode(metric.identifier())).hexdigest(), 16) % 2**25
                )
                # Avoid collisions by incrementing the number until we find an unused id.
                while metric_id in metric_ids:
                    metric_id = (metric_id + 1) % 2**25
                assert metric_id < 2**25
                metric_ids.add(metric_id)
                metric_id_mapping[(category_name, metric.name)] = metric_id
    else:
        # Metric ID 0 is reserved (but unused) right now.
        metric_id = 1

        for category_name, metrics in objs.items():
            for metric in metrics.values():
                metric_id_mapping[(category_name, metric.name)] = metric_id
                metric_id += 1

    return lambda metric: metric_id_mapping[(metric.category, metric.name)]


def get_metrics(objs):
    """
    Returns *just* the metrics in a set of Glean objects
    """
    ret = copy.copy(objs)
    for category in ["pings", "tags"]:
        if ret.get(category):
            del ret[category]
    return ret


def type_ids_and_categories(objs) -> tuple[dict[str, tuple[int, list[str]]], list[str]]:
    """
    Iterates over the metrics in objs, constructing two metadata structures:
     - metric_types: dict[str, tuple[int, list[str]]] - map from a metric
       type (snake_case) to its metric type id and ordered list of arguments.
     - categories: list[str] - category names (snake_case)

    Is stable across invocations: Will generate same ids for same objs.
    (If it doesn't, JOG's factory disagreeing with GleanJSMetricsLookup
    will break the build).
    Uses the same order of metric args set out in glean_parser.util's
    common_metric_args and extra_metric_args.
    (If it didn't, it would supply args in the wrong order to metric type
    constructors with multiple extra args (e.g. custom_distribution)).
    """
    metric_types = set()
    metric_type_ids = {}
    categories = []

    for category_name, objects in get_metrics(objs).items():
        for metric in objects.values():
            if metric.type not in metric_type_ids:
                metric_types.add(metric.type)
    metric_types = sorted(metric_types)

    for category_name, objects in get_metrics(objs).items():
        categories.append(category_name)

        for metric in objects.values():
            if metric.type not in metric_type_ids:
                type_id = (
                    next(i for i, v in enumerate(metric_types) if v == metric.type) + 1
                )
                args = util.common_metric_args.copy()
                for arg_name in util.extra_metric_args:
                    if hasattr(metric, arg_name):
                        args.append(arg_name)
                metric_type_ids[metric.type] = {"id": type_id, "args": args}

    metric_type_ids = dict(sorted(metric_type_ids.items()))
    categories = sorted(categories)
    return (metric_type_ids, categories)