diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..7edf564 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,19 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 8f14b6b..36e0613 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,7 @@ - + + + \ No newline at end of file diff --git a/.idea/pyuserconfig.iml b/.idea/pyuserconfig.iml index 5bbded1..4becf17 100644 --- a/.idea/pyuserconfig.iml +++ b/.idea/pyuserconfig.iml @@ -2,7 +2,7 @@ - + diff --git a/Userconfig/__init__.py b/Userconfig/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/Userconfig/cfgfile.py b/Userconfig/cfgfile.py deleted file mode 100644 index d0038ef..0000000 --- a/Userconfig/cfgfile.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env python3 -# encoding: utf-8 -# - -""" -cfgfile.py - -Created by Marcus Stoegbauer on 2013-01-12. -""" -import configparser -import os -import re - -class Conf(object): - confobj = configparser.RawConfigParser() - cfgfile = '' - debug = None - - def __init__(self, filename=None): - """if filename is set, open config file and initialize the ConfigParser - """ - self.confobj = configparser.RawConfigParser() - if filename: - self.setfilename(filename) - - def setdebug(self, debug): - """docstring for setdebug""" - self.debug = debug - - def setfilename(self, filename): - """initialize the ConfigParser - """ - ret = self.confobj.read(filename) - if len(ret) == 0 or ret[0] != filename: - raise Exception('Cannot read config file ' + filename) - self.cfgfile = filename - if self.debug: - self.debug.debug("Read config file %s" % filename, 2) - self.debug.debug("Replacing environment variables in %s." % filename, 3) - - for s in self.confobj.sections(): - for (i, val) in self.confobj.items(s): - tempre = re.search(r"\$([A-Z]+)[^A-Z]*", val) - if tempre: - varname = tempre.group(1) - if self.debug: - self.debug.debug("Found variable %s in %s." % (varname, i), 3) - if varname in os.environ: - if self.debug: - self.debug.debug("%s exists in environment, replacing with %s." % - (varname, os.environ[varname]), 3) - self.set(s, i, val.replace("$"+varname, os.environ[varname])) - - def get(self, section, option): - """returns the value of option in section - """ - if not self.cfgfile: - raise Exception('No config file set') - try: - return self.confobj.get(section, option) - except configparser.NoOptionError: - raise ValueError('Option does not exist') - - def set(self, section, option, value): - """docstring for update""" - self.confobj.set(section, option, value) - - def getitems(self, section): - """returns all items in section - """ - if not self.cfgfile: - raise Exception('No config file set') - return self.confobj.items(section) - - def check(self, section, option): - """checks for option in section""" - return self.confobj.has_option(section, option) diff --git a/Userconfig/checks.py b/Userconfig/checks.py deleted file mode 100644 index ed72da3..0000000 --- a/Userconfig/checks.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 -# encoding: utf-8 -# -""" -checks.py - -Created by Marcus Stoegbauer on 2013-01-10. -""" - -import platform -from operator import itemgetter - -class Checks(object): - def __init__(self): - pass - - def get_short_hostname(self): - """docstring for getShortHostname""" - hostname = platform.node() - if hostname.count("."): - hostname = hostname.split(".")[0] - return hostname - - # def getShortHostname - - def __classes_for_host__(self, reverse=False): - """docstring for __classesForHost""" - classes = [] - for c in dir(self): - if c.startswith("__"): - continue - ret = getattr(self, c)() - if type(ret) == tuple and len(ret) == 3: - classes.append(ret) - return map(lambda k: (k[1], k[2]), sorted(classes, key=itemgetter(0), reverse=reverse)) - - def header(self): - """docstring for header""" - return (0, "", "header") - - def footer(self): - """docstring for footer""" - return (1000, "", "footer") - - def all(self): - """docstring for all""" - return (998, "", "all") - - def arch(self): - """docstring for arch""" - return (800, "Arch", platform.system()) - - def hostname(self): - """docstring for hostname""" - hostname = self.get_short_hostname() - return (10, "Host", hostname) - - def app(self): - """docstring for app""" - hostname = self.get_short_hostname() - if hostname == "glitters": - return (500, "", "rancid_hosts") - else: - return () - # def app -# def checks diff --git a/cli/__init__.py b/cli/__init__.py new file mode 100755 index 0000000..f85c788 --- /dev/null +++ b/cli/__init__.py @@ -0,0 +1,171 @@ +import sys +import os +import tempfile +import re +import argparse +import time +from userconfig.cfgfile import Conf +from userconfig.tools import Debug +from userconfig.checks import classes_for_host +#FIXME: fill in: from userconfig.tools import ... + + +class Usage(Exception): + def __init__(self, msg): + self.msg = msg + + +def workconf(directory, depth=2): + """walks through directory, collecting all filenames, returns list of all filenames""" + dirs = os.listdir(directory) + ret = [] + debug.stdout(" ================ workconf ===============", 1) + debug.stdout(" Finding files in directory %s." % directory, 4) + for d in dirs: + name = directory+"/"+d + if os.path.isdir(name): + workconf(name, depth+1) + if name.endswith(".swp"): + continue + if d == ".svn": + continue + ret.append(name) + debug.stdout(" +++ Found file %s in directory %s" % (name, directory), 4) + debug.stdout(" ================ workconf ===============", 1) + return ret + + + + +def build_file(classfiles, destfile, commentstring): + """open all classfiles, assemble them and write the contents into a tempfile + returns the name of tempfile + :param classfiles: + :param destfile: + :param commentstring: + :return: str + """ + content = [] + debug.stdout(" ================ build_file ===============", 1) + if commentstring != "": + debug.stdout(" +++ commentstring found, adding header.", 3) + content.append(commentstring + " " + cfg.get("Main","stamp") + " " + time.strftime("%+") + "\n") + + for f in classfiles: + debug.stdout(" +++ Merging %s." % f, 4) + fp = open(f, "r") + filecontent = fp.read() + fp.close() + if commentstring == "": + # look for stamp in content, replace with real stamp + if re.search(re.escape(cfg.get("Main","stampreplace")), filecontent): + debug.stdout(" +++ commentstring empty, replacing stamp in file", 3) + filecontent = re.sub(re.escape(cfg.get("Main","stampreplace")), cfg.get("Main","stamp"), filecontent) + content.append(filecontent) + + (tempfd, tempfilename) = tempfile.mkstemp(prefix=os.path.basename(destfile), dir="/tmp") + + try: + fp = os.fdopen(tempfd, "w") + except: + Tools.error("Cannot write to temporary file %s" % tempfilename) + os.remove(tempfilename) + return False + debug.stdout(" +++ Writing merged files into tempfile %s." % tempfilename, 3) + for block in content: + fp.write(block) + fp.write("\n") + fp.close() + debug.stdout(" ================ build_file ===============", 1) + return tempfilename + + +def process_all_files(destfiles, dir_config): + """processes all files in destfiles, generate files from classes, compare and copy if necessary""" + debug.stdout(" ================ process_all_files ===============", 1) + for df in destfiles.keys(): + debug.stdout(" ??? Processing source files for %s." % df, 2) + if not os.path.exists(os.path.dirname(df)): + debug.stdout(" +++ Directory %s does not exist, creating" % os.path.dirname(df), 1) + os.mkdir(os.path.dirname(df)) + if not os.path.isdir(os.path.dirname(df)): + debug.stdout(" --- Destination directory %s does not exist, skipping." % os.path.dirname(df), 1) + return False + # assemble file to tmp + commentstring = "" + if dir_config.check("Main", "commentstring"): + commentstring = dir_config.get("Main", "commentstring") + debug.stdout(" +++ Found commentstring %s in %s" % (commentstring, df), 3) + + tempfilename = build_file(destfiles[df], df, commentstring) + if not tempfilename: + debug.stdout(" --- Error while creating temp file for %s, skipping." % df, 1) + continue + debug.stdout(" +++ Merged files %s for %s into %s" % (str(destfiles[df]), df, tempfilename), 2) + + # diff assembled file and config file + if Tools.diff(df, tempfilename, commentstring, debug): + debug.stdout("File %s has changed" % df, 0) + if not Tools.user_config_generated(df, cfg): + debug.stdout(" +++ %s not generated by userconfig, backing up." % df, 2) + # file not generated from userconfig -> back up + Tools.backup_file(df, debug) + # copy tmp file to real location + debug.stdout("Copy %s to %s." % (tempfilename, df), 0) + Tools.copy_file(tempfilename, df, debug) + # remove tmp + debug.stdout(" +++ Removing temporary file %s." % tempfilename, 2) + os.remove(tempfilename) + debug.stdout(" ================ process_all_files ===============", 1) + + +classchecks = Checks() + + +def main(): + parser = argparse.ArgumentParser(prog='userconfig', + description='Manages configuration files, usually in the user home directory') + + parser.add_argument('-v', + help='Verbosity level (multiple v for higher level)', + dest='verbose', action='count', default=0) + parser.add_argument('-f', '--file', + help='userconfig2.cfg config file', + dest='file', action='store') + cmdline = parser.parse_args() + cfg = Conf(cmdline.file) + debug = Debug() + cfg.set_debug(debug) + + cfg.debug(f"Verbose level is {cfg.debug._verbose}", 1) + cfg.debug("================ main ===============", 1) + temp_classes = "" + for h in classes_for_host(): + temp_classes=temp_classes + str(h) + "," + cfg.debug("+++ Current host is in classes %s" % temp_classes, 1) + configdir = cfg.get("configdir") + for d in os.listdir(configdir): + name = configdir+"/"+d + cfg.debug("+++ Working in %s" % name, 1) + if not os.path.isdir(name): + cfg.debug("--- %s is not a directory, skipping." % name, 3) + continue + elif d.startswith(".svn") or d.startswith(".git"): + cfg.debug("--- %s is .svn or .git, skipping." % name, 3) + continue + elif os.path.isfile(name+"/.ignore"): + cfg.debug("--- %s contains file .ignore, skipping." % name, 3) + continue + else: + cfg.debug("+++ Processing files in %s" % name, 2) + (destfiles, dirConfig) = workdir(name) + if isinstance(destfiles, dict): + if len(destfiles.keys()) > 0: + cfg.debug("+++ Building %d files: %s" % (len(destfiles.keys()), destfiles.keys()), 3) + process_all_files(destfiles, dirConfig) + else: + cfg.debug("--- No files found for %s, skipping." % name, 1) + + +if __name__ == '__main__': + main() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..3b2dc24 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,16 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "userconfig" +version = "2.0b-1" +authors = [ {name = "Marcus Stoegbauer", email = "marcus@grmpf.org"} ] +maintainers = [ {name = "Marcus Stoegbauer", email = "marcus@grmpf.org"} ] +description = "Generate config files for user home" + +[project.scripts] +userconfig = "cli:main" + +[tool.setuptools] +packages = [ "userconfig", "cli" ] diff --git a/setup.py b/setup.py index 064a0bc..fb09abf 100755 --- a/setup.py +++ b/setup.py @@ -1,13 +1,5 @@ -#!/usr/bin/env python3 +# migrated to pyproject.toml +# as reccomended: https://packaging.python.org/en/latest/guides/modernize-setup-py-project/#what-if-something-that-can-not-be-changed-expects-a-setup-py-file +from setuptools import setup -from distutils.core import setup - -setup(name="userconfig", - version="0.1", - description="Generate config files for user home", - author="Marcus Stoegbauer", - author_email="marcus@grmpf.org", - packages=["Userconfig"], - scripts=["userconfig.py"], - data_files=[('etc', ['userconfig.cfg'])] - ) +setup() \ No newline at end of file diff --git a/userconfig.py b/userconfig.py deleted file mode 100755 index cfa52e6..0000000 --- a/userconfig.py +++ /dev/null @@ -1,270 +0,0 @@ -#!/usr/bin/env python3 -# encoding: utf-8 -# - -""" -userconfig.py - -Created by Marcus Stoegbauer on 2013-01-10. -""" - -import sys -import os -import tempfile -import re -import getopt -import time -import subprocess - -# -import Userconfig.cfgfile as cfgfile -from Userconfig.checks import Checks -import Userconfig.Tools as Tools - -debug = Tools.Debug() - -classchecks = Checks() -cfg = cfgfile.Conf() - -help_message = """ --h help --v verbose level (multiple v for higher level) --c userconfig.cfg config file -""" - - -class Usage(Exception): - def __init__(self, msg): - self.msg = msg - - -def workconf(directory, depth=2): - """walks through directory, collecting all filenames, returns list of all filenames""" - dirs = os.listdir(directory) - ret = [] - debug.debug(" ================ workconf ===============", 1) - debug.debug(" Finding files in directory %s." % directory, 4) - for d in dirs: - name = directory+"/"+d - if os.path.isdir(name): - workconf(name, depth+1) - if name.endswith(".swp"): - continue - if d == ".svn": - continue - ret.append(name) - debug.debug(" +++ Found file %s in directory %s" % (name, directory), 4) - debug.debug(" ================ workconf ===============", 1) - return ret - - -def workdir(directory): - """walks through all host classes, checking if the classes directory exists in directory - then collect all filenames within this directory and return a dict of all files for this - directory - """ - debug.debug(" ================ workdir ===============", 1) - debug.debug(" Working on directory %s" % directory, 3) - # skip directory if no CONFIGFILE present - if not os.path.isfile(directory+"/"+cfg.get("Main", "configfile")): - debug.debug(" --- No %s in %s, skipping." % (cfg.get("Main", "configfile"), directory), 1) - return {},None - - # get config file for directory - dir_config = Tools.get_config(directory + "/" + cfg.get("Main", "configfile")) - if not dir_config: - debug.debug(" --- Cannot read %s in %s, skipping." % (cfg.get("Main", "configfile"), directory), 1) - return {},None - - destdir = dir_config.get("Main","dest") - # destfiles is a dict of all files that will be created from the classes config - # key is the destination filename, values are all classes filenames that are used to - # build the file - destfiles = {} - # FIXME: reverse_order should really be a bool in .cfg, implement variable types in cfg file - reverse_sort = False - try: - reverse_sort = (dir_config.get("Main", "reverse") == 'True') - except ValueError: - reverse_sort = False - - if os.access(directory + "/install.sh", os.X_OK): - subprocess.call([directory + "/install.sh"]) - - # walk through all know classes in directory and find filenames - for h in classchecks.__classes_for_host__(reverse_sort): - # build classes directory - if h[0] != "": - classdir = directory+"/"+h[0]+"_"+h[1] - else: - classdir = directory+"/"+h[1] - debug.debug(" ??? Looking for directory %s." % classdir, 4) - # if class directory exists - if os.path.isdir(classdir): - debug.debug(" +++ Found directory %s, getting files." % classdir, 4) - # get list of files within this class directory - tempfiles = workconf(classdir) - debug.debug(" +++ Got %d files: %s." % (len(tempfiles), str(tempfiles)), 4) - # put files into dict - for f in tempfiles: - destname = destdir+os.path.basename(f) # destination filename - if not destname in destfiles: - destfiles[destname] = [] - destfiles[destname].append(f) # append each file to dict - debug.debug(" +++ Added file to %s, now %d files: %s" % (destname, len(destfiles[destname]), destfiles[destname]), 4) - - debug.debug(" === workdir: %s, Files: %s" % (directory, str(destfiles)), 3) - debug.debug(" ================ workdir ===============", 1) - return destfiles, dir_config - - -def build_file(classfiles, destfile, commentstring): - """open all classfiles, assemble them and write the contents into a tempfile - returns the name of tempfile - :param classfiles: - :param destfile: - :param commentstring: - :return: str - """ - content = [] - debug.debug(" ================ build_file ===============", 1) - if commentstring != "": - debug.debug(" +++ commentstring found, adding header.", 3) - content.append(commentstring + " " + cfg.get("Main","stamp") + " " + time.strftime("%+") + "\n") - - for f in classfiles: - debug.debug(" +++ Merging %s." % f, 4) - fp = open(f, "r") - filecontent = fp.read() - fp.close() - if commentstring == "": - # look for stamp in content, replace with real stamp - if re.search(re.escape(cfg.get("Main","stampreplace")), filecontent): - debug.debug(" +++ commentstring empty, replacing stamp in file", 3) - filecontent = re.sub(re.escape(cfg.get("Main","stampreplace")), cfg.get("Main","stamp"), filecontent) - content.append(filecontent) - - (tempfd, tempfilename) = tempfile.mkstemp(prefix=os.path.basename(destfile), dir="/tmp") - - try: - fp = os.fdopen(tempfd, "w") - except: - Tools.error("Cannot write to temporary file %s" % tempfilename) - os.remove(tempfilename) - return False - debug.debug(" +++ Writing merged files into tempfile %s." % tempfilename, 3) - for block in content: - fp.write(block) - fp.write("\n") - fp.close() - debug.debug(" ================ build_file ===============", 1) - return tempfilename - - -def process_all_files(destfiles, dir_config): - """processes all files in destfiles, generate files from classes, compare and copy if necessary""" - debug.debug(" ================ process_all_files ===============", 1) - for df in destfiles.keys(): - debug.debug(" ??? Processing source files for %s." % df, 2) - if not os.path.exists(os.path.dirname(df)): - debug.debug(" +++ Directory %s does not exist, creating" % os.path.dirname(df), 1) - os.mkdir(os.path.dirname(df)) - if not os.path.isdir(os.path.dirname(df)): - debug.debug(" --- Destination directory %s does not exist, skipping." % os.path.dirname(df), 1) - return False - # assemble file to tmp - commentstring = "" - if dir_config.check("Main", "commentstring"): - commentstring = dir_config.get("Main", "commentstring") - debug.debug(" +++ Found commentstring %s in %s" % (commentstring, df), 3) - - tempfilename = build_file(destfiles[df], df, commentstring) - if not tempfilename: - debug.debug(" --- Error while creating temp file for %s, skipping." % df, 1) - continue - debug.debug(" +++ Merged files %s for %s into %s" % (str(destfiles[df]), df, tempfilename), 2) - - # diff assembled file and config file - if Tools.diff(df, tempfilename, commentstring, debug): - debug.debug("File %s has changed" % df, 0) - if not Tools.user_config_generated(df, cfg): - debug.debug(" +++ %s not generated by userconfig, backing up." % df, 2) - # file not generated from userconfig -> back up - Tools.backup_file(df, debug) - # copy tmp file to real location - debug.debug("Copy %s to %s." % (tempfilename, df), 0) - Tools.copy_file(tempfilename, df, debug) - # remove tmp - debug.debug(" +++ Removing temporary file %s." % tempfilename, 2) - os.remove(tempfilename) - debug.debug(" ================ process_all_files ===============", 1) - -def main(): - configfile_destinations = [os.environ['HOME'] + "/etc/", - os.environ['HOME'] + "/.local/etc"] - configfile = '' - for directory in configfile_destinations: - if os.path.isfile(directory + "/userconfig.cfg"): - configfile = directory + "/userconfig.cfg" - break - try: - try: - opts, args = getopt.getopt(sys.argv[1:], "hdc:v", ["help", "debug", "config="]) - except getopt.GetoptError as msg: - raise Usage(msg) - - for option, value in opts: - if option == "-v": - debug.addverbose() - if option in ("-h", "--help"): - raise Usage(help_message) - if option in ("-d", "--debug.debug"): - pass - if option in ("-c", "--config"): - configfile = value - except Usage as err: - Tools.error(sys.argv[0].split("/")[-1] + ": " + str(err.msg)) - Tools.error("\t for help use --help") - return 2 - - debug.debug("Verbose level is %d" % debug.verbose, 1) - debug.debug("Using configfile %s." % configfile, 1) - if not os.path.isfile(configfile): - Tools.error("No config file specified.") - return 2 - - cfg.setdebug(debug) - cfg.setfilename(configfile) - - debug.debug("================ main ===============", 1) - tempclasses = "" - for h in classchecks.__classes_for_host__(): - tempclasses=tempclasses + str(h) + "," - debug.debug("+++ Current host is in classes %s" % tempclasses, 1) - configdir = cfg.get("Main", "configdir") - for d in os.listdir(configdir): - destfiles = {} - name = configdir+"/"+d - debug.debug("+++ Working in %s" % name, 1) - if not os.path.isdir(name): - debug.debug("--- %s is not a directory, skipping." % name, 3) - continue - elif d.startswith(".svn") or d.startswith(".git"): - debug.debug("--- %s is .svn or .git, skipping." % name, 3) - continue - elif os.path.isfile(name+"/.ignore"): - debug.debug("--- %s contains file .ignore, skipping." % name, 3) - continue - else: - debug.debug("+++ Processing files in %s" % name, 2) - (destfiles, dirConfig) = workdir(name) - if isinstance(destfiles, dict): - if len(destfiles.keys()) > 0: - debug.debug("+++ Building %d files: %s" % (len(destfiles.keys()), destfiles.keys()), 3) - process_all_files(destfiles, dirConfig) - else: - debug.debug("--- No files found for %s, skipping." % name, 1) - - -if __name__ == '__main__': - main() diff --git a/userconfig/__init__.py b/userconfig/__init__.py new file mode 100644 index 0000000..2b68104 --- /dev/null +++ b/userconfig/__init__.py @@ -0,0 +1,67 @@ +from userconfig.tools import get_config +import subprocess +from userconfig.checks import classes_for_host + +class Userconfig(): + _cfg = None + + def __init__(self, cfg): + self._cfg = cfg + + def workdir(self, directory): + """walks through all host classes, checking if the classes directory exists in directory + then collect all filenames within this directory and return a dict of all files for this + directory + """ + self._cfg.debug.stdout(" ================ workdir ===============", 1) + self._cfg.debug.stdout(" Working on directory %s" % directory, 3) + # skip directory if no CONFIGFILE present + if not os.path.isfile(directory+"/"+self._cfg.get("configfile")): + self._cfg.debug.stdout(f' --- No file {self._cfg.get("configfile")} in {directory}, skipping.', 1) + return {}, None + + # get config file for directory + dir_config = get_config(directory + "/" + cfg.get("configfile")) + if not dir_config: + self._cfg.debug.stdout(f' --- Cannot read config file {self._cfg.get("configfile")} in {directory}, skipping.', 1) + return {}, None + + destdir = dir_config.get(section="Main",option="dest") + # destfiles is a dict of all files that will be created from the classes config + # key is the destination filename, values are all classes filenames that are used to + # build the file + destfiles = {} + reverse_sort = False + try: + reverse_sort = dir_config.get(section="Main", option="reverse", boolean=True) + except ValueError: + reverse_sort = False + + if os.access(directory + "/install.sh", os.X_OK): + subprocess.call([directory + "/install.sh"]) + + # walk through all know classes in directory and find filenames + for h in classes_for_host(reverse_sort): #### continue here + # build classes directory + if h[0] != "": + classdir = directory+"/"+h[0]+"_"+h[1] + else: + classdir = directory+"/"+h[1] + debug.stdout(" ??? Looking for directory %s." % classdir, 4) + # if class directory exists + if os.path.isdir(classdir): + debug.stdout(" +++ Found directory %s, getting files." % classdir, 4) + # get list of files within this class directory + tempfiles = workconf(classdir) + debug.stdout(" +++ Got %d files: %s." % (len(tempfiles), str(tempfiles)), 4) + # put files into dict + for f in tempfiles: + destname = destdir+os.path.basename(f) # destination filename + if not destname in destfiles: + destfiles[destname] = [] + destfiles[destname].append(f) # append each file to dict + debug.stdout(" +++ Added file to %s, now %d files: %s" % (destname, len(destfiles[destname]), destfiles[destname]), 4) + + debug.stdout(" === workdir: %s, Files: %s" % (directory, str(destfiles)), 3) + debug.stdout(" ================ workdir ===============", 1) + return destfiles, dir_config diff --git a/userconfig/cfgfile.py b/userconfig/cfgfile.py new file mode 100644 index 0000000..7817780 --- /dev/null +++ b/userconfig/cfgfile.py @@ -0,0 +1,57 @@ +import configparser +import os + + +class Conf(object): + _confobj = configparser.ConfigParser(os.environ) + _cfgfiles = [] + debug = None + + def __init__(self, filename=None, debug=None): + if debug: + self.set_debug(debug) + filenames = [] + if filename: + filenames.append(filename) + filenames.append(f'{os.environ.get("HOME")}/etc/userconfig2.conf') + ret = self.set_filenames(filenames) + if not ret: + raise ValueError(f'Cannot open either configuration file: {",".join(filenames)}') + + def set_debug(self, debug): + self.debug = debug + + def set_filenames(self, filenames): + ret = self._confobj.read(filenames) + if len(ret) == 0: + return None + if self.debug: + self.debug.stdout("Read config files: %s" % ", ".join(filenames), 2) + return ret + + def get(self, option, boolean=False, section='userconfig'): + try: + if boolean: + return self._confobj.getboolean(section, option) + else: + return self._confobj.get(section, option) + except (configparser.NoOptionError, configparser.NoOptionError) as e: + raise ValueError(f'Option {option} does not exist in section {section}: {e}') + + def set(self, option, value, section='userconfig'): + try: + self._confobj.set(section, option, value) + except configparser.NoSectionError as e: + raise ValueError(f'Section {section} does not exist: {e}') + + def get_items(self, section='userconfig'): + try: + return self._confobj.items(section) + except configparser.NoSectionError as e: + raise ValueError(f'Section {section} does not exist: {e}') + + def check(self, option, section='userconfig'): + return self._confobj.has_option(section, option) + + def sections(self): + return self._confobj.sections() diff --git a/userconfig/checks.py b/userconfig/checks.py new file mode 100644 index 0000000..ec45195 --- /dev/null +++ b/userconfig/checks.py @@ -0,0 +1,19 @@ +import platform + + +def get_short_hostname(): + hostname = platform.node() + if hostname.count("."): + hostname = hostname.split(".")[0] + return hostname + + +def classes_for_host(reverse=False): + classes = [(0, "", "header"), + (1000, "", "footer"), + (998, "", "all"), + (800, "Arch", platform.system()), + (10, "Host", get_short_hostname()) + ] + classes.sort(key=lambda k: k[0], reverse=reverse) + return [(k[1], k[2]) for k in classes] diff --git a/Userconfig/Tools.py b/userconfig/tools.py similarity index 65% rename from Userconfig/Tools.py rename to userconfig/tools.py index 87dc1cd..c9b628f 100644 --- a/Userconfig/Tools.py +++ b/userconfig/tools.py @@ -1,67 +1,47 @@ -#!/usr/bin/env python3 -# encoding: utf-8 - -""" -Tools.py - -Created by Marcus Stoegbauer on 2013-01-12. -""" - import sys import os -import Userconfig.cfgfile as cfgfile +from userconfig.cfgfile import Conf import re import time import shutil -class Debug(object): - verbose = 0 +class Debug(): + _verbose = 0 def __init__(self, verbose=0): - self.setverbose(verbose) + self.set_verbose(verbose) - def setverbose(self, verbose): - """docstring for setverbose""" - self.verbose = verbose + def set_verbose(self, verbose): + self._verbose = verbose - def addverbose(self): - """docstring for setverbose""" - self.verbose += 1 + def add_verbose(self): + self._verbose += 1 - def debug(self, out, level=0): - """docstring for debug""" - if self.verbose >= level: + def stdout(self, out, level=0): + if self._verbose >= level: print(out) - -def error(out): - """Print error on stderr""" - print(str(out)+"\n", file=sys.stderr) + def stderr(self, out, level=0): + if self._verbose >= level: + print(str(out)+"\n", file=sys.stderr) -def get_config(filename): +def get_config(filename, cfg): """reads filename as config, checks for DEST parameter and returns cfgfile object""" ret = None try: - ret = cfgfile.Conf(filename) + ret = Conf(filename) except: - error("Error reading config file %s" % filename) + cfg.debug.stderr("Error reading config file %s" % filename) return False - # check for DEST parameter if not ret.check("Main", "dest"): - error("No DEST in config file %s" % filename) + cfg.debug.stderr("No dest in config file %s" % filename) return False - - # replace $HOME with real home directory - if ret.get("Main", "dest") == "$HOME": - ret.set("Main", "dest", os.environ['HOME']) - # make sure DEST ends with / if not ret.get("Main", "dest").endswith("/"): ret.set("Main", "dest", ret.get("Main", "dest")+"/") - return ret @@ -76,9 +56,9 @@ def read_skip_comment(fp, commentstring): def diff(destfile, tempfile, commentstring, debug): """diff destfile and tempfile, returns True if files differ, False if they are the same""" - debug.debug("Diffing %s and %s" % (destfile, tempfile), 3) + debug.stdout("Diffing %s and %s" % (destfile, tempfile), 3) if not os.path.isfile(destfile): - debug.debug("Destfile %s does not exist, returning True." % destfile, 3) + debug.stdout("Destfile %s does not exist, returning True." % destfile, 3) # destfile does not exist -> copy tempfile over return True # if not destfile @@ -95,13 +75,13 @@ def diff(destfile, tempfile, commentstring, debug): if line1 != line2: fp1.close() fp2.close() - debug.debug("%s differs, return true" % destfile, 3) + debug.stdout("%s differs, return true" % destfile, 3) return True # if differ # for line fp1.close() fp2.close() - debug.debug("%s is the same, return false" % destfile, 3) + debug.stdout("%s is the same, return false" % destfile, 3) return False @@ -127,18 +107,18 @@ def user_config_generated(filename, cfg): def backup_file(filename, debug): """make backup of filename, returns True if backup is successful, False else""" if os.path.isfile(filename): - debug.debug("%s exists, finding backup name." % filename, 3) + debug.stdout("%s exists, finding backup name." % filename, 3) backupname = filename+".userconfig."+time.strftime("%F") testbackupname = backupname counter = 0 while os.path.isfile(testbackupname): counter+=1 testbackupname=backupname+"."+str(counter) - debug.debug("Renaming %s to %s" % (filename, testbackupname), 1) + debug.stdout("Renaming %s to %s" % (filename, testbackupname), 1) os.rename(filename, testbackupname) return True else: - debug.debug("%s does not exist, do not need backup." % filename, 3) + debug.stdout("%s does not exist, do not need backup." % filename, 3) return False @@ -147,12 +127,12 @@ def copy_file(sourcefile, destfile, debug): if os.path.isfile(sourcefile): # sourcefile exists - debug.debug("Source file %s exists, proceeding with copy." % sourcefile, 3) + debug.stdout("Source file %s exists, proceeding with copy." % sourcefile, 3) if not os.path.isfile(destfile) or os.access(destfile, os.W_OK): - debug.debug("Copying %s to %s" % (sourcefile, destfile), 1) + debug.stdout("Copying %s to %s" % (sourcefile, destfile), 1) shutil.copy(sourcefile, destfile) return True # destfile is writable else: - debug.debug("Destination file %s does not exist or is not writable." % destfile, 3) + debug.stdout("Destination file %s does not exist or is not writable." % destfile, 3) return False