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