rewriter: allow setting values regardless of the previous type

Alterations to kwargs come in two flavors:
1. Remove/replace values (delete/set)
2. Modify values (add/remove)

Only for the second flavor do we care about the type of any existing
value.
pull/12908/head
Jouke Witteveen 12 months ago committed by Jussi Pakkanen
parent fb72a8ecdb
commit f013a68898
  1. 188
      mesonbuild/rewriter.py

@ -98,17 +98,23 @@ def _symbol(val: str) -> SymbolNode:
class MTypeBase:
def __init__(self, node: T.Optional[BaseNode] = None):
if node is None:
self.node = self._new_node() # lgtm [py/init-calls-subclass] (node creation does not depend on base class state)
self.node = self.new_node()
else:
self.node = node
self.node_type = None
for i in self.supported_nodes(): # lgtm [py/init-calls-subclass] (listing nodes does not depend on base class state)
for i in self.supported_nodes():
if isinstance(self.node, i):
self.node_type = i
def _new_node(self):
@classmethod
def new_node(cls, value=None):
# Overwrite in derived class
raise RewriterException('Internal error: _new_node of MTypeBase was called')
raise RewriterException('Internal error: new_node of MTypeBase was called')
@classmethod
def supported_nodes(cls):
# Overwrite in derived class
return []
def can_modify(self):
return self.node_type is not None
@ -116,14 +122,6 @@ class MTypeBase:
def get_node(self):
return self.node
def supported_nodes(self):
# Overwrite in derived class
return []
def set_value(self, value):
# Overwrite in derived class
mlog.warning('Cannot set the value of type', mlog.bold(type(self).__name__), '--> skipping')
def add_value(self, value):
# Overwrite in derived class
mlog.warning('Cannot add a value of type', mlog.bold(type(self).__name__), '--> skipping')
@ -140,63 +138,74 @@ class MTypeStr(MTypeBase):
def __init__(self, node: T.Optional[BaseNode] = None):
super().__init__(node)
def _new_node(self):
return StringNode(Token('', '', 0, 0, 0, None, ''))
@classmethod
def new_node(cls, value=None):
if value is None:
value = ''
return StringNode(Token('', '', 0, 0, 0, None, str(value)))
def supported_nodes(self):
@classmethod
def supported_nodes(cls):
return [StringNode]
def set_value(self, value):
self.node.value = str(value)
class MTypeBool(MTypeBase):
def __init__(self, node: T.Optional[BaseNode] = None):
super().__init__(node)
def _new_node(self):
return BooleanNode(Token('', '', 0, 0, 0, None, False))
@classmethod
def new_node(cls, value=None):
return BooleanNode(Token('', '', 0, 0, 0, None, bool(value)))
def supported_nodes(self):
@classmethod
def supported_nodes(cls):
return [BooleanNode]
def set_value(self, value):
self.node.value = bool(value)
class MTypeID(MTypeBase):
def __init__(self, node: T.Optional[BaseNode] = None):
super().__init__(node)
def _new_node(self):
return IdNode(Token('', '', 0, 0, 0, None, ''))
@classmethod
def new_node(cls, value=None):
if value is None:
value = ''
return IdNode(Token('', '', 0, 0, 0, None, str(value)))
def supported_nodes(self):
@classmethod
def supported_nodes(cls):
return [IdNode]
def set_value(self, value):
self.node.value = str(value)
class MTypeList(MTypeBase):
def __init__(self, node: T.Optional[BaseNode] = None):
super().__init__(node)
def _new_node(self):
return ArrayNode(_symbol('['), ArgumentNode(Token('', '', 0, 0, 0, None, '')), _symbol(']'))
def _new_element_node(self, value):
@classmethod
def new_node(cls, value=None):
if value is None:
value = []
elif not isinstance(value, list):
return cls._new_element_node(value)
args = ArgumentNode(Token('', '', 0, 0, 0, None, ''))
args.arguments = [cls._new_element_node(i) for i in value]
return ArrayNode(_symbol('['), args, _symbol(']'))
@classmethod
def _new_element_node(cls, value):
# Overwrite in derived class
raise RewriterException('Internal error: _new_element_node of MTypeList was called')
def _ensure_array_node(self):
if not isinstance(self.node, ArrayNode):
tmp = self.node
self.node = self._new_node()
self.node.args.arguments += [tmp]
self.node = self.new_node()
self.node.args.arguments = [tmp]
def _check_is_equal(self, node, value) -> bool:
@staticmethod
def _check_is_equal(node, value) -> bool:
# Overwrite in derived class
return False
def _check_regex_matches(self, node, regex: str) -> bool:
@staticmethod
def _check_regex_matches(node, regex: str) -> bool:
# Overwrite in derived class
return False
@ -206,20 +215,14 @@ class MTypeList(MTypeBase):
return self.node.args.arguments[0]
return self.node
def supported_element_nodes(self):
@classmethod
def supported_element_nodes(cls):
# Overwrite in derived class
return []
def supported_nodes(self):
return [ArrayNode] + self.supported_element_nodes()
def set_value(self, value):
if not isinstance(value, list):
value = [value]
self._ensure_array_node()
self.node.args.arguments = [] # Remove all current nodes
for i in value:
self.node.args.arguments += [self._new_element_node(i)]
@classmethod
def supported_nodes(cls):
return [ArrayNode] + cls.supported_element_nodes()
def add_value(self, value):
if not isinstance(value, list):
@ -254,40 +257,48 @@ class MTypeStrList(MTypeList):
def __init__(self, node: T.Optional[BaseNode] = None):
super().__init__(node)
def _new_element_node(self, value):
@classmethod
def _new_element_node(cls, value):
return StringNode(Token('', '', 0, 0, 0, None, str(value)))
def _check_is_equal(self, node, value) -> bool:
@staticmethod
def _check_is_equal(node, value) -> bool:
if isinstance(node, BaseStringNode):
return node.value == value
return False
def _check_regex_matches(self, node, regex: str) -> bool:
@staticmethod
def _check_regex_matches(node, regex: str) -> bool:
if isinstance(node, BaseStringNode):
return re.match(regex, node.value) is not None
return False
def supported_element_nodes(self):
@classmethod
def supported_element_nodes(cls):
return [StringNode]
class MTypeIDList(MTypeList):
def __init__(self, node: T.Optional[BaseNode] = None):
super().__init__(node)
def _new_element_node(self, value):
@classmethod
def _new_element_node(cls, value):
return IdNode(Token('', '', 0, 0, 0, None, str(value)))
def _check_is_equal(self, node, value) -> bool:
@staticmethod
def _check_is_equal(node, value) -> bool:
if isinstance(node, IdNode):
return node.value == value
return False
def _check_regex_matches(self, node, regex: str) -> bool:
@staticmethod
def _check_regex_matches(node, regex: str) -> bool:
if isinstance(node, BaseStringNode):
return re.match(regex, node.value) is not None
return False
def supported_element_nodes(self):
@classmethod
def supported_element_nodes(cls):
return [IdNode]
rewriter_keys = {
@ -548,39 +559,42 @@ class Rewriter:
self.handle_error()
continue
# Remove the key from the kwargs
if cmd['operation'] == 'delete':
if key in arg_node.kwargs:
mlog.log(' -- Deleting', mlog.bold(key), 'from the kwargs')
del arg_node.kwargs[key]
num_changed += 1
else:
# Remove the key from the kwargs
if key not in arg_node.kwargs:
mlog.log(' -- Key', mlog.bold(key), 'is already deleted')
continue
continue
mlog.log(' -- Deleting', mlog.bold(key), 'from the kwargs')
del arg_node.kwargs[key]
elif cmd['operation'] == 'set':
# Replace the key from the kwargs
mlog.log(' -- Setting', mlog.bold(key), 'to', mlog.yellow(str(val)))
arg_node.kwargs[key] = kwargs_def[key].new_node(val)
else:
# Modify the value from the kwargs
if key not in arg_node.kwargs:
arg_node.kwargs[key] = None
modifier = kwargs_def[key](arg_node.kwargs[key])
if not modifier.can_modify():
mlog.log(' -- Skipping', mlog.bold(key), 'because it is too complex to modify')
continue
# Apply the operation
val_str = str(val)
if cmd['operation'] == 'add':
mlog.log(' -- Adding', mlog.yellow(val_str), 'to', mlog.bold(key))
modifier.add_value(val)
elif cmd['operation'] == 'remove':
mlog.log(' -- Removing', mlog.yellow(val_str), 'from', mlog.bold(key))
modifier.remove_value(val)
elif cmd['operation'] == 'remove_regex':
mlog.log(' -- Removing all values matching', mlog.yellow(val_str), 'from', mlog.bold(key))
modifier.remove_regex(val)
# Write back the result
arg_node.kwargs[key] = modifier.get_node()
if key not in arg_node.kwargs:
arg_node.kwargs[key] = None
modifier = kwargs_def[key](arg_node.kwargs[key])
if not modifier.can_modify():
mlog.log(' -- Skipping', mlog.bold(key), 'because it is to complex to modify')
# Apply the operation
val_str = str(val)
if cmd['operation'] == 'set':
mlog.log(' -- Setting', mlog.bold(key), 'to', mlog.yellow(val_str))
modifier.set_value(val)
elif cmd['operation'] == 'add':
mlog.log(' -- Adding', mlog.yellow(val_str), 'to', mlog.bold(key))
modifier.add_value(val)
elif cmd['operation'] == 'remove':
mlog.log(' -- Removing', mlog.yellow(val_str), 'from', mlog.bold(key))
modifier.remove_value(val)
elif cmd['operation'] == 'remove_regex':
mlog.log(' -- Removing all values matching', mlog.yellow(val_str), 'from', mlog.bold(key))
modifier.remove_regex(val)
# Write back the result
arg_node.kwargs[key] = modifier.get_node()
num_changed += 1
# Convert the keys back to IdNode's

Loading…
Cancel
Save