Handle basic tree import
Only update existing properties.
This commit is contained in:
parent
5310496238
commit
036db7b1a7
12
xfconf.py
12
xfconf.py
|
@ -44,6 +44,18 @@ class Xfconf:
|
||||||
return None
|
return None
|
||||||
return XfconfProperty.parse(output)
|
return XfconfProperty.parse(output)
|
||||||
|
|
||||||
|
def set_property(self, channel, prop, prop_type, value):
|
||||||
|
"""Create or update this property."""
|
||||||
|
# TODO handle new properties
|
||||||
|
self.update_property(channel, prop, value)
|
||||||
|
|
||||||
|
def update_property(self, channel, prop, value):
|
||||||
|
"""Update an existing property, return True on success."""
|
||||||
|
if " " in value:
|
||||||
|
value = f'"{value}"'
|
||||||
|
output = self.xqs(f"-c {channel} -p {prop} -s {value}")
|
||||||
|
return output == ""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def find_xq():
|
def find_xq():
|
||||||
xq = shutil.which("xion-query")
|
xq = shutil.which("xion-query")
|
||||||
|
|
96
xion.py
96
xion.py
|
@ -4,37 +4,55 @@ import json
|
||||||
from xfconf import Xfconf
|
from xfconf import Xfconf
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_FILE_PATH = "xion.json"
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
argparser = argparse.ArgumentParser()
|
argparser = argparse.ArgumentParser()
|
||||||
argparser.add_argument("--xq-path", type=str,
|
argparser.add_argument(
|
||||||
help="Optional path to xion-query")
|
"--xq-path", type=str,
|
||||||
argparser.add_argument("-e", "--export", type=str, nargs=2,
|
help="Optional path to xion-query"
|
||||||
metavar=("channel", "root"),
|
)
|
||||||
help="Channel and root to export")
|
argparser.add_argument(
|
||||||
argparser.add_argument("-f", "--file", type=str,
|
"-e", "--export", type=str, nargs=3,
|
||||||
help="JSON file for import/export")
|
metavar=("CHANNEL", "ROOT", "OUTPUT"),
|
||||||
|
help=("Export settings in channel under this root. "
|
||||||
|
"Use '/' as root to export the whole channel.")
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
"-i", "--import", dest="import_tree", type=str,
|
||||||
|
metavar=("JSON",),
|
||||||
|
help="Import a JSON settings file"
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
"-y", "--yes", action="store_true",
|
||||||
|
help="Do not ask for confirmation"
|
||||||
|
)
|
||||||
args = argparser.parse_args()
|
args = argparser.parse_args()
|
||||||
|
|
||||||
xion = Xion(xq=args.xq_path)
|
xion = Xion(xq=args.xq_path)
|
||||||
if args.export:
|
if args.export:
|
||||||
channel, root = args.export
|
channel, root, output = args.export
|
||||||
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
|
return
|
||||||
if args.file:
|
xion.export_tree(channel, root, tree, output)
|
||||||
output_path = args.file
|
elif args.import_tree:
|
||||||
else:
|
channel, root, tree = xion.import_tree(args.import_tree)
|
||||||
print(f"No output file, using {DEFAULT_FILE_PATH}.")
|
if channel and root and tree:
|
||||||
output_path = DEFAULT_FILE_PATH
|
force = bool(args.yes)
|
||||||
xion.export_tree(tree, output_path)
|
xion.apply_tree(channel, root, tree, confirm=not force)
|
||||||
|
|
||||||
|
|
||||||
class Xion:
|
class Xion:
|
||||||
|
|
||||||
|
# GTypes to xfconf-query types along with a value string parser.
|
||||||
|
TYPE_MAP = {
|
||||||
|
"gboolean": "bool",
|
||||||
|
"gint": "int",
|
||||||
|
"guint": "uint",
|
||||||
|
"gdouble": "double",
|
||||||
|
"gchararray": "string",
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, xq=None):
|
def __init__(self, xq=None):
|
||||||
self.xfconf = Xfconf(xq=xq)
|
self.xfconf = Xfconf(xq=xq)
|
||||||
|
|
||||||
|
@ -43,6 +61,9 @@ class Xion:
|
||||||
|
|
||||||
Return None on error.
|
Return None on error.
|
||||||
"""
|
"""
|
||||||
|
if not root.startswith("/"):
|
||||||
|
print("Invalid root, must start with /")
|
||||||
|
return None
|
||||||
props = self.xfconf.get_property_list(channel, root=root)
|
props = self.xfconf.get_property_list(channel, root=root)
|
||||||
if props is None:
|
if props is None:
|
||||||
print(f"Failed to get property list for channel {channel}.")
|
print(f"Failed to get property list for channel {channel}.")
|
||||||
|
@ -60,11 +81,50 @@ class Xion:
|
||||||
tree[prop_name] = leaf
|
tree[prop_name] = leaf
|
||||||
return tree
|
return tree
|
||||||
|
|
||||||
def export_tree(self, tree, output_path):
|
def export_tree(self, channel, root, tree, output_path):
|
||||||
"""Export a config tree as a sorted JSON file."""
|
"""Export a config tree as a sorted JSON file."""
|
||||||
|
tree["channel"] = channel
|
||||||
|
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):
|
||||||
|
"""Load a config tree."""
|
||||||
|
with open(file_path, "rt") as input_file:
|
||||||
|
tree = json.load(input_file)
|
||||||
|
try:
|
||||||
|
channel = tree.pop("channel")
|
||||||
|
root = tree.pop("root")
|
||||||
|
except KeyError:
|
||||||
|
print("Missing channel or root in JSON.")
|
||||||
|
return None, None, tree
|
||||||
|
return channel, root, tree
|
||||||
|
|
||||||
|
def apply_tree(self, channel, root, tree, confirm=True, replace=False):
|
||||||
|
"""Apply tree settings under root to channel."""
|
||||||
|
num_changes = len(tree)
|
||||||
|
print(f"Applying {num_changes} changes to {channel} under {root}.")
|
||||||
|
if replace:
|
||||||
|
print("This will erase all settings in the channel.")
|
||||||
|
if confirm and input("Confirm? [y/N]") != "y":
|
||||||
|
print("Operation cancelled.")
|
||||||
|
return
|
||||||
|
for prop, content in tree.items():
|
||||||
|
self.apply_property(channel, prop, content)
|
||||||
|
|
||||||
|
def apply_property(self, channel, name, content):
|
||||||
|
"""Update one property in Xfconf, return True on success."""
|
||||||
|
# if isinstance(content, list):
|
||||||
|
# for subprop in content:
|
||||||
|
# if not self.apply_property(channel,
|
||||||
|
prop_type = content["type"]
|
||||||
|
if not prop_type in Xion.TYPE_MAP:
|
||||||
|
print(f"Unknown property type {prop_type}!")
|
||||||
|
return False
|
||||||
|
xq_type = Xion.TYPE_MAP[prop_type]
|
||||||
|
value = content["value"]
|
||||||
|
self.xfconf.set_property(channel, name, xq_type, value)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _build_prop_leaf(prop):
|
def _build_prop_leaf(prop):
|
||||||
return {"type": prop.gtype, "value": str(prop.value)}
|
return {"type": prop.gtype, "value": str(prop.value)}
|
||||||
|
|
Loading…
Reference in a new issue