blob: 7a83bdf602c32dd1e90d74390dfab82ca39782a4 [file] [log] [blame]
Brad Bishopc342db32019-05-15 21:57:59 -04001#
Patrick Williams92b42cb2022-09-03 06:53:57 -05002# Copyright OpenEmbedded Contributors
3#
Brad Bishopc342db32019-05-15 21:57:59 -04004# SPDX-License-Identifier: GPL-2.0-only
5#
Patrick Williamsc124f4f2015-09-15 14:41:29 -05006"""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
Patrick Williamsc0f7c042017-02-23 20:41:17 -060014import oe.types as types
Andrew Geissler5199d832021-09-24 16:47:35 -050015from collections.abc import Callable
Patrick Williamsc124f4f2015-09-15 14:41:29 -050016
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
Patrick Williamsc0f7c042017-02-23 20:41:17 -060062 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)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050065 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)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800104 if not isinstance(obj, Callable):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500105 continue
106
107 register(name, obj)