overhaul to configParser. Parsing config works now.
This commit is contained in:
100
overviewer.py
100
overviewer.py
@@ -86,44 +86,6 @@ def configure_logger(loglevel=logging.INFO, verbose=False):
|
|||||||
logger.addHandler(logger.overviewerHandler)
|
logger.addHandler(logger.overviewerHandler)
|
||||||
logger.setLevel(loglevel)
|
logger.setLevel(loglevel)
|
||||||
|
|
||||||
def build_fake_settings(worldpath):
|
|
||||||
"""Builds and returns a renders dict as if it was parsed from a settings
|
|
||||||
file and returned with get_render_things()
|
|
||||||
|
|
||||||
This is used for the simple command line usage with no config file
|
|
||||||
|
|
||||||
"""
|
|
||||||
from overviewer_core import settingsDefinition, rendermodes
|
|
||||||
world = {}
|
|
||||||
# Seed this render with all the defaults
|
|
||||||
for defaultname, defaultinfo in settingsDefinition.render['values'].iteritems():
|
|
||||||
if 'default' in defaultinfo:
|
|
||||||
world[defaultname] = defaultinfo['default']
|
|
||||||
# Set required items for the render. If any new required items without
|
|
||||||
# defaults are added, this will need to be updated.
|
|
||||||
worlds = {'world': worldpath}
|
|
||||||
world['worldname'] = 'world'
|
|
||||||
world['title'] = "Overviewer Render"
|
|
||||||
world['rendermode'] = rendermodes.normal
|
|
||||||
|
|
||||||
renders = {worldpath: world}
|
|
||||||
|
|
||||||
# The following is mostly a copy/paste of the code in
|
|
||||||
# MultiWorldParser.validate(). Someone make MultiWorldParser more
|
|
||||||
# extensible to avoid this!
|
|
||||||
origs = dict()
|
|
||||||
for key in world:
|
|
||||||
definition = settingsDefinition.render['values'][key]
|
|
||||||
val = definition['validator'](world[key], world = worlds)
|
|
||||||
if definition.get('save_orig', False):
|
|
||||||
origs[key + "_orig"] = world[key]
|
|
||||||
world[key] = val
|
|
||||||
|
|
||||||
world['name'] = worldpath
|
|
||||||
world.update(origs)
|
|
||||||
renders[worldpath] = world
|
|
||||||
return renders
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# bootstrap the logger with defaults
|
# bootstrap the logger with defaults
|
||||||
configure_logger()
|
configure_logger()
|
||||||
@@ -249,42 +211,56 @@ dir but you forgot to put quotes around the directory, since it contains spaces.
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
#########################################################################
|
#########################################################################
|
||||||
# These two blocks of code unify config-file mode and command-line mode.
|
# These two halfs of this if statement unify config-file mode and
|
||||||
# When the blocks have exited, they are expected to have set the following
|
# command-line mode.
|
||||||
# vars:
|
mw_parser = configParser.MultiWorldParser()
|
||||||
# destdir - the output directory
|
|
||||||
# renders - the dict heirarchy
|
|
||||||
if not options.config:
|
if not options.config:
|
||||||
# No config file mode.
|
# No config file mode.
|
||||||
worldpath, destdir = map(os.path.expanduser, args)
|
worldpath, destdir = map(os.path.expanduser, args)
|
||||||
logging.debug("Using %r as the world directory", worldpath)
|
logging.debug("Using %r as the world directory", worldpath)
|
||||||
logging.debug("Using %r as the output directory", destdir)
|
logging.debug("Using %r as the output directory", destdir)
|
||||||
|
|
||||||
renders = build_fake_settings(worldpath)
|
mw_parser.set_config_item("world", {'world': worldpath})
|
||||||
|
mw_parser.set_config_item("outputdir", destdir)
|
||||||
|
# Now for some good defaults
|
||||||
|
mw_parser.set_config_item("render", {'world': {
|
||||||
|
'worldname': 'world',
|
||||||
|
'title': 'Overviewer Render',
|
||||||
|
'rendermode': 'normal',
|
||||||
|
}})
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Parse the config file
|
# Parse the config file
|
||||||
mw_parser = configParser.MultiWorldParser(options.config)
|
mw_parser = configParser.MultiWorldParser()
|
||||||
mw_parser.parse()
|
mw_parser.parse(options.config)
|
||||||
try:
|
|
||||||
mw_parser.validate()
|
|
||||||
except Exception:
|
|
||||||
logging.exception("Please investigate these errors in settings.py then try running Overviewer again")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
destdir = mw_parser.outputdir
|
config = mw_parser.get_validated_config()
|
||||||
except AttributeError:
|
except Exception:
|
||||||
# Will get caught by the error check just below
|
logging.exception("An error was encountered with your configuration. See the info below.")
|
||||||
logging.debug("Attribute error while getting the outputdir from the config file. Will error in just a sec")
|
return 1
|
||||||
destdir = ""
|
|
||||||
else:
|
|
||||||
logging.debug("outputdir from parser: %r", destdir)
|
|
||||||
|
|
||||||
renders = mw_parser.get_render_things()
|
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
# Final validation and creation of the destination directory
|
# Final validation steps and creation of the destination directory
|
||||||
|
if not config['render']:
|
||||||
|
logging.error("You must specify at least one render in your config file. See the docs if you're having trouble")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
for rname, render in config['render'].iteritems():
|
||||||
|
# Convert render['worldname'] to the world path, and store the original
|
||||||
|
# in render['worldname_orig']
|
||||||
|
try:
|
||||||
|
worldpath = config['world'][render['worldname']]
|
||||||
|
except KeyError:
|
||||||
|
logging.error("Render %s's world is '%s', but I could not find a corresponding entry in the worlds dictionary.",
|
||||||
|
rname, render['worldname'])
|
||||||
|
return 1
|
||||||
|
render['worldname_orig'] = render['worldname']
|
||||||
|
render['worldname'] = worldpath
|
||||||
|
|
||||||
|
destdir = config['outputdir']
|
||||||
if not destdir:
|
if not destdir:
|
||||||
logging.error("You must specify the output directory in your config file.")
|
logging.error("You must specify the output directory in your config file.")
|
||||||
logging.error("e.g. outputdir = '/path/to/outputdir'")
|
logging.error("e.g. outputdir = '/path/to/outputdir'")
|
||||||
@@ -313,6 +289,7 @@ dir but you forgot to put quotes around the directory, since it contains spaces.
|
|||||||
# same for textures
|
# same for textures
|
||||||
texcache = {}
|
texcache = {}
|
||||||
|
|
||||||
|
renders = config['render']
|
||||||
for render_name, render in renders.iteritems():
|
for render_name, render in renders.iteritems():
|
||||||
logging.debug("Found the following render thing: %r", render)
|
logging.debug("Found the following render thing: %r", render)
|
||||||
|
|
||||||
@@ -348,6 +325,7 @@ dir but you forgot to put quotes around the directory, since it contains spaces.
|
|||||||
os.mkdir(tileset_dir)
|
os.mkdir(tileset_dir)
|
||||||
|
|
||||||
# only pass to the TileSet the options it really cares about
|
# only pass to the TileSet the options it really cares about
|
||||||
|
render['name'] = render_name # perhaps a hack. This is stored here for the asset manager
|
||||||
tileSetOpts = util.dict_subset(render, ["name", "imgformat", "renderchecks", "rerenderprob", "bgcolor", "imgquality", "optimizeimg", "rendermode", "worldname_orig", "title", "dimension"])
|
tileSetOpts = util.dict_subset(render, ["name", "imgformat", "renderchecks", "rerenderprob", "bgcolor", "imgquality", "optimizeimg", "rendermode", "worldname_orig", "title", "dimension"])
|
||||||
tset = tileset.TileSet(rset, assetMrg, tex, tileSetOpts, tileset_dir)
|
tset = tileset.TileSet(rset, assetMrg, tex, tileSetOpts, tileset_dir)
|
||||||
tilesets.append(tset)
|
tilesets.append(tset)
|
||||||
|
|||||||
@@ -4,103 +4,72 @@ import os.path
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import settingsDefinition
|
import settingsDefinition
|
||||||
|
import settingsValidators
|
||||||
|
|
||||||
class MultiWorldParser(object):
|
class MultiWorldParser(object):
|
||||||
"""A class that is used to parse a settings.py file. It should replace
|
"""A class that is used to parse a settings.py file.
|
||||||
ConfigOptionParser class above."""
|
|
||||||
|
This class's job is to compile and validate the configuration settings for
|
||||||
|
a set of renders. It can read in configuration from the given file with the
|
||||||
|
parse() method, and one can set configuration options directly with the
|
||||||
|
set_config_item() method.
|
||||||
|
|
||||||
def __init__(self, settings):
|
get_validated_config() validates and returns the validated config
|
||||||
"""Settings is a path to a settings.py file"""
|
"""
|
||||||
if not os.path.exists(settings) and not os.path.isfile(settings):
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Initialize this parser object"""
|
||||||
|
# This maps config names to their values
|
||||||
|
self._config_state = {}
|
||||||
|
|
||||||
|
# Scan the settings definition and build the config state heirarchy.
|
||||||
|
# Also go ahead and set default values for non-required settings.
|
||||||
|
# This maps setting names to their values as given in
|
||||||
|
# settingsDefinition.py
|
||||||
|
self._settings = {}
|
||||||
|
for settingname in settingsDefinition.__all__:
|
||||||
|
setting = getattr(settingsDefinition, settingname)
|
||||||
|
assert isinstance(setting, settingsValidators.Setting)
|
||||||
|
|
||||||
|
self._settings[settingname] = setting
|
||||||
|
|
||||||
|
if not setting.required:
|
||||||
|
self._config_state[settingname] = setting.default
|
||||||
|
|
||||||
|
def set_config_item(self, itemname, itemvalue):
|
||||||
|
self._config_state[itemname] = itemvalue
|
||||||
|
|
||||||
|
def parse(self, settings_file):
|
||||||
|
"""Reads in the named file and parses it, storing the results in an
|
||||||
|
internal state awating to be validated and returned upon call to
|
||||||
|
get_render_settings()
|
||||||
|
|
||||||
|
"""
|
||||||
|
if not os.path.exists(settings_file) and not os.path.isfile(settings_file):
|
||||||
raise ValueError("bad settings file")
|
raise ValueError("bad settings file")
|
||||||
|
|
||||||
self.settings_file = settings
|
# The global environment should be the rendermode module, so the config
|
||||||
|
# file has access to those resources.
|
||||||
def parse(self):
|
|
||||||
# settingsDefinition.py defines what types of things you can put in a configfile
|
|
||||||
# anything in settingsDefinitino that is a dict with a "type" key is a definiton
|
|
||||||
defDicts = [x for x in dir(settingsDefinition) if type(getattr(settingsDefinition,x)) == dict and getattr(settingsDefinition,x).has_key("type") and x != "__builtins__"]
|
|
||||||
|
|
||||||
glob = dict()
|
|
||||||
for name in defDicts:
|
|
||||||
d = getattr(settingsDefinition, name)
|
|
||||||
glob[name] = d['type']()
|
|
||||||
|
|
||||||
import rendermodes
|
import rendermodes
|
||||||
loc=dict()
|
|
||||||
for thing in dir(rendermodes):
|
|
||||||
thething = getattr(rendermodes, thing)
|
|
||||||
if isinstance(thething, type) and issubclass(thething, rendermodes.RenderPrimitive):
|
|
||||||
loc[thing] = thething
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
execfile(self.settings_file, glob, loc)
|
execfile(settings_file, rendermodes.__dict__, self._config_state)
|
||||||
# delete the builtins, we don't need it
|
|
||||||
del glob['__builtins__']
|
|
||||||
|
|
||||||
except NameError, ex:
|
except NameError, ex:
|
||||||
import traceback
|
logging.exception("Error parsing %s. Please check the trackback for more info" % settings_file)
|
||||||
traceback.print_exc()
|
|
||||||
logging.error("Error parsing %s. Please check the trackback above" % self.settings_file)
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
except SyntaxError, ex:
|
except SyntaxError, ex:
|
||||||
import traceback
|
logging.exception("Error parsing %s. Please check the trackback for more info" % self.settings_file)
|
||||||
traceback.print_exc()
|
|
||||||
tb = sys.exc_info()[2]
|
|
||||||
#print tb.tb_frame.f_code.co_filename
|
|
||||||
logging.error("Error parsing %s. Please check the trackback above" % self.settings_file)
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
for name in defDicts:
|
def get_validated_config(self):
|
||||||
setattr(self, name, glob[name])
|
"""Validate and return the configuration"""
|
||||||
del glob[name]
|
# Okay, this is okay, isn't it? We're going to create the validation
|
||||||
|
# routine right here, right now. I hope this works!
|
||||||
|
validator = settingsValidators.make_configdictvalidator(self._settings)
|
||||||
# seed with the Overviewer defaults, then update with the user defaults
|
# Woah. What just happened? No. WAIT, WHAT ARE YOU...
|
||||||
self.defaults = dict()
|
validated_config = validator(self._config_state)
|
||||||
for name in defDicts:
|
# WHAT HAVE YOU DONE?
|
||||||
d = getattr(settingsDefinition, name)
|
return validated_config
|
||||||
if d['type'] == dict and d['valuetype'] == dict:
|
# WHAT HAVE YOU DOOOOOOOOOOONE????
|
||||||
for key in d['values']:
|
|
||||||
option = d['values'][key]
|
|
||||||
if option.has_key("default"):
|
|
||||||
self.defaults[key] = option["default"]
|
|
||||||
|
|
||||||
|
|
||||||
self.defaults.update(glob)
|
|
||||||
|
|
||||||
|
|
||||||
def validate(self):
|
|
||||||
|
|
||||||
|
|
||||||
origs = dict()
|
|
||||||
|
|
||||||
for worldname in self.render:
|
|
||||||
world = dict()
|
|
||||||
world.update(self.defaults)
|
|
||||||
world.update(self.render[worldname])
|
|
||||||
|
|
||||||
for key in world:
|
|
||||||
if key not in settingsDefinition.render['values']:
|
|
||||||
logging.warning("%r is not a known setting", key)
|
|
||||||
continue
|
|
||||||
|
|
||||||
definition = settingsDefinition.render['values'][key]
|
|
||||||
try:
|
|
||||||
val = definition['validator'](world[key], world = self.world)
|
|
||||||
if definition.get('save_orig', False):
|
|
||||||
origs[key + "_orig"] = world[key]
|
|
||||||
world[key] = val
|
|
||||||
except Exception as e:
|
|
||||||
logging.error("Error validating '%s' option in render definition for '%s':", key, worldname)
|
|
||||||
logging.error(e)
|
|
||||||
raise e
|
|
||||||
world['name'] = worldname
|
|
||||||
world.update(origs)
|
|
||||||
self.render[worldname] = world
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_render_things(self):
|
|
||||||
return self.render
|
|
||||||
|
|||||||
@@ -1,54 +1,73 @@
|
|||||||
# This file defines all of the things than can
|
# This file describes the format of the config file. Each item defined in this
|
||||||
# appear in a settings.py file, as well as the
|
# module is expected to appear in the same format in a settings file. The only
|
||||||
# function that can validate them
|
# difference is, instead of actual values for the settings, one is to use a
|
||||||
|
# Setting object. Here is its signature:
|
||||||
|
|
||||||
# the validator should raise an exception if there
|
# Setting(required, validator, default)
|
||||||
# is a problem parsing or validating the value.
|
|
||||||
# it should return the value to use (which gives
|
|
||||||
# the validator an oppertunity to cleanup/normalize/
|
|
||||||
# whaterver)
|
|
||||||
|
|
||||||
# if a setting is not required, the validator is
|
# required is a boolean indicating the user is required to provide this
|
||||||
# expected to return the default option
|
# setting. In this case, default is unused and can be set to anything (None is
|
||||||
|
# a good choice).
|
||||||
|
|
||||||
|
# validator is a callable that takes the provided value and returns a
|
||||||
|
# cleaned/normalized value to use. It should raise an exception if there is a
|
||||||
|
# problem parsing or validating the value given.
|
||||||
|
|
||||||
|
# default is used instead of the user-provided value in the event that required
|
||||||
|
# is false. It is passed into the validator just the same. If default is None
|
||||||
|
# and required is False, then that value is skipped entirely and will not
|
||||||
|
# appear in the resulting parsed options.
|
||||||
|
|
||||||
|
# The signature for validators is validator(value_given). Remember that the
|
||||||
|
# default is passed in as value_given in the event that required is False and
|
||||||
|
# default is not None.
|
||||||
|
|
||||||
|
# This file doesn't specify the format or even the type of the setting values,
|
||||||
|
# that is up to the validators used.
|
||||||
|
|
||||||
from settingsValidators import *
|
from settingsValidators import *
|
||||||
|
|
||||||
# note that all defaults go thought the validator
|
# This is the export list for this module. It defines which items defined in
|
||||||
render = {
|
# this module are recognized by the config parser. Don't forget to update this
|
||||||
"type": dict,
|
# if you add new items!
|
||||||
"valuetype": dict,
|
__all__ = ['render', 'world', 'outputdir']
|
||||||
"values": {
|
|
||||||
"worldname": dict(required=True, validator=validateWorldPath, save_orig=True),
|
# render is a dictionary mapping names to dicts describing the configuration
|
||||||
"dimension": dict(required=False, validator=validateDimension, default="default"),
|
# for that render. It is therefore set to a settings object with a dict
|
||||||
"title": dict(required=True, validator=validateStr),
|
# validator configured to validate keys as strings and values as... values are
|
||||||
"rendermode": dict(required=False, validator=validateRenderMode),
|
# set to validate as a "configdict", which is a dict mapping a set of strings
|
||||||
"northdirection": dict(required=False, validator=validateNorthDirection, default=0),
|
# to some value. the make_configdictvalidator function creates a validator to
|
||||||
"renderrange": dict(required=False, validator=validateRenderRange),
|
# use here configured with the given set of keys and Setting objects with their
|
||||||
"forcerender": dict(required=False, validator=validateBool),
|
# respective validators.
|
||||||
"stochasticrender": dict(required=False, validator=validateStochastic),
|
|
||||||
"imgformat": dict(required=False, validator=validateImgFormat, default="png"),
|
# Perhaps unintuitively, this is set to required=False. Of course, if no
|
||||||
"imgquality": dict(required=False, validator=validateImgQuality),
|
# renders are specified, this is an error. However, this is caught later on in
|
||||||
"bgcolor": dict(required=False, validator=validateBGColor, default="1a1a1a"),
|
# the code, and it also lets an empty dict get defined beforehand for the
|
||||||
"optimizeimg": dict(required=False, validator=validateOptImg, default=0),
|
# config file.
|
||||||
"nomarkers": dict(required=False, validator=validateBool),
|
render = Setting(required=False, default={},
|
||||||
"texturepath": dict(required=False, validator=validateTexturePath),
|
validator=dictValidator(validateStr, make_configdictvalidator(
|
||||||
"renderchecks": dict(required=False, validator=validateInt, default=0),
|
{
|
||||||
"rerenderprob": dict(required=False, validator=validateFloat, default=0),
|
"worldname": Setting(required=True, validator=validateStr, default=None),
|
||||||
}
|
"dimension": Setting(required=False, validator=validateDimension, default="default"),
|
||||||
|
"title": Setting(required=True, validator=validateStr, default=None),
|
||||||
|
"rendermode": Setting(required=False, validator=validateRenderMode, default=None),
|
||||||
|
"northdirection": Setting(required=False, validator=validateNorthDirection, default=0),
|
||||||
|
"renderrange": Setting(required=False, validator=validateRenderRange, default=None),
|
||||||
|
"forcerender": Setting(required=False, validator=validateBool, default=None),
|
||||||
|
"stochasticrender": Setting(required=False, validator=validateStochastic, default=None),
|
||||||
|
"imgformat": Setting(required=False, validator=validateImgFormat, default="png"),
|
||||||
|
"imgquality": Setting(required=False, validator=validateImgQuality, default=None),
|
||||||
|
"bgcolor": Setting(required=False, validator=validateBGColor, default="1a1a1a"),
|
||||||
|
"optimizeimg": Setting(required=False, validator=validateOptImg, default=0),
|
||||||
|
"nomarkers": Setting(required=False, validator=validateBool, default=None),
|
||||||
|
"texturepath": Setting(required=False, validator=validateTexturePath, default=None),
|
||||||
|
"renderchecks": Setting(required=False, validator=validateInt, default=0),
|
||||||
|
"rerenderprob": Setting(required=False, validator=validateFloat, default=0),
|
||||||
}
|
}
|
||||||
|
)))
|
||||||
|
|
||||||
world = {
|
# The world dict, mapping world names to world paths
|
||||||
"type": dict,
|
world = Setting(required=False, validator=dictValidator(validateStr, validateWorldPath), default={})
|
||||||
"valuetype": str,
|
|
||||||
"value": dict(validator=validateStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
outputdir = {
|
|
||||||
"type": str,
|
|
||||||
"value": dict(validator=validateOutputDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
#defines the values for each member of the world dict
|
|
||||||
#world = dict(require
|
|
||||||
|
|
||||||
|
outputdir = Setting(required=True, validator=validateOutputDir, default=None)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# see settingsDefinition.py
|
# see settingsDefinition.py
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
import rendermodes
|
import rendermodes
|
||||||
from world import UPPER_LEFT, UPPER_RIGHT, LOWER_LEFT, LOWER_RIGHT
|
from world import UPPER_LEFT, UPPER_RIGHT, LOWER_LEFT, LOWER_RIGHT
|
||||||
@@ -8,20 +9,34 @@ from world import UPPER_LEFT, UPPER_RIGHT, LOWER_LEFT, LOWER_RIGHT
|
|||||||
class ValidationException(Exception):
|
class ValidationException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def validateWorldPath(name, **kwargs):
|
Setting = namedtuple("Setting", [
|
||||||
world = kwargs.get('world', dict())
|
'required',
|
||||||
if name not in world.keys():
|
'validator',
|
||||||
raise ValidationException("bad world name")
|
'default',
|
||||||
abs_path = os.path.abspath(world[name])
|
])
|
||||||
|
|
||||||
|
def validateWorldPath(worldpath):
|
||||||
|
abs_path = os.path.abspath(worldpath)
|
||||||
if not os.path.exists(os.path.join(abs_path, "level.dat")):
|
if not os.path.exists(os.path.join(abs_path, "level.dat")):
|
||||||
raise ValidationException("No level.dat file in %r. Check the path for world %r" % (abs_path, name))
|
raise ValidationException("No level.dat file in %r. Are you sure you have the right path?" % (abs_path,))
|
||||||
return abs_path
|
return abs_path
|
||||||
|
|
||||||
|
|
||||||
def validateRenderMode(mode, **kwargs):
|
def validateRenderMode(mode):
|
||||||
# make sure that mode is a list of things that are all rendermode primative
|
# make sure that mode is a list of things that are all rendermode primative
|
||||||
if type(mode) != list:
|
if isinstance(mode, str):
|
||||||
|
# Try and find an item named "mode" in the rendermodes module
|
||||||
|
try:
|
||||||
|
mode = getattr(rendermodes, mode)
|
||||||
|
except AttributeError:
|
||||||
|
raise ValidationException("You must specify a valid rendermode, not '%s'. See the docs for valid rendermodes." % mode)
|
||||||
|
|
||||||
|
if isinstance(mode, rendermodes.RenderPrimitive):
|
||||||
|
mode = [mode]
|
||||||
|
|
||||||
|
if not isinstance(mode, list):
|
||||||
raise ValidationException("%r is not a valid list of rendermodes. It should be a list"% mode)
|
raise ValidationException("%r is not a valid list of rendermodes. It should be a list"% mode)
|
||||||
|
|
||||||
for m in mode:
|
for m in mode:
|
||||||
if not isinstance(m, rendermodes.RenderPrimitive):
|
if not isinstance(m, rendermodes.RenderPrimitive):
|
||||||
raise ValidationException("%r is not a valid rendermode primitive." % m)
|
raise ValidationException("%r is not a valid rendermode primitive." % m)
|
||||||
@@ -29,7 +44,7 @@ def validateRenderMode(mode, **kwargs):
|
|||||||
|
|
||||||
return mode
|
return mode
|
||||||
|
|
||||||
def validateNorthDirection(direction, **kwargs):
|
def validateNorthDirection(direction):
|
||||||
# normalize to integers
|
# normalize to integers
|
||||||
intdir = 0 #default
|
intdir = 0 #default
|
||||||
if type(direction) == int:
|
if type(direction) == int:
|
||||||
@@ -43,28 +58,28 @@ def validateNorthDirection(direction, **kwargs):
|
|||||||
raise ValidationException("%r is not a valid north direction" % direction)
|
raise ValidationException("%r is not a valid north direction" % direction)
|
||||||
return intdir
|
return intdir
|
||||||
|
|
||||||
def validateRenderRange(r, **kwargs):
|
def validateRenderRange(r):
|
||||||
raise NotImplementedError("render range")
|
raise NotImplementedError("render range")
|
||||||
|
|
||||||
def validateStochastic(s, **kwargs):
|
def validateStochastic(s):
|
||||||
val = float(s)
|
val = float(s)
|
||||||
if val < 0 or val > 1:
|
if val < 0 or val > 1:
|
||||||
raise ValidationException("%r is not a valid stochastic value. Should be between 0.0 and 1.0" % s)
|
raise ValidationException("%r is not a valid stochastic value. Should be between 0.0 and 1.0" % s)
|
||||||
return val
|
return val
|
||||||
|
|
||||||
def validateImgFormat(fmt, **kwargs):
|
def validateImgFormat(fmt):
|
||||||
if fmt not in ("png", "jpg", "jpeg"):
|
if fmt not in ("png", "jpg", "jpeg"):
|
||||||
raise ValidationException("%r is not a valid image format" % fmt)
|
raise ValidationException("%r is not a valid image format" % fmt)
|
||||||
if fmt == "jpeg": fmt = "jpg"
|
if fmt == "jpeg": fmt = "jpg"
|
||||||
return fmt
|
return fmt
|
||||||
|
|
||||||
def validateImgQuality(qual, **kwargs):
|
def validateImgQuality(qual):
|
||||||
intqual = int(qual)
|
intqual = int(qual)
|
||||||
if (intqual < 0 or intqual > 100):
|
if (intqual < 0 or intqual > 100):
|
||||||
raise ValidationException("%r is not a valid image quality" % intqual)
|
raise ValidationException("%r is not a valid image quality" % intqual)
|
||||||
return intqual
|
return intqual
|
||||||
|
|
||||||
def validateBGColor(color, **kwargs):
|
def validateBGColor(color):
|
||||||
"""BG color must be an HTML color, with an option leading # (hash symbol)
|
"""BG color must be an HTML color, with an option leading # (hash symbol)
|
||||||
returns an (r,b,g) 3-tuple
|
returns an (r,b,g) 3-tuple
|
||||||
"""
|
"""
|
||||||
@@ -86,32 +101,80 @@ def validateBGColor(color, **kwargs):
|
|||||||
return color
|
return color
|
||||||
|
|
||||||
|
|
||||||
def validateOptImg(opt, **kwargs):
|
def validateOptImg(opt):
|
||||||
return bool(opt)
|
return bool(opt)
|
||||||
|
|
||||||
def validateTexturePath(path, **kwargs):
|
def validateTexturePath(path):
|
||||||
# Expand user dir in directories strings
|
# Expand user dir in directories strings
|
||||||
path = os.path.expanduser(path)
|
path = os.path.expanduser(path)
|
||||||
# TODO assert this path exists?
|
# TODO assert this path exists?
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def validateBool(b, **kwargs):
|
def validateBool(b):
|
||||||
return bool(b)
|
return bool(b)
|
||||||
|
|
||||||
def validateFloat(f, **kwargs):
|
def validateFloat(f):
|
||||||
return float(f)
|
return float(f)
|
||||||
|
|
||||||
def validateInt(i, **kwargs):
|
def validateInt(i):
|
||||||
return int(i)
|
return int(i)
|
||||||
|
|
||||||
def validateStr(s, **kwargs):
|
def validateStr(s):
|
||||||
return str(s)
|
return str(s)
|
||||||
|
|
||||||
def validateDimension(d, **kwargs):
|
def validateDimension(d):
|
||||||
if d in ["nether", "overworld", "end", "default"]:
|
if d in ["nether", "overworld", "end", "default"]:
|
||||||
return d
|
return d
|
||||||
raise ValidationException("%r is not a valid dimension" % d)
|
raise ValidationException("%r is not a valid dimension" % d)
|
||||||
|
|
||||||
def validateOutputDir(d, **kwargs):
|
def validateOutputDir(d):
|
||||||
return os.path.abs(d)
|
if not d.strip():
|
||||||
|
raise ValidationException("You must specify a valid output directory")
|
||||||
|
return os.path.abspath(d)
|
||||||
|
|
||||||
|
def dictValidator(keyvalidator, valuevalidator):
|
||||||
|
"""Compose a dict validator by validating each key/value combination with
|
||||||
|
the given key validator and value validator
|
||||||
|
|
||||||
|
"""
|
||||||
|
def v(d):
|
||||||
|
newd = {}
|
||||||
|
for key, value in d.iteritems():
|
||||||
|
newd[keyvalidator(key)] = valuevalidator(value)
|
||||||
|
return newd
|
||||||
|
return v
|
||||||
|
|
||||||
|
def make_configdictvalidator(config):
|
||||||
|
"""Okay, stay with me here, this may get confusing. This function returns a
|
||||||
|
validator that validates a "configdict". This is a term I just made up to
|
||||||
|
refer to a dict where keys are strings and values are something. The
|
||||||
|
argument to /this/ function is a dictionary mapping those key names to
|
||||||
|
Setting objects. When the validator validates, it calls all the appropriate
|
||||||
|
validators to validate each item in the configdict.
|
||||||
|
|
||||||
|
I hope that makes sense.
|
||||||
|
|
||||||
|
"""
|
||||||
|
def configDictValidator(d):
|
||||||
|
newdict = {}
|
||||||
|
for configkey, configsetting in config.iteritems():
|
||||||
|
if configkey in d:
|
||||||
|
# This key /was/ specified in the user's dict. Make sure it validates.
|
||||||
|
newdict[configkey] = configsetting.validator(d[configkey])
|
||||||
|
else:
|
||||||
|
# The user did not give us this key. If it's required, send up
|
||||||
|
# an error. Otherwise, just return the default.
|
||||||
|
if configsetting.required:
|
||||||
|
raise ValidationException("Required key '%s' was not specified" % configkey)
|
||||||
|
elif configsetting.default is not None:
|
||||||
|
newdict[configkey] = configsetting.validator(configsetting.default)
|
||||||
|
|
||||||
|
# Now that all the defined keys have been accounted for, check to make
|
||||||
|
# sure any unauthorized keys were not specified.
|
||||||
|
for key in d.iterkeys():
|
||||||
|
if key not in config:
|
||||||
|
raise ValidationException("'%s' is not a configuration item" % key)
|
||||||
|
return newdict
|
||||||
|
|
||||||
|
return configDictValidator
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
world['test'] = "test/data/settings/test_world"
|
world['test'] = "test/data/settings/test_world"
|
||||||
worldname = 'test'
|
|
||||||
|
|
||||||
rendermode = "normal"
|
render["myworld"] = {
|
||||||
|
"title": "myworld title",
|
||||||
render["world"] = {
|
"worldname": "test",
|
||||||
"rendermode": "normal",
|
"rendermode": normal,
|
||||||
"northdirection": ["upper-left"],
|
"northdirection": "upper-left",
|
||||||
}
|
}
|
||||||
|
|
||||||
render["otherworld"] = {
|
render["otherworld"] = {
|
||||||
"rendermode": "foo",
|
"title": "otherworld title",
|
||||||
|
"worldname": "test",
|
||||||
|
"rendermode": normal,
|
||||||
"bgcolor": "#ffffff"
|
"bgcolor": "#ffffff"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outputdir = "/tmp/fictional/outputdir"
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
bgcolor="#000000"
|
|
||||||
|
|
||||||
world['test'] = "test/data/settings/test_world"
|
|
||||||
|
|
||||||
render["world"] = {
|
|
||||||
"worldname": "test",
|
|
||||||
"bgcolor":"ffff"
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,9 @@ world['test'] = "test/data/settings/test_world"
|
|||||||
|
|
||||||
render["world"] = {
|
render["world"] = {
|
||||||
"worldname": "test",
|
"worldname": "test",
|
||||||
|
"title": "myworld title",
|
||||||
"rendermode": "bad_rendermode",
|
"rendermode": "bad_rendermode",
|
||||||
"northdirection": ["upper-left"],
|
"northdirection": "upper-left",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outputdir = "/tmp/fictional/outputdir"
|
||||||
|
|||||||
@@ -3,32 +3,81 @@ import unittest
|
|||||||
from overviewer_core import configParser
|
from overviewer_core import configParser
|
||||||
from overviewer_core.settingsValidators import ValidationException
|
from overviewer_core.settingsValidators import ValidationException
|
||||||
|
|
||||||
|
from overviewer_core import world
|
||||||
|
from overviewer_core import rendermodes
|
||||||
|
|
||||||
class SettingsTest(unittest.TestCase):
|
class SettingsTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.s = configParser.MultiWorldParser()
|
||||||
|
|
||||||
def test_missing(self):
|
def test_missing(self):
|
||||||
"Validates that a non-existant settings.py causes an exception"
|
"Validates that a non-existant settings.py causes an exception"
|
||||||
self.assertRaises(ValueError, configParser.MultiWorldParser, "doesnotexist.py")
|
self.assertRaises(ValueError, self.s.parse, "doesnotexist.py")
|
||||||
|
|
||||||
def test_existing_file(self):
|
def test_existing_file(self):
|
||||||
s = configParser.MultiWorldParser("test/data/settings/settings_test_1.py")
|
self.s.parse("test/data/settings/settings_test_1.py")
|
||||||
s.parse()
|
things = self.s.get_validated_config()
|
||||||
s.validate()
|
# no exceptions so far. that's a good thing
|
||||||
things = s.get_render_things()
|
|
||||||
# no exceptions so far. that's good
|
# Test the default
|
||||||
self.assertEquals(things['world']['bgcolor'], (26,26,26,0))
|
self.assertEquals(things['render']['myworld']['bgcolor'], (26,26,26,0))
|
||||||
self.assertEquals(things['otherworld']['bgcolor'], (255,255,255,0))
|
|
||||||
|
# Test a non-default
|
||||||
|
self.assertEquals(things['render']['otherworld']['bgcolor'], (255,255,255,0))
|
||||||
|
|
||||||
|
self.assertEquals(things['render']['myworld']['northdirection'],
|
||||||
|
world.UPPER_LEFT)
|
||||||
|
|
||||||
def test_rendermode_validation(self):
|
def test_rendermode_validation(self):
|
||||||
s = configParser.MultiWorldParser("test/data/settings/settings_test_rendermode.py")
|
self.s.parse("test/data/settings/settings_test_rendermode.py")
|
||||||
s.parse()
|
|
||||||
|
|
||||||
self.assertRaises(ValidationException,s.validate)
|
self.assertRaises(ValidationException,self.s.get_validated_config)
|
||||||
|
|
||||||
def test_bgcolor_validation(self):
|
def test_manual(self):
|
||||||
s = configParser.MultiWorldParser("test/data/settings/settings_test_bgcolor.py")
|
"""Tests that manually setting the config parser works, you don't have
|
||||||
s.parse()
|
to do it from a file
|
||||||
|
|
||||||
|
"""
|
||||||
|
fromfile = configParser.MultiWorldParser()
|
||||||
|
fromfile.parse("test/data/settings/settings_test_1.py")
|
||||||
|
|
||||||
self.assertRaises(ValidationException, s.validate)
|
self.s.set_config_item("world", {
|
||||||
|
'test': "test/data/settings/test_world",
|
||||||
|
})
|
||||||
|
self.s.set_config_item("render", {
|
||||||
|
"myworld": {
|
||||||
|
"title": "myworld title",
|
||||||
|
"worldname": "test",
|
||||||
|
"rendermode": rendermodes.normal,
|
||||||
|
"northdirection": "upper-left",
|
||||||
|
},
|
||||||
|
|
||||||
|
"otherworld": {
|
||||||
|
"title": "otherworld title",
|
||||||
|
"worldname": "test",
|
||||||
|
"rendermode": rendermodes.normal,
|
||||||
|
"bgcolor": "#ffffff"
|
||||||
|
},
|
||||||
|
})
|
||||||
|
self.s.set_config_item("outputdir", "/tmp/fictional/outputdir")
|
||||||
|
self.assertEquals(fromfile.get_validated_config(), self.s.get_validated_config())
|
||||||
|
|
||||||
|
def test_rendermode_string(self):
|
||||||
|
self.s.set_config_item("world", {
|
||||||
|
'test': "test/data/settings/test_world",
|
||||||
|
})
|
||||||
|
self.s.set_config_item("outputdir", "/tmp/fictional/outputdir")
|
||||||
|
self.s.set_config_item("render", {
|
||||||
|
"myworld": {
|
||||||
|
"title": "myworld title",
|
||||||
|
"worldname": "test",
|
||||||
|
"rendermode": "normal",
|
||||||
|
"northdirection": "upper-left",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
p = self.s.get_validated_config()
|
||||||
|
self.assertEquals(p['render']['myworld']['rendermode'], rendermodes.normal)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user