blob: d929c8b3e5401926cfc0a0b27569e0f77ba3db25 [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
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080013try:
14 # Python 3.7+
15 from collections.abc import Callable
16except ImportError:
17 # Python < 3.7
18 from collections import Callable
Patrick Williamsc124f4f2015-09-15 14:41:29 -050019
20available_types = {}
21
22class MissingFlag(TypeError):
23 """A particular flag is required to construct the type, but has not been
24 provided."""
25 def __init__(self, flag, type):
26 self.flag = flag
27 self.type = type
28 TypeError.__init__(self)
29
30 def __str__(self):
31 return "Type '%s' requires flag '%s'" % (self.type, self.flag)
32
33def factory(var_type):
34 """Return the factory for a specified type."""
35 if var_type is None:
36 raise TypeError("No type specified. Valid types: %s" %
37 ', '.join(available_types))
38 try:
39 return available_types[var_type]
40 except KeyError:
41 raise TypeError("Invalid type '%s':\n Valid types: %s" %
42 (var_type, ', '.join(available_types)))
43
44def create(value, var_type, **flags):
45 """Create an object of the specified type, given the specified flags and
46 string value."""
47 obj = factory(var_type)
48 objflags = {}
49 for flag in obj.flags:
50 if flag not in flags:
51 if flag not in obj.optflags:
52 raise MissingFlag(flag, var_type)
53 else:
54 objflags[flag] = flags[flag]
55
56 return obj(value, **objflags)
57
58def get_callable_args(obj):
59 """Grab all but the first argument of the specified callable, returning
60 the list, as well as a list of which of the arguments have default
61 values."""
62 if type(obj) is type:
63 obj = obj.__init__
64
Patrick Williamsc0f7c042017-02-23 20:41:17 -060065 sig = inspect.signature(obj)
66 args = list(sig.parameters.keys())
67 defaults = list(s for s in sig.parameters.keys() if sig.parameters[s].default != inspect.Parameter.empty)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050068 flaglist = []
69 if args:
70 if len(args) > 1 and args[0] == 'self':
71 args = args[1:]
72 flaglist.extend(args)
73
74 optional = set()
75 if defaults:
76 optional |= set(flaglist[-len(defaults):])
77 return flaglist, optional
78
79def factory_setup(name, obj):
80 """Prepare a factory for use."""
81 args, optional = get_callable_args(obj)
82 extra_args = args[1:]
83 if extra_args:
84 obj.flags, optional = extra_args, optional
85 obj.optflags = set(optional)
86 else:
87 obj.flags = obj.optflags = ()
88
89 if not hasattr(obj, 'name'):
90 obj.name = name
91
92def register(name, factory):
93 """Register a type, given its name and a factory callable.
94
95 Determines the required and optional flags from the factory's
96 arguments."""
97 factory_setup(name, factory)
98 available_types[factory.name] = factory
99
100
101# Register all our included types
102for name in dir(types):
103 if name.startswith('_'):
104 continue
105
106 obj = getattr(types, name)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800107 if not isinstance(obj, Callable):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500108 continue
109
110 register(name, obj)