maketype.py 3.1 KB

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