Module lib.tools.jsonconfig
Functions for managing the json configuration. All configuration classes end the name with the word Config. For each of these classes, a json file with the same name is stored in the config directory of the board.
Expand source code
# Distributed under Pycameresp License
# Copyright (c) 2023 Remi BERTHOLET
# pylint:disable=consider-using-f-string
""" Functions for managing the json configuration.
All configuration classes end the name with the word Config.
For each of these classes, a json file with the same name is stored in the config directory of the board. """
# pylint:disable=consider-using-dict-items
# pylint:disable=consider-iterating-dictionary
import time
import json
import re
try:
import uos
except:
import os as uos
import tools.logger
import tools.strings
import tools.filesystem
import tools.date
jsonconfig_self = None
jsonconfig_value = None
class JsonConfig:
""" Manage json configuration """
classes = set()
def __init__(self):
""" Constructor """
self.modification_date = 0
self.last_refresh = 0
JsonConfig.classes.add(self.__class__)
def config_root(self):
""" Configuration root path """
if tools.filesystem.ismicropython():
return "/config"
else:
return uos.path.expanduser('~') + "/.pycameresp"
def purify(self, datas):
""" Remove unwanted items """
result = datas
if type(datas) == type(b""):
result = datas
elif type(datas) == type([]):
result = []
for item in datas:
result.append(self.purify(item))
elif type(datas) == type((0,0)):
result = []
for item in datas:
result.append(self.purify(item))
result = tuple(result)
elif type(datas) == type({}):
result = {}
for key, value in datas.items():
if key not in ["last_refresh","modification_date"]:
result[key] = self.purify(value)
elif isinstance(datas, JsonConfig):
result = self.purify(datas.__dict__.copy())
result["__class__"] = datas.__class__.__name__
return result
def save(self, file=None, part_filename=""):
""" Save object in json file """
try:
filename = self.get_pathname(tools.strings.tofilename(part_filename))
str_data = self.to_string()
file, filename = self.open(file=file, read_write="w", part_filename=part_filename)
file.write(str_data)
file.close()
self.modification_date = uos.stat(filename)[8]
self.last_refresh = time.time()
return True
except Exception as _err:
str_data = self.to_string()
tools.logger.syslog(_err, "Cannot save %s "%(filename))
return False
def to_string(self):
""" Convert the configuration to string """
return json.dumps(self.to_dict(),separators=(',', ':'))
def to_dict(self):
""" Convert the configuration to dictionnary """
return tools.strings.tostrings(self.purify(self.__dict__.copy()))
def get_pathname(self, part_filename=""):
""" Get the configuration filename according to the class name """
return self.config_root()+"/"+self.get_filename(part_filename) + ".json"
def list_all(self):
""" List all configuration files """
result = []
pattern = self.get_filename() + ".*"
for fileinfo in tools.filesystem.list_directory(self.config_root()):
name = fileinfo[0]
typ = fileinfo[1]
if typ & 0xF000 != 0x4000:
if re.match(pattern, name):
result.append(tools.strings.tobytes(name[len(self.get_filename()):-len(".json")]))
return result
def get_filename(self, part_filename=""):
""" Return the config filename """
if self.__class__.__name__[-len("Config"):] == "Config":
name = self.__class__.__name__[:-len("Config")]
else:
name = self.__class__.__name__
return name + tools.strings.tostrings(part_filename)
def open(self, file=None, read_write="r", part_filename=""):
""" Create or open configuration file """
# pylint:disable=unspecified-encoding
filename = file
if tools.filesystem.exists(self.config_root()) is False:
tools.filesystem.makedir(self.config_root())
if file is None:
filename = self.get_pathname(tools.strings.tofilename(part_filename))
file = open(filename, read_write)
elif type(file) == type(""):
file = open(filename, read_write)
return file, filename
def update(self, params, show_error=True):
""" Update object with html request params """
if b"name" in params and b"value" in params and len(params) == 2:
setmany = False
params = {params[b"name"]:params[b"value"]}
else:
setmany = True
for name in self.__dict__.keys():
# Case of web input is missing when bool is false
if type(self.__dict__[name]) == type(True):
name = tools.strings.tobytes(name)
if name in params:
if type(params[name]) == type(""):
if params[name] == "":
params[name] = True
elif params[name] == "1" or params[name].lower() == "true":
params[name] = True
elif params[name] == "0" or params[name].lower() == "false":
params[name] = False
elif type(params[name]) == type(b""):
if params[name] == b"":
params[name] = True
elif params[name] == b"1" or params[name].lower() == b"true":
params[name] = True
elif params[name] == b"0" or params[name].lower() == b"false":
params[name] = False
else:
if setmany:
params[name] = False
# Case of web input is integer but string with number received
elif type(self.__dict__[name]) == type(0):
name = tools.strings.tobytes(name)
if name in params:
try:
params[name] = int(params[name])
except:
if b"date" in name:
try:
params[name] = tools.date.html_to_date(params[name])
except:
params[name] = 0
elif b"time" in name:
try:
params[name] = tools.date.html_to_time(params[name])
except:
params[name] = 0
else:
params[name] = 0
elif type(self.__dict__[name]) == type(0.):
name = tools.strings.tobytes(name)
if name in params:
try:
params[name] = float(params[name])
except:
params[name] = 0
result = True
for name, value in params.items():
res = self.exec(name, value, show_error)
if res is not True:
result = res
return result
def exec(self, name, value, show_error=True):
""" Add attribute to current object """
result = True
global jsonconfig_self, jsonconfig_value
name = tools.strings.tostrings(name)
try:
try:
# pylint: disable=exec-used
jsonconfig_self = self
exec("a = jsonconfig_self.%s"%name)
jsonconfig_self = None
existing = True
except Exception as _err:
if "'NoneType' object" in str(_err):
result = None
existing = False
if existing:
execval = "jsonconfig_self.%s = jsonconfig_value"%(name)
# pylint: disable=exec-used
jsonconfig_value = self.instantiate(value)
jsonconfig_self = self
exec(execval)
jsonconfig_self = None
jsonconfig_value = None
else:
if name != "action" and show_error and result is not None:
print("%s.%s not existing"%(self.__class__.__name__, tools.strings.tostrings(name)))
except Exception as _err:
tools.logger.syslog(_err, "Error on %s"%(name))
result = False
return result
def search_class(self, class_name):
""" Search class with class name """
for class_ in JsonConfig.classes:
if class_.__name__ == class_name:
return class_
return None
def instantiate(self, values):
""" Instantiate all objects with its classes """
result = values
if type(values) == type({}):
class_name = values.setdefault(b"__class__",None)
instance = None
if class_name is not None:
class_ = self.search_class(tools.strings.tostrings(class_name))
del values[b"__class__"]
if class_ is not None:
instance = class_()
for key, value in values.items():
instance.exec(key,value)
result = instance
elif type(values) == type([]):
result = []
for value in values:
result.append(self.instantiate(value))
return result
def load(self, file=None, part_filename="", tobytes=True, errorlog=True):
""" Load object with the file specified """
filename = ""
try:
filename = self.get_pathname(tools.strings.tofilename(part_filename))
file, filename = self.open(file=file, read_write="r", part_filename=part_filename)
data = json.load(file)
if tobytes:
data = tools.strings.tobytes(data)
self.update(data)
file.close()
self.last_refresh = time.time()
return True
except OSError as _err:
if _err.args[0] == 2:
if errorlog:
tools.logger.syslog("Not existing %s "%(filename))
else:
tools.logger.syslog(_err, "Cannot load %s "%(filename))
return False
except Exception as _err:
tools.logger.syslog(_err, "Cannot load %s "%(filename))
return False
def forget(self, part_filename=""):
""" Forget configuration """
tools.filesystem.remove(self.get_pathname(part_filename=part_filename))
def is_changed(self, part_filename=""):
""" Indicates if the configuration changed """
if self.last_refresh + 31 < time.time():
try:
modification_date = uos.stat(self.get_pathname(tools.strings.tofilename(part_filename)))[8]
if self.modification_date != modification_date:
self.modification_date = modification_date
return True
except:
pass
return False
def load_create(self, file=None, part_filename="", tobytes=True, errorlog=True):
""" Load and create file if not existing """
if self.load(file, part_filename, tobytes, errorlog) is False:
self.save(file, part_filename)
def refresh(self, file=None, part_filename="", tobytes=True, errorlog=True):
""" refresh the configuration if it has changed since then """
if self.is_changed(part_filename):
self.load(file, part_filename, tobytes, errorlog)
return True
return False
def exists(self, part_filename=""):
""" Indicates if the configuration file existing """
return tools.filesystem.exists(self.get_pathname(tools.strings.tofilename(part_filename)))
Classes
class JsonConfig-
Manage json configuration
Constructor
Expand source code
class JsonConfig: """ Manage json configuration """ classes = set() def __init__(self): """ Constructor """ self.modification_date = 0 self.last_refresh = 0 JsonConfig.classes.add(self.__class__) def config_root(self): """ Configuration root path """ if tools.filesystem.ismicropython(): return "/config" else: return uos.path.expanduser('~') + "/.pycameresp" def purify(self, datas): """ Remove unwanted items """ result = datas if type(datas) == type(b""): result = datas elif type(datas) == type([]): result = [] for item in datas: result.append(self.purify(item)) elif type(datas) == type((0,0)): result = [] for item in datas: result.append(self.purify(item)) result = tuple(result) elif type(datas) == type({}): result = {} for key, value in datas.items(): if key not in ["last_refresh","modification_date"]: result[key] = self.purify(value) elif isinstance(datas, JsonConfig): result = self.purify(datas.__dict__.copy()) result["__class__"] = datas.__class__.__name__ return result def save(self, file=None, part_filename=""): """ Save object in json file """ try: filename = self.get_pathname(tools.strings.tofilename(part_filename)) str_data = self.to_string() file, filename = self.open(file=file, read_write="w", part_filename=part_filename) file.write(str_data) file.close() self.modification_date = uos.stat(filename)[8] self.last_refresh = time.time() return True except Exception as _err: str_data = self.to_string() tools.logger.syslog(_err, "Cannot save %s "%(filename)) return False def to_string(self): """ Convert the configuration to string """ return json.dumps(self.to_dict(),separators=(',', ':')) def to_dict(self): """ Convert the configuration to dictionnary """ return tools.strings.tostrings(self.purify(self.__dict__.copy())) def get_pathname(self, part_filename=""): """ Get the configuration filename according to the class name """ return self.config_root()+"/"+self.get_filename(part_filename) + ".json" def list_all(self): """ List all configuration files """ result = [] pattern = self.get_filename() + ".*" for fileinfo in tools.filesystem.list_directory(self.config_root()): name = fileinfo[0] typ = fileinfo[1] if typ & 0xF000 != 0x4000: if re.match(pattern, name): result.append(tools.strings.tobytes(name[len(self.get_filename()):-len(".json")])) return result def get_filename(self, part_filename=""): """ Return the config filename """ if self.__class__.__name__[-len("Config"):] == "Config": name = self.__class__.__name__[:-len("Config")] else: name = self.__class__.__name__ return name + tools.strings.tostrings(part_filename) def open(self, file=None, read_write="r", part_filename=""): """ Create or open configuration file """ # pylint:disable=unspecified-encoding filename = file if tools.filesystem.exists(self.config_root()) is False: tools.filesystem.makedir(self.config_root()) if file is None: filename = self.get_pathname(tools.strings.tofilename(part_filename)) file = open(filename, read_write) elif type(file) == type(""): file = open(filename, read_write) return file, filename def update(self, params, show_error=True): """ Update object with html request params """ if b"name" in params and b"value" in params and len(params) == 2: setmany = False params = {params[b"name"]:params[b"value"]} else: setmany = True for name in self.__dict__.keys(): # Case of web input is missing when bool is false if type(self.__dict__[name]) == type(True): name = tools.strings.tobytes(name) if name in params: if type(params[name]) == type(""): if params[name] == "": params[name] = True elif params[name] == "1" or params[name].lower() == "true": params[name] = True elif params[name] == "0" or params[name].lower() == "false": params[name] = False elif type(params[name]) == type(b""): if params[name] == b"": params[name] = True elif params[name] == b"1" or params[name].lower() == b"true": params[name] = True elif params[name] == b"0" or params[name].lower() == b"false": params[name] = False else: if setmany: params[name] = False # Case of web input is integer but string with number received elif type(self.__dict__[name]) == type(0): name = tools.strings.tobytes(name) if name in params: try: params[name] = int(params[name]) except: if b"date" in name: try: params[name] = tools.date.html_to_date(params[name]) except: params[name] = 0 elif b"time" in name: try: params[name] = tools.date.html_to_time(params[name]) except: params[name] = 0 else: params[name] = 0 elif type(self.__dict__[name]) == type(0.): name = tools.strings.tobytes(name) if name in params: try: params[name] = float(params[name]) except: params[name] = 0 result = True for name, value in params.items(): res = self.exec(name, value, show_error) if res is not True: result = res return result def exec(self, name, value, show_error=True): """ Add attribute to current object """ result = True global jsonconfig_self, jsonconfig_value name = tools.strings.tostrings(name) try: try: # pylint: disable=exec-used jsonconfig_self = self exec("a = jsonconfig_self.%s"%name) jsonconfig_self = None existing = True except Exception as _err: if "'NoneType' object" in str(_err): result = None existing = False if existing: execval = "jsonconfig_self.%s = jsonconfig_value"%(name) # pylint: disable=exec-used jsonconfig_value = self.instantiate(value) jsonconfig_self = self exec(execval) jsonconfig_self = None jsonconfig_value = None else: if name != "action" and show_error and result is not None: print("%s.%s not existing"%(self.__class__.__name__, tools.strings.tostrings(name))) except Exception as _err: tools.logger.syslog(_err, "Error on %s"%(name)) result = False return result def search_class(self, class_name): """ Search class with class name """ for class_ in JsonConfig.classes: if class_.__name__ == class_name: return class_ return None def instantiate(self, values): """ Instantiate all objects with its classes """ result = values if type(values) == type({}): class_name = values.setdefault(b"__class__",None) instance = None if class_name is not None: class_ = self.search_class(tools.strings.tostrings(class_name)) del values[b"__class__"] if class_ is not None: instance = class_() for key, value in values.items(): instance.exec(key,value) result = instance elif type(values) == type([]): result = [] for value in values: result.append(self.instantiate(value)) return result def load(self, file=None, part_filename="", tobytes=True, errorlog=True): """ Load object with the file specified """ filename = "" try: filename = self.get_pathname(tools.strings.tofilename(part_filename)) file, filename = self.open(file=file, read_write="r", part_filename=part_filename) data = json.load(file) if tobytes: data = tools.strings.tobytes(data) self.update(data) file.close() self.last_refresh = time.time() return True except OSError as _err: if _err.args[0] == 2: if errorlog: tools.logger.syslog("Not existing %s "%(filename)) else: tools.logger.syslog(_err, "Cannot load %s "%(filename)) return False except Exception as _err: tools.logger.syslog(_err, "Cannot load %s "%(filename)) return False def forget(self, part_filename=""): """ Forget configuration """ tools.filesystem.remove(self.get_pathname(part_filename=part_filename)) def is_changed(self, part_filename=""): """ Indicates if the configuration changed """ if self.last_refresh + 31 < time.time(): try: modification_date = uos.stat(self.get_pathname(tools.strings.tofilename(part_filename)))[8] if self.modification_date != modification_date: self.modification_date = modification_date return True except: pass return False def load_create(self, file=None, part_filename="", tobytes=True, errorlog=True): """ Load and create file if not existing """ if self.load(file, part_filename, tobytes, errorlog) is False: self.save(file, part_filename) def refresh(self, file=None, part_filename="", tobytes=True, errorlog=True): """ refresh the configuration if it has changed since then """ if self.is_changed(part_filename): self.load(file, part_filename, tobytes, errorlog) return True return False def exists(self, part_filename=""): """ Indicates if the configuration file existing """ return tools.filesystem.exists(self.get_pathname(tools.strings.tofilename(part_filename)))Class variables
var classes
Methods
def config_root(self)-
Configuration root path
Expand source code
def config_root(self): """ Configuration root path """ if tools.filesystem.ismicropython(): return "/config" else: return uos.path.expanduser('~') + "/.pycameresp" def exec(self, name, value, show_error=True)-
Add attribute to current object
Expand source code
def exec(self, name, value, show_error=True): """ Add attribute to current object """ result = True global jsonconfig_self, jsonconfig_value name = tools.strings.tostrings(name) try: try: # pylint: disable=exec-used jsonconfig_self = self exec("a = jsonconfig_self.%s"%name) jsonconfig_self = None existing = True except Exception as _err: if "'NoneType' object" in str(_err): result = None existing = False if existing: execval = "jsonconfig_self.%s = jsonconfig_value"%(name) # pylint: disable=exec-used jsonconfig_value = self.instantiate(value) jsonconfig_self = self exec(execval) jsonconfig_self = None jsonconfig_value = None else: if name != "action" and show_error and result is not None: print("%s.%s not existing"%(self.__class__.__name__, tools.strings.tostrings(name))) except Exception as _err: tools.logger.syslog(_err, "Error on %s"%(name)) result = False return result def exists(self, part_filename='')-
Indicates if the configuration file existing
Expand source code
def exists(self, part_filename=""): """ Indicates if the configuration file existing """ return tools.filesystem.exists(self.get_pathname(tools.strings.tofilename(part_filename))) def forget(self, part_filename='')-
Forget configuration
Expand source code
def forget(self, part_filename=""): """ Forget configuration """ tools.filesystem.remove(self.get_pathname(part_filename=part_filename)) def get_filename(self, part_filename='')-
Return the config filename
Expand source code
def get_filename(self, part_filename=""): """ Return the config filename """ if self.__class__.__name__[-len("Config"):] == "Config": name = self.__class__.__name__[:-len("Config")] else: name = self.__class__.__name__ return name + tools.strings.tostrings(part_filename) def get_pathname(self, part_filename='')-
Get the configuration filename according to the class name
Expand source code
def get_pathname(self, part_filename=""): """ Get the configuration filename according to the class name """ return self.config_root()+"/"+self.get_filename(part_filename) + ".json" def instantiate(self, values)-
Instantiate all objects with its classes
Expand source code
def instantiate(self, values): """ Instantiate all objects with its classes """ result = values if type(values) == type({}): class_name = values.setdefault(b"__class__",None) instance = None if class_name is not None: class_ = self.search_class(tools.strings.tostrings(class_name)) del values[b"__class__"] if class_ is not None: instance = class_() for key, value in values.items(): instance.exec(key,value) result = instance elif type(values) == type([]): result = [] for value in values: result.append(self.instantiate(value)) return result def is_changed(self, part_filename='')-
Indicates if the configuration changed
Expand source code
def is_changed(self, part_filename=""): """ Indicates if the configuration changed """ if self.last_refresh + 31 < time.time(): try: modification_date = uos.stat(self.get_pathname(tools.strings.tofilename(part_filename)))[8] if self.modification_date != modification_date: self.modification_date = modification_date return True except: pass return False def list_all(self)-
List all configuration files
Expand source code
def list_all(self): """ List all configuration files """ result = [] pattern = self.get_filename() + ".*" for fileinfo in tools.filesystem.list_directory(self.config_root()): name = fileinfo[0] typ = fileinfo[1] if typ & 0xF000 != 0x4000: if re.match(pattern, name): result.append(tools.strings.tobytes(name[len(self.get_filename()):-len(".json")])) return result def load(self, file=None, part_filename='', tobytes=True, errorlog=True)-
Load object with the file specified
Expand source code
def load(self, file=None, part_filename="", tobytes=True, errorlog=True): """ Load object with the file specified """ filename = "" try: filename = self.get_pathname(tools.strings.tofilename(part_filename)) file, filename = self.open(file=file, read_write="r", part_filename=part_filename) data = json.load(file) if tobytes: data = tools.strings.tobytes(data) self.update(data) file.close() self.last_refresh = time.time() return True except OSError as _err: if _err.args[0] == 2: if errorlog: tools.logger.syslog("Not existing %s "%(filename)) else: tools.logger.syslog(_err, "Cannot load %s "%(filename)) return False except Exception as _err: tools.logger.syslog(_err, "Cannot load %s "%(filename)) return False def load_create(self, file=None, part_filename='', tobytes=True, errorlog=True)-
Load and create file if not existing
Expand source code
def load_create(self, file=None, part_filename="", tobytes=True, errorlog=True): """ Load and create file if not existing """ if self.load(file, part_filename, tobytes, errorlog) is False: self.save(file, part_filename) def open(self, file=None, read_write='r', part_filename='')-
Create or open configuration file
Expand source code
def open(self, file=None, read_write="r", part_filename=""): """ Create or open configuration file """ # pylint:disable=unspecified-encoding filename = file if tools.filesystem.exists(self.config_root()) is False: tools.filesystem.makedir(self.config_root()) if file is None: filename = self.get_pathname(tools.strings.tofilename(part_filename)) file = open(filename, read_write) elif type(file) == type(""): file = open(filename, read_write) return file, filename def purify(self, datas)-
Remove unwanted items
Expand source code
def purify(self, datas): """ Remove unwanted items """ result = datas if type(datas) == type(b""): result = datas elif type(datas) == type([]): result = [] for item in datas: result.append(self.purify(item)) elif type(datas) == type((0,0)): result = [] for item in datas: result.append(self.purify(item)) result = tuple(result) elif type(datas) == type({}): result = {} for key, value in datas.items(): if key not in ["last_refresh","modification_date"]: result[key] = self.purify(value) elif isinstance(datas, JsonConfig): result = self.purify(datas.__dict__.copy()) result["__class__"] = datas.__class__.__name__ return result def refresh(self, file=None, part_filename='', tobytes=True, errorlog=True)-
refresh the configuration if it has changed since then
Expand source code
def refresh(self, file=None, part_filename="", tobytes=True, errorlog=True): """ refresh the configuration if it has changed since then """ if self.is_changed(part_filename): self.load(file, part_filename, tobytes, errorlog) return True return False def save(self, file=None, part_filename='')-
Save object in json file
Expand source code
def save(self, file=None, part_filename=""): """ Save object in json file """ try: filename = self.get_pathname(tools.strings.tofilename(part_filename)) str_data = self.to_string() file, filename = self.open(file=file, read_write="w", part_filename=part_filename) file.write(str_data) file.close() self.modification_date = uos.stat(filename)[8] self.last_refresh = time.time() return True except Exception as _err: str_data = self.to_string() tools.logger.syslog(_err, "Cannot save %s "%(filename)) return False def search_class(self, class_name)-
Search class with class name
Expand source code
def search_class(self, class_name): """ Search class with class name """ for class_ in JsonConfig.classes: if class_.__name__ == class_name: return class_ return None def to_dict(self)-
Convert the configuration to dictionnary
Expand source code
def to_dict(self): """ Convert the configuration to dictionnary """ return tools.strings.tostrings(self.purify(self.__dict__.copy())) def to_string(self)-
Convert the configuration to string
Expand source code
def to_string(self): """ Convert the configuration to string """ return json.dumps(self.to_dict(),separators=(',', ':')) def update(self, params, show_error=True)-
Update object with html request params
Expand source code
def update(self, params, show_error=True): """ Update object with html request params """ if b"name" in params and b"value" in params and len(params) == 2: setmany = False params = {params[b"name"]:params[b"value"]} else: setmany = True for name in self.__dict__.keys(): # Case of web input is missing when bool is false if type(self.__dict__[name]) == type(True): name = tools.strings.tobytes(name) if name in params: if type(params[name]) == type(""): if params[name] == "": params[name] = True elif params[name] == "1" or params[name].lower() == "true": params[name] = True elif params[name] == "0" or params[name].lower() == "false": params[name] = False elif type(params[name]) == type(b""): if params[name] == b"": params[name] = True elif params[name] == b"1" or params[name].lower() == b"true": params[name] = True elif params[name] == b"0" or params[name].lower() == b"false": params[name] = False else: if setmany: params[name] = False # Case of web input is integer but string with number received elif type(self.__dict__[name]) == type(0): name = tools.strings.tobytes(name) if name in params: try: params[name] = int(params[name]) except: if b"date" in name: try: params[name] = tools.date.html_to_date(params[name]) except: params[name] = 0 elif b"time" in name: try: params[name] = tools.date.html_to_time(params[name]) except: params[name] = 0 else: params[name] = 0 elif type(self.__dict__[name]) == type(0.): name = tools.strings.tobytes(name) if name in params: try: params[name] = float(params[name]) except: params[name] = 0 result = True for name, value in params.items(): res = self.exec(name, value, show_error) if res is not True: result = res return result