Handle clean replacement of whole roots

This commit is contained in:
dece 2020-09-27 17:28:59 +02:00
parent 07e44b035d
commit 8cf32a44c2
2 changed files with 43 additions and 13 deletions

View file

@ -80,6 +80,11 @@ class Xfconf:
output = self.xq(["-c", channel, "-p", prop, "-s", value]) output = self.xq(["-c", channel, "-p", prop, "-s", value])
return output == "" return output == ""
def reset_root(self, channel, root):
"""Reset all channel properties under root, return True on success."""
output = self.xqs(f"-c {channel} -p {root} -r -R")
return output == ""
@staticmethod @staticmethod
def find_xq(): def find_xq():
xq = shutil.which("xion-query") xq = shutil.which("xion-query")

49
xion.py
View file

@ -21,6 +21,10 @@ def main():
metavar=("JSON",), metavar=("JSON",),
help="Import a JSON settings file" help="Import a JSON settings file"
) )
argparser.add_argument(
"-r", "--replace", action="store_true",
help="Replace the root with imported settings, remove unknowns"
)
argparser.add_argument( argparser.add_argument(
"-y", "--yes", action="store_true", "-y", "--yes", action="store_true",
help="Do not ask for confirmation" help="Do not ask for confirmation"
@ -33,16 +37,25 @@ def main():
tree = xion.build_tree(channel, root) tree = xion.build_tree(channel, root)
if tree is None: if tree is None:
print("Failed to build config tree.") print("Failed to build config tree.")
return exit(1)
xion.export_tree(channel, root, tree, output) success = xion.export_tree(channel, root, tree, output)
exit(0 if success else 1)
elif args.import_tree: elif args.import_tree:
channel, root, tree = xion.import_tree(args.import_tree) channel, root, tree = xion.import_tree(args.import_tree)
if channel and root and tree: if channel and root and tree:
force = bool(args.yes) force = bool(args.yes)
xion.apply_tree(channel, root, tree, confirm=not force) replace = bool(args.replace)
success = xion.apply_tree(channel, root, tree,
confirm=not force, replace=replace)
exit(0 if success else 1)
else:
print("Import failed.")
exit(1)
exit(0)
class Xion: class Xion:
"""Manipulate Xfconf settings trees."""
# GTypes to xfconf-query types along with a value string parser. # GTypes to xfconf-query types along with a value string parser.
TYPE_MAP = { TYPE_MAP = {
@ -57,9 +70,10 @@ class Xion:
self.xfconf = Xfconf(xq=xq) self.xfconf = Xfconf(xq=xq)
def build_tree(self, channel, root="/"): def build_tree(self, channel, root="/"):
"""Return a dict of configs in this channel, filtering on root. """Return a dict of properties in this channel, filtering on root.
Return None on error. Return None on error. Root has to start with "/" to be valid. Arrays are
added to the tree as a list of properties.
""" """
if not root.startswith("/"): if not root.startswith("/"):
print("Invalid root, must start with /") print("Invalid root, must start with /")
@ -82,14 +96,14 @@ class Xion:
return tree return tree
def export_tree(self, channel, root, tree, output_path): def export_tree(self, channel, root, tree, output_path):
"""Export a config tree as a sorted JSON file.""" """Export a property tree as a sorted JSON file."""
tree["channel"] = channel tree["channel"] = channel
tree["root"] = root tree["root"] = root
with open(output_path, "wt") as output_file: with open(output_path, "wt") as output_file:
json.dump(tree, output_file, indent=2, sort_keys=True) json.dump(tree, output_file, indent=2, sort_keys=True)
def import_tree(self, file_path): def import_tree(self, file_path):
"""Load a config tree.""" """Load a property tree."""
with open(file_path, "rt") as input_file: with open(file_path, "rt") as input_file:
tree = json.load(input_file) tree = json.load(input_file)
try: try:
@ -101,16 +115,27 @@ class Xion:
return channel, root, tree return channel, root, tree
def apply_tree(self, channel, root, tree, confirm=True, replace=False): def apply_tree(self, channel, root, tree, confirm=True, replace=False):
"""Apply tree settings under root to channel.""" """Apply tree settings under root to channel, return True on success."""
num_changes = len(tree) num_changes = len(tree)
print(f"Applying {num_changes} changes to {channel} under {root}.") print(f"{num_changes} changes to do in {channel} for {root}.")
if replace: if replace:
print("This will erase all settings in the channel.") print("This will erase all properties in the channel.")
if confirm and input("Confirm? [y/N]") != "y": if confirm and input("Confirm? [y/N]") != "y":
print("Operation cancelled.") print("Operation cancelled.")
return return False
if replace and not self.clear_tree(channel, root):
print("Failed to clear properties.")
return False
for prop, content in tree.items(): for prop, content in tree.items():
self.apply_property(channel, prop, content) if not self.apply_property(channel, prop, content):
print(f"Failed to apply property {prop}.")
return False
print("Done.")
return True
def clear_tree(self, channel, root):
"""Remove all channel configs under root, return True on success."""
return self.xfconf.reset_root(channel, root)
def apply_property(self, channel, name, content): def apply_property(self, channel, name, content):
"""Update one property in Xfconf, return True on success.""" """Update one property in Xfconf, return True on success."""