Skip to content

Commit 9cc22f6

Browse files
authored
Ovos conf (#12)
* feat/ovos.conf support authored-by: jarbasai <jarbasai@mailfence.com>
1 parent 6908517 commit 9cc22f6

File tree

3 files changed

+104
-127
lines changed

3 files changed

+104
-127
lines changed

ovos_utils/configuration.py

Lines changed: 99 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,120 +1,147 @@
1-
import json
2-
import os
3-
from os import makedirs
4-
from os.path import isfile, exists, expanduser, join, dirname, isdir
1+
from importlib.util import find_spec
2+
from os.path import isfile, join
53

4+
import xdg.BaseDirectory
5+
from json_database import JsonStorage
66
from xdg import BaseDirectory as XDG
77

8-
from ovos_utils.fingerprinting import core_supports_xdg
9-
from ovos_utils.json_helper import merge_dict, load_commented_json
8+
from ovos_utils.json_helper import load_commented_json, merge_dict
109
from ovos_utils.log import LOG
1110
from ovos_utils.system import search_mycroft_core_location
1211

13-
# for downstream support, all XDG paths should respect this
14-
_BASE_FOLDER = "mycroft"
15-
_CONFIG_FILE_NAME = "mycroft.conf"
1612

17-
_DEFAULT_CONFIG = None
18-
_SYSTEM_CONFIG = os.environ.get('MYCROFT_SYSTEM_CONFIG',
19-
f'/etc/{_BASE_FOLDER}/{_CONFIG_FILE_NAME}')
20-
# Make sure we support the old location until mycroft moves to XDG
21-
_OLD_USER_CONFIG = join(expanduser('~'), '.' + _BASE_FOLDER, _CONFIG_FILE_NAME)
22-
_USER_CONFIG = join(XDG.xdg_config_home, _BASE_FOLDER, _CONFIG_FILE_NAME)
23-
_WEB_CONFIG_CACHE = join(XDG.xdg_config_home, _BASE_FOLDER, 'web_cache.json')
13+
def get_ovos_config():
14+
config = {"xdg": True,
15+
"base_folder": "mycroft",
16+
"config_filename": "mycroft.conf",
17+
"default_config_path": find_default_config()}
18+
19+
try:
20+
if isfile("/etc/OpenVoiceOS/ovos.conf"):
21+
config = merge_dict(config,
22+
load_commented_json(
23+
"/etc/OpenVoiceOS/ovos.conf"))
24+
elif isfile("/etc/mycroft/ovos.conf"):
25+
config = merge_dict(config,
26+
load_commented_json("/etc/mycroft/ovos.conf"))
27+
except:
28+
# tolerate bad json TODO proper exception (?)
29+
pass
30+
31+
# This includes both the user config and
32+
# /etc/xdg/OpenVoiceOS/ovos.conf
33+
for p in xdg.BaseDirectory.load_config_paths("OpenVoiceOS"):
34+
if isfile(join(p, "ovos.conf")):
35+
try:
36+
xdg_cfg = load_commented_json(join(p, "ovos.conf"))
37+
config = merge_dict(config, xdg_cfg)
38+
except:
39+
# tolerate bad json TODO proper exception (?)
40+
pass
41+
42+
# let's check for derivatives specific configs
43+
# the assumption is that these cores are exclusive to each other,
44+
# this will never find more than one override
45+
# TODO this works if using dedicated .venvs what about system installs?
46+
cores = config.get("module_overrides") or {}
47+
for k in cores:
48+
if find_spec(k):
49+
config = merge_dict(config, cores[k])
50+
break
51+
else:
52+
subcores = config.get("submodule_mappings") or {}
53+
for k in subcores:
54+
if find_spec(k):
55+
config = merge_dict(config, cores[subcores[k]])
56+
break
57+
58+
return config
59+
60+
61+
def is_using_xdg():
62+
return get_ovos_config().get("xdg", True)
2463

2564

2665
def get_xdg_base():
27-
global _BASE_FOLDER
28-
return _BASE_FOLDER
66+
return get_ovos_config().get("base_folder") or "mycroft"
67+
68+
69+
def save_ovos_core_config(new_config):
70+
OVOS_CONFIG = join(xdg.BaseDirectory.save_config_path("OpenVoiceOS"),
71+
"ovos.conf")
72+
cfg = JsonStorage(OVOS_CONFIG)
73+
cfg.update(new_config)
74+
cfg.store()
75+
return cfg
2976

3077

3178
def set_xdg_base(folder_name):
32-
global _BASE_FOLDER, _WEB_CONFIG_CACHE
3379
LOG.info(f"XDG base folder set to: '{folder_name}'")
34-
_BASE_FOLDER = folder_name
35-
_WEB_CONFIG_CACHE = join(XDG.xdg_config_home, _BASE_FOLDER,
36-
'web_cache.json')
80+
save_ovos_core_config({"base_folder": folder_name})
3781

3882

3983
def set_config_filename(file_name, core_folder=None):
40-
global _CONFIG_FILE_NAME, _SYSTEM_CONFIG, _OLD_USER_CONFIG, _USER_CONFIG, \
41-
_BASE_FOLDER
4284
if core_folder:
43-
_BASE_FOLDER = core_folder
4485
set_xdg_base(core_folder)
4586
LOG.info(f"config filename set to: '{file_name}'")
46-
_CONFIG_FILE_NAME = file_name
47-
_SYSTEM_CONFIG = os.environ.get('MYCROFT_SYSTEM_CONFIG',
48-
f'/etc/{_BASE_FOLDER}/{_CONFIG_FILE_NAME}')
49-
# Make sure we support the old location still
50-
# Deprecated and will be removed eventually
51-
_OLD_USER_CONFIG = join(expanduser('~'), '.' + _BASE_FOLDER,
52-
_CONFIG_FILE_NAME)
53-
_USER_CONFIG = join(XDG.xdg_config_home, _BASE_FOLDER, _CONFIG_FILE_NAME)
87+
save_ovos_core_config({"config_filename": file_name})
5488

5589

5690
def set_default_config(file_path=None):
57-
global _DEFAULT_CONFIG
58-
_DEFAULT_CONFIG = file_path or find_default_config()
91+
file_path = file_path or find_default_config()
5992
LOG.info(f"default config file changed to: {file_path}")
93+
save_ovos_core_config({"default_config_path": file_path})
6094

6195

6296
def find_default_config():
63-
if _DEFAULT_CONFIG:
64-
# previously set, otherwise None
65-
return _DEFAULT_CONFIG
6697
mycroft_root = search_mycroft_core_location()
6798
if not mycroft_root:
6899
raise FileNotFoundError("Couldn't find mycroft core root folder.")
69-
return join(mycroft_root, _BASE_FOLDER, "configuration", _CONFIG_FILE_NAME)
100+
return join(mycroft_root, "mycroft", "configuration", "mycroft.conf")
70101

71102

72103
def find_user_config():
73-
# ideally it will have been set by downstream using util methods
104+
if is_using_xdg():
105+
path = join(XDG.xdg_config_home, get_xdg_base(), get_config_filename())
106+
if isfile(path):
107+
return path
74108
old, path = get_config_locations(default=False, web_cache=False,
75109
system=False, old_user=True,
76110
user=True)
77111
if isfile(path):
78112
return path
79-
80-
if core_supports_xdg():
81-
path = join(XDG.xdg_config_home, _BASE_FOLDER, _CONFIG_FILE_NAME)
82-
else:
83-
path = old
84-
# mark1 runs as a different user
85-
sysconfig = MycroftSystemConfig()
86-
platform_str = sysconfig.get("enclosure", {}).get("platform", "")
87-
if platform_str == "mycroft_mark_1":
88-
path = "/home/mycroft/.mycroft/mycroft.conf"
89-
90-
if not isfile(path) and isfile(old):
91-
# xdg might be disabled in HolmesV compatibility mode
92-
# or migration might be in progress
93-
# (user action required when updated from a no xdg install)
94-
path = old
95-
113+
if isfile(old):
114+
return old
115+
# mark1 runs as a different user
116+
sysconfig = MycroftSystemConfig()
117+
platform_str = sysconfig.get("enclosure", {}).get("platform", "")
118+
if platform_str == "mycroft_mark_1":
119+
path = "/home/mycroft/.mycroft/mycroft.conf"
96120
return path
97121

98122

99123
def get_config_locations(default=True, web_cache=True, system=True,
100124
old_user=True, user=True):
101125
locs = []
126+
ovos_cfg = get_ovos_config()
102127
if default:
103-
locs.append(_DEFAULT_CONFIG)
128+
locs.append(ovos_cfg["default_config_path"])
104129
if system:
105-
locs.append(_SYSTEM_CONFIG)
130+
locs.append(f"/etc/{ovos_cfg['base_folder']}/{ovos_cfg['config_filename']}")
106131
if web_cache:
107-
locs.append(_WEB_CONFIG_CACHE)
132+
locs.append(f"{XDG.xdg_config_home}/{ovos_cfg['base_folder']}/web_cache.json")
108133
if old_user:
109-
locs.append(_OLD_USER_CONFIG)
134+
locs.append(f"~/.{ovos_cfg['base_folder']}/{ovos_cfg['config_filename']}")
110135
if user:
111-
locs.append(_USER_CONFIG)
112-
136+
if is_using_xdg():
137+
locs.append(f"{XDG.xdg_config_home}/{ovos_cfg['base_folder']}/{ovos_cfg['config_filename']}")
138+
else:
139+
locs.append(f"~/.{ovos_cfg['base_folder']}/{ovos_cfg['config_filename']}")
113140
return locs
114141

115142

116143
def get_webcache_location():
117-
return join(XDG.xdg_config_home, _BASE_FOLDER, _CONFIG_FILE_NAME)
144+
return join(XDG.xdg_config_home, get_xdg_base(), 'web_cache.json')
118145

119146

120147
def get_xdg_config_locations():
@@ -128,7 +155,7 @@ def get_xdg_config_locations():
128155

129156

130157
def get_config_filename():
131-
return _CONFIG_FILE_NAME
158+
return get_ovos_config().get("config_filename") or "mycroft.conf"
132159

133160

134161
def set_config_name(name, core_folder=None):
@@ -138,7 +165,7 @@ def set_config_name(name, core_folder=None):
138165

139166

140167
def read_mycroft_config():
141-
conf = LocalConf(None)
168+
conf = LocalConf("tmp/dummy.conf")
142169
conf.merge(MycroftDefaultConfig())
143170
conf.merge(MycroftSystemConfig())
144171
conf.merge(MycroftUserConfig())
@@ -155,60 +182,13 @@ def update_mycroft_config(config, path=None):
155182
return conf
156183

157184

158-
class LocalConf(dict):
185+
class LocalConf(JsonStorage):
159186
"""
160187
Config dict from file.
161188
"""
162189
allow_overwrite = True
163-
164-
def __init__(self, path):
165-
super(LocalConf, self).__init__()
166-
self.path = path
167-
if self.path:
168-
self.load_local(self.path)
169-
170-
def load_local(self, path):
171-
"""
172-
Load local json file into self.
173-
174-
Args:
175-
path (str): file to load
176-
"""
177-
path = expanduser(path)
178-
if exists(path) and isfile(path):
179-
try:
180-
config = load_commented_json(path)
181-
for key in config:
182-
self.__setitem__(key, config[key])
183-
#LOG.debug("Configuration {} loaded".format(path))
184-
except Exception as e:
185-
LOG.error("Error loading configuration '{}'".format(path))
186-
LOG.error(repr(e))
187-
else:
188-
pass
189-
#LOG.debug("Configuration '{}' not defined, skipping".format(path))
190-
191-
def reload(self):
192-
self.load_local(self.path)
193-
194-
def store(self, path=None):
195-
"""
196-
store the configuration locally.
197-
"""
198-
path = path or self.path
199-
if not path:
200-
LOG.warning("config path not set, updating user config!!")
201-
update_mycroft_config(self)
202-
return
203-
path = expanduser(path)
204-
if not isdir(dirname(path)):
205-
makedirs(dirname(path))
206-
with open(path, 'w', encoding="utf-8") as f:
207-
json.dump(self, f, indent=4, ensure_ascii=False)
208-
209-
def merge(self, conf):
210-
merge_dict(self, conf)
211-
return self
190+
def __init__(self, path=None):
191+
super(LocalConf, self).__init__(path)
212192

213193

214194
class ReadOnlyConfig(LocalConf):
@@ -234,10 +214,10 @@ def __setattr__(self, key, value):
234214
raise PermissionError
235215
super().__setattr__(key, value)
236216

237-
def merge(self, conf):
217+
def merge(self, *args, **kwargs):
238218
if not self.allow_overwrite:
239219
raise PermissionError
240-
super().merge(conf)
220+
super().merge(*args, **kwargs)
241221

242222
def store(self, path=None):
243223
if not self.allow_overwrite:
@@ -253,7 +233,7 @@ def __init__(self):
253233

254234
class MycroftDefaultConfig(ReadOnlyConfig):
255235
def __init__(self):
256-
path = find_default_config()
236+
path = get_ovos_config()["default_config_path"]
257237
super().__init__(path)
258238
if not self.path or not isfile(self.path):
259239
LOG.debug(f"mycroft root path not found, could not load default .conf: {self.path}")

ovos_utils/fingerprinting.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@
22
import socket
33
from enum import Enum
44
from os.path import join, isfile
5-
from xdg import BaseDirectory as XDG
65
from ovos_utils.system import is_installed, has_screen, \
76
get_desktop_environment, search_mycroft_core_location, is_process_running
7+
from ovos_utils.configuration import is_using_xdg
88

99

1010
class MycroftPlatform(str, Enum):
1111
PICROFT = "picroft"
1212
BIGSCREEN = "kde"
1313
OVOS = "OpenVoiceOS"
14+
HIVEMIND = "HiveMind"
1415
MARK1 = "mycroft_mark_1"
1516
MARK2 = "mycroft_mark_2"
1617
HOLMESV = "HolmesV"
@@ -79,13 +80,9 @@ def get_fingerprint():
7980

8081

8182
def core_supports_xdg():
82-
if any((is_holmes(), is_ovos(), is_neon_core(), is_chatterbox_core())):
83+
if any((is_holmes(), is_chatterbox_core())):
8384
return True
84-
# mycroft-core does not support XDG as of 10 may 2021
85-
# however there are patched versions out there, eg, alpine package
86-
# check if the .conf exists in new location
87-
# TODO deprecate
88-
return isfile(join(XDG.xdg_config_home, 'mycroft', 'mycroft.conf'))
85+
return is_using_xdg()
8986

9087

9188
def get_mycroft_version():

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
setup(
44
name='ovos_utils',
5-
version='0.0.14a3',
5+
version='0.0.14a4',
66
packages=['ovos_utils',
77
'ovos_utils.intents',
88
'ovos_utils.sound',

0 commit comments

Comments
 (0)