Handle array properties
This commit is contained in:
parent
8cf32a44c2
commit
099672e43c
65
xfconf.py
65
xfconf.py
|
@ -1,5 +1,6 @@
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
import string
|
||||||
import subprocess
|
import subprocess
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
@ -7,6 +8,15 @@ from dataclasses import dataclass
|
||||||
class Xfconf:
|
class Xfconf:
|
||||||
"""Interface around Xfconf, using xion-query behind the scene."""
|
"""Interface around Xfconf, using xion-query behind the scene."""
|
||||||
|
|
||||||
|
# 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._xq = xq or self.find_xq()
|
self._xq = xq or self.find_xq()
|
||||||
|
|
||||||
|
@ -21,6 +31,7 @@ class Xfconf:
|
||||||
if not print_failures:
|
if not print_failures:
|
||||||
return None
|
return None
|
||||||
print(f"xion-query command failed with code {exc.returncode}.")
|
print(f"xion-query command failed with code {exc.returncode}.")
|
||||||
|
print("command:", command)
|
||||||
if exc.stdout:
|
if exc.stdout:
|
||||||
print("stdout:", exc.stdout.decode().strip())
|
print("stdout:", exc.stdout.decode().strip())
|
||||||
if exc.stderr:
|
if exc.stderr:
|
||||||
|
@ -58,28 +69,44 @@ class Xfconf:
|
||||||
return XfconfProperty.parse(output)
|
return XfconfProperty.parse(output)
|
||||||
|
|
||||||
def set_property(self, channel, prop, prop_type, value):
|
def set_property(self, channel, prop, prop_type, value):
|
||||||
"""Create or update this property."""
|
"""Create or update this property, return True on success."""
|
||||||
if not self.does_property_exist(channel, prop):
|
if not self.does_property_exist(channel, prop):
|
||||||
self.create_property(channel, prop, prop_type, value)
|
return self.create_property(channel, prop, prop_type, value)
|
||||||
else:
|
else:
|
||||||
self.update_property(channel, prop, value)
|
return self.update_property(channel, prop, value)
|
||||||
|
|
||||||
def create_property(self, channel, prop, prop_type, value):
|
def create_property(self, channel, prop, prop_type, value):
|
||||||
"""Create a new property with those params, return True on success."""
|
"""Create a new property with those params, return True on success."""
|
||||||
if " " in value:
|
prop_type = Xfconf.convert_type(prop_type)
|
||||||
value = f'"{value}"'
|
if not prop_type:
|
||||||
|
return False
|
||||||
|
value = Xfconf.sanitize_str(value)
|
||||||
output = self.xq(["-c", channel, "-p", prop, "-n",
|
output = self.xq(["-c", channel, "-p", prop, "-n",
|
||||||
"-t", prop_type, "-s", value])
|
"-t", prop_type, "-s", value])
|
||||||
if output is None:
|
return output == ""
|
||||||
return False
|
|
||||||
|
|
||||||
def update_property(self, channel, prop, value):
|
def update_property(self, channel, prop, value):
|
||||||
"""Update an existing property, return True on success."""
|
"""Update an existing property, return True on success."""
|
||||||
if " " in value:
|
value = Xfconf.sanitize_str(value)
|
||||||
value = f'"{value}"'
|
|
||||||
output = self.xq(["-c", channel, "-p", prop, "-s", value])
|
output = self.xq(["-c", channel, "-p", prop, "-s", value])
|
||||||
return output == ""
|
return output == ""
|
||||||
|
|
||||||
|
def set_property_array(self, channel, prop, values):
|
||||||
|
"""Set a property array, return True on success.
|
||||||
|
|
||||||
|
Due to limitations in the way xfconf-query work, whether the array
|
||||||
|
exists or not it is entirely replaced by values.
|
||||||
|
"""
|
||||||
|
command = ["-c", channel, "-p", prop, "-n", "-a"]
|
||||||
|
for value in values:
|
||||||
|
subtype = Xfconf.convert_type(value["type"])
|
||||||
|
if not subtype:
|
||||||
|
return False
|
||||||
|
subvalue = Xfconf.sanitize_str(value["value"])
|
||||||
|
command += ["-t", subtype, "-s", subvalue]
|
||||||
|
output = self.xq(command)
|
||||||
|
return output == ""
|
||||||
|
|
||||||
def reset_root(self, channel, root):
|
def reset_root(self, channel, root):
|
||||||
"""Reset all channel properties under root, return True on success."""
|
"""Reset all channel properties under root, return True on success."""
|
||||||
output = self.xqs(f"-c {channel} -p {root} -r -R")
|
output = self.xqs(f"-c {channel} -p {root} -r -R")
|
||||||
|
@ -87,11 +114,29 @@ class Xfconf:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def find_xq():
|
def find_xq():
|
||||||
|
"""Find xion-query in the path and return it, or None on failure."""
|
||||||
xq = shutil.which("xion-query")
|
xq = shutil.which("xion-query")
|
||||||
if not xq:
|
if not xq:
|
||||||
exit("Could not find xion-query in path.")
|
print("Could not find xion-query in path.")
|
||||||
|
return None
|
||||||
return xq
|
return xq
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def convert_type(gtype):
|
||||||
|
"""Get an xfconf-query type from a gtype."""
|
||||||
|
xq_type = Xfconf.TYPE_MAP.get(gtype)
|
||||||
|
if xq_type is None:
|
||||||
|
print(f"Unknown gtype {gtype}.")
|
||||||
|
return xq_type
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def sanitize_str(value):
|
||||||
|
"""Wrap value with doublequotes if it contains whitespaces."""
|
||||||
|
for char in string.whitespace:
|
||||||
|
if char in value:
|
||||||
|
return f'"{value}"'
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
XION_PROP_RE = re.compile(r"t:(\S+) (.+)")
|
XION_PROP_RE = re.compile(r"t:(\S+) (.+)")
|
||||||
|
|
||||||
|
|
28
xion.py
28
xion.py
|
@ -57,15 +57,6 @@ def main():
|
||||||
class Xion:
|
class Xion:
|
||||||
"""Manipulate Xfconf settings trees."""
|
"""Manipulate Xfconf settings trees."""
|
||||||
|
|
||||||
# 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)
|
||||||
|
|
||||||
|
@ -95,6 +86,10 @@ class Xion:
|
||||||
tree[prop_name] = leaf
|
tree[prop_name] = leaf
|
||||||
return tree
|
return tree
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _build_prop_leaf(prop):
|
||||||
|
return {"type": prop.gtype, "value": str(prop.value)}
|
||||||
|
|
||||||
def export_tree(self, channel, root, tree, output_path):
|
def export_tree(self, channel, root, tree, output_path):
|
||||||
"""Export a property tree as a sorted JSON file."""
|
"""Export a property tree as a sorted JSON file."""
|
||||||
tree["channel"] = channel
|
tree["channel"] = channel
|
||||||
|
@ -139,20 +134,11 @@ class Xion:
|
||||||
|
|
||||||
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."""
|
||||||
# if isinstance(content, list):
|
if isinstance(content, list):
|
||||||
# for subprop in content:
|
return self.xfconf.set_property_array(channel, name, content)
|
||||||
# if not self.apply_property(channel,
|
|
||||||
prop_type = content["type"]
|
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"]
|
value = content["value"]
|
||||||
self.xfconf.set_property(channel, name, xq_type, value)
|
return self.xfconf.set_property(channel, name, prop_type, value)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _build_prop_leaf(prop):
|
|
||||||
return {"type": prop.gtype, "value": str(prop.value)}
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
Loading…
Reference in a new issue