aboutsummaryrefslogtreecommitdiffstats
path: root/klippy
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2018-09-04 20:49:47 -0400
committerKevinOConnor <kevin@koconnor.net>2018-09-25 13:48:46 -0400
commitf80456a6983566ae073e477b9033090263c4745d (patch)
tree4a977a3091288ad1a83321487ee61ba9d5f13dea /klippy
parent5144c5f01e82c07e332703cce27ce0aa6890b234 (diff)
downloadkutter-f80456a6983566ae073e477b9033090263c4745d.tar.gz
kutter-f80456a6983566ae073e477b9033090263c4745d.tar.xz
kutter-f80456a6983566ae073e477b9033090263c4745d.zip
configfile: Move config file code from klippy.py to new file
Add a klippy/configfile.py file with the code needed to read the main printer config file. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'klippy')
-rw-r--r--klippy/configfile.py116
-rw-r--r--klippy/extras/display/menu.py9
-rw-r--r--klippy/klippy.py121
3 files changed, 130 insertions, 116 deletions
diff --git a/klippy/configfile.py b/klippy/configfile.py
new file mode 100644
index 00000000..9c4ad4e1
--- /dev/null
+++ b/klippy/configfile.py
@@ -0,0 +1,116 @@
+# Code for reading the Klipper config file
+#
+# Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
+#
+# This file may be distributed under the terms of the GNU GPLv3 license.
+import ConfigParser
+
+error = ConfigParser.Error
+
+class sentinel:
+ pass
+
+class ConfigWrapper:
+ error = ConfigParser.Error
+ def __init__(self, printer, fileconfig, access_tracking, section):
+ self.printer = printer
+ self.fileconfig = fileconfig
+ self.access_tracking = access_tracking
+ self.section = section
+ def get_printer(self):
+ return self.printer
+ def get_name(self):
+ return self.section
+ def _get_wrapper(self, parser, option, default,
+ minval=None, maxval=None, above=None, below=None):
+ if (default is not sentinel
+ and not self.fileconfig.has_option(self.section, option)):
+ return default
+ self.access_tracking[(self.section.lower(), option.lower())] = 1
+ try:
+ v = parser(self.section, option)
+ except self.error as e:
+ raise
+ except:
+ raise error("Unable to parse option '%s' in section '%s'" % (
+ option, self.section))
+ if minval is not None and v < minval:
+ raise error(
+ "Option '%s' in section '%s' must have minimum of %s" % (
+ option, self.section, minval))
+ if maxval is not None and v > maxval:
+ raise error(
+ "Option '%s' in section '%s' must have maximum of %s" % (
+ option, self.section, maxval))
+ if above is not None and v <= above:
+ raise error("Option '%s' in section '%s' must be above %s" % (
+ option, self.section, above))
+ if below is not None and v >= below:
+ raise self.error("Option '%s' in section '%s' must be below %s" % (
+ option, self.section, below))
+ return v
+ def get(self, option, default=sentinel):
+ return self._get_wrapper(self.fileconfig.get, option, default)
+ def getint(self, option, default=sentinel, minval=None, maxval=None):
+ return self._get_wrapper(
+ self.fileconfig.getint, option, default, minval, maxval)
+ def getfloat(self, option, default=sentinel,
+ minval=None, maxval=None, above=None, below=None):
+ return self._get_wrapper(self.fileconfig.getfloat, option, default,
+ minval, maxval, above, below)
+ def getboolean(self, option, default=sentinel):
+ return self._get_wrapper(self.fileconfig.getboolean, option, default)
+ def getchoice(self, option, choices, default=sentinel):
+ c = self.get(option, default)
+ if c not in choices:
+ raise error("Choice '%s' for option '%s' in section '%s'"
+ " is not a valid choice" % (c, option, self.section))
+ return choices[c]
+ def getsection(self, section):
+ return ConfigWrapper(self.printer, self.fileconfig,
+ self.access_tracking, section)
+ def has_section(self, section):
+ return self.fileconfig.has_section(section)
+ def get_prefix_sections(self, prefix):
+ return [self.getsection(s) for s in self.fileconfig.sections()
+ if s.startswith(prefix)]
+
+class ConfigLogger:
+ def __init__(self, fileconfig, printer):
+ self.lines = ["===== Config file ====="]
+ fileconfig.write(self)
+ self.lines.append("=======================")
+ printer.set_rollover_info("config", "\n".join(self.lines))
+ def write(self, data):
+ self.lines.append(data.strip())
+
+class PrinterConfig:
+ def __init__(self, printer):
+ self.printer = printer
+ def read_config(self, filename):
+ fileconfig = ConfigParser.RawConfigParser()
+ res = fileconfig.read(filename)
+ if not res:
+ raise error("Unable to open config file %s" % (filename,))
+ return ConfigWrapper(self.printer, fileconfig, {}, 'printer')
+ def read_main_config(self):
+ filename = self.printer.get_start_args()['config_file']
+ return self.read_config(filename)
+ def check_unused_options(self, config):
+ access_tracking = config.access_tracking
+ fileconfig = config.fileconfig
+ objects = dict(self.printer.lookup_objects())
+ # Validate that there are no undefined parameters in the config file
+ valid_sections = { s: 1 for s, o in access_tracking }
+ for section_name in fileconfig.sections():
+ section = section_name.lower()
+ if section not in valid_sections and section not in objects:
+ raise error("Section '%s' is not a valid config section" % (
+ section,))
+ for option in fileconfig.options(section_name):
+ option = option.lower()
+ if (section, option) not in access_tracking:
+ raise error("Option '%s' is not valid in section '%s'" % (
+ option, section))
+ def log_config(self, config):
+ ConfigLogger(config.fileconfig, self.printer)
diff --git a/klippy/extras/display/menu.py b/klippy/extras/display/menu.py
index c32d9ed3..a8c74341 100644
--- a/klippy/extras/display/menu.py
+++ b/klippy/extras/display/menu.py
@@ -5,9 +5,7 @@
# Copyright (C) 2018 Janar Sööt <janar.soot@gmail.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
-import os, ConfigParser, logging
-import sys, ast, re
-import klippy
+import os, logging, sys, ast, re
class error(Exception):
@@ -963,10 +961,9 @@ class MenuManager:
desc=self.cmd_DO_help)
# Parse local config file in same directory as current module
- fileconfig = ConfigParser.RawConfigParser()
+ pconfig = self.printer.lookup_object('configfile')
localname = os.path.join(os.path.dirname(__file__), 'menu.cfg')
- fileconfig.read(localname)
- localconfig = klippy.ConfigWrapper(self.printer, fileconfig, {}, None)
+ localconfig = pconfig.read_config(localname)
# Load items from local config
self.load_menuitems(localconfig)
diff --git a/klippy/klippy.py b/klippy/klippy.py
index e88b54d8..e8f737f7 100644
--- a/klippy/klippy.py
+++ b/klippy/klippy.py
@@ -4,10 +4,9 @@
# Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
-import sys, os, optparse, logging, time, threading
-import collections, ConfigParser, importlib
+import sys, os, optparse, logging, time, threading, collections, importlib
import util, reactor, queuelogger, msgproto
-import gcode, pins, heater, mcu, toolhead
+import gcode, configfile, pins, heater, mcu, toolhead
message_ready = "Printer is ready"
@@ -46,89 +45,8 @@ config, and restart the host software.
Printer is shutdown
"""
-class ConfigWrapper:
- error = ConfigParser.Error
- class sentinel:
- pass
- def __init__(self, printer, fileconfig, access_tracking, section):
- self.printer = printer
- self.fileconfig = fileconfig
- self.access_tracking = access_tracking
- self.section = section
- def get_printer(self):
- return self.printer
- def get_name(self):
- return self.section
- def _get_wrapper(self, parser, option, default,
- minval=None, maxval=None, above=None, below=None):
- if (default is not self.sentinel
- and not self.fileconfig.has_option(self.section, option)):
- return default
- self.access_tracking[(self.section.lower(), option.lower())] = 1
- try:
- v = parser(self.section, option)
- except self.error as e:
- raise
- except:
- raise self.error("Unable to parse option '%s' in section '%s'" % (
- option, self.section))
- if minval is not None and v < minval:
- raise self.error(
- "Option '%s' in section '%s' must have minimum of %s" % (
- option, self.section, minval))
- if maxval is not None and v > maxval:
- raise self.error(
- "Option '%s' in section '%s' must have maximum of %s" % (
- option, self.section, maxval))
- if above is not None and v <= above:
- raise self.error(
- "Option '%s' in section '%s' must be above %s" % (
- option, self.section, above))
- if below is not None and v >= below:
- raise self.error(
- "Option '%s' in section '%s' must be below %s" % (
- option, self.section, below))
- return v
- def get(self, option, default=sentinel):
- return self._get_wrapper(self.fileconfig.get, option, default)
- def getint(self, option, default=sentinel, minval=None, maxval=None):
- return self._get_wrapper(
- self.fileconfig.getint, option, default, minval, maxval)
- def getfloat(self, option, default=sentinel,
- minval=None, maxval=None, above=None, below=None):
- return self._get_wrapper(self.fileconfig.getfloat, option, default,
- minval, maxval, above, below)
- def getboolean(self, option, default=sentinel):
- return self._get_wrapper(self.fileconfig.getboolean, option, default)
- def getchoice(self, option, choices, default=sentinel):
- c = self.get(option, default)
- if c not in choices:
- raise self.error(
- "Choice '%s' for option '%s' in section '%s'"
- " is not a valid choice" % (c, option, self.section))
- return choices[c]
- def getsection(self, section):
- return ConfigWrapper(self.printer, self.fileconfig,
- self.access_tracking, section)
- def has_section(self, section):
- return self.fileconfig.has_section(section)
- def get_prefix_sections(self, prefix):
- return [self.getsection(s) for s in self.fileconfig.sections()
- if s.startswith(prefix)]
-
-class ConfigLogger():
- def __init__(self, cfg, bglogger):
- self.lines = ["===== Config file ====="]
- cfg.write(self)
- self.lines.append("=======================")
- data = "\n".join(self.lines)
- logging.info(data)
- bglogger.set_rollover_info("config", data)
- def write(self, data):
- self.lines.append(data.strip())
-
class Printer:
- config_error = ConfigParser.Error
+ config_error = configfile.error
def __init__(self, input_fd, bglogger, start_args):
self.bglogger = bglogger
self.start_args = start_args
@@ -156,10 +74,10 @@ class Printer:
raise self.config_error(
"Printer object '%s' already created" % (name,))
self.objects[name] = obj
- def lookup_object(self, name, default=ConfigWrapper.sentinel):
+ def lookup_object(self, name, default=configfile.sentinel):
if name in self.objects:
return self.objects[name]
- if default is ConfigWrapper.sentinel:
+ if default is configfile.sentinel:
raise self.config_error("Unknown config object '%s'" % (name,))
return default
def lookup_objects(self, module=None):
@@ -196,36 +114,19 @@ class Printer:
self.objects[section] = init_func(config.getsection(section))
return self.objects[section]
def _read_config(self):
- fileconfig = ConfigParser.RawConfigParser()
- config_file = self.start_args['config_file']
- res = fileconfig.read(config_file)
- if not res:
- raise self.config_error("Unable to open config file %s" % (
- config_file,))
+ self.objects['configfile'] = pconfig = configfile.PrinterConfig(self)
+ config = pconfig.read_main_config()
if self.bglogger is not None:
- ConfigLogger(fileconfig, self.bglogger)
+ pconfig.log_config(config)
# Create printer components
- access_tracking = {}
- config = ConfigWrapper(self, fileconfig, access_tracking, 'printer')
for m in [pins, heater, mcu]:
m.add_printer_objects(config)
- for section in fileconfig.sections():
- self.try_load_module(config, section)
+ for section_config in config.get_prefix_sections(''):
+ self.try_load_module(config, section_config.get_name())
for m in [toolhead]:
m.add_printer_objects(config)
# Validate that there are no undefined parameters in the config file
- valid_sections = { s: 1 for s, o in access_tracking }
- for section_name in fileconfig.sections():
- section = section_name.lower()
- if section not in valid_sections and section not in self.objects:
- raise self.config_error(
- "Section '%s' is not a valid config section" % (section,))
- for option in fileconfig.options(section_name):
- option = option.lower()
- if (section, option) not in access_tracking:
- raise self.config_error(
- "Option '%s' is not valid in section '%s'" % (
- option, section))
+ pconfig.check_unused_options(config)
# Determine which printer objects have state callbacks
self.state_cb = [o.printer_state for o in self.objects.values()
if hasattr(o, 'printer_state')]