xref: /openbmc/openbmc/poky/meta/lib/oe/maketype.py (revision 92b42cb3)
1#
2# Copyright OpenEmbedded Contributors
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6"""OpenEmbedded variable typing support
7
8Types are defined in the metadata by name, using the 'type' flag on a
9variable.  Other flags may be utilized in the construction of the types.  See
10the arguments of the type's factory for details.
11"""
12
13import inspect
14import oe.types as types
15from collections.abc import Callable
16
17available_types = {}
18
19class MissingFlag(TypeError):
20    """A particular flag is required to construct the type, but has not been
21    provided."""
22    def __init__(self, flag, type):
23        self.flag = flag
24        self.type = type
25        TypeError.__init__(self)
26
27    def __str__(self):
28        return "Type '%s' requires flag '%s'" % (self.type, self.flag)
29
30def factory(var_type):
31    """Return the factory for a specified type."""
32    if var_type is None:
33        raise TypeError("No type specified. Valid types: %s" %
34                        ', '.join(available_types))
35    try:
36        return available_types[var_type]
37    except KeyError:
38        raise TypeError("Invalid type '%s':\n  Valid types: %s" %
39                        (var_type, ', '.join(available_types)))
40
41def create(value, var_type, **flags):
42    """Create an object of the specified type, given the specified flags and
43    string value."""
44    obj = factory(var_type)
45    objflags = {}
46    for flag in obj.flags:
47        if flag not in flags:
48            if flag not in obj.optflags:
49                raise MissingFlag(flag, var_type)
50        else:
51            objflags[flag] = flags[flag]
52
53    return obj(value, **objflags)
54
55def get_callable_args(obj):
56    """Grab all but the first argument of the specified callable, returning
57    the list, as well as a list of which of the arguments have default
58    values."""
59    if type(obj) is type:
60        obj = obj.__init__
61
62    sig = inspect.signature(obj)
63    args = list(sig.parameters.keys())
64    defaults = list(s for s in sig.parameters.keys() if sig.parameters[s].default != inspect.Parameter.empty)
65    flaglist = []
66    if args:
67        if len(args) > 1 and args[0] == 'self':
68            args = args[1:]
69        flaglist.extend(args)
70
71    optional = set()
72    if defaults:
73        optional |= set(flaglist[-len(defaults):])
74    return flaglist, optional
75
76def factory_setup(name, obj):
77    """Prepare a factory for use."""
78    args, optional = get_callable_args(obj)
79    extra_args = args[1:]
80    if extra_args:
81        obj.flags, optional = extra_args, optional
82        obj.optflags = set(optional)
83    else:
84        obj.flags = obj.optflags = ()
85
86    if not hasattr(obj, 'name'):
87        obj.name = name
88
89def register(name, factory):
90    """Register a type, given its name and a factory callable.
91
92    Determines the required and optional flags from the factory's
93    arguments."""
94    factory_setup(name, factory)
95    available_types[factory.name] = factory
96
97
98# Register all our included types
99for name in dir(types):
100    if name.startswith('_'):
101        continue
102
103    obj = getattr(types, name)
104    if not isinstance(obj, Callable):
105        continue
106
107    register(name, obj)
108