Skip to content

Commit

Permalink
Merge branch 'config_loc_v2' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
TeamSpen210 committed Dec 9, 2018
2 parents e37a9f8 + c367cc5 commit de6d7c4
Show file tree
Hide file tree
Showing 15 changed files with 593 additions and 207 deletions.
3 changes: 1 addition & 2 deletions src/BEE2.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@

DEFAULT_SETTINGS = {
'Directories': {
'palette': 'palettes/',
'package': 'packages/',
},
'General': {
Expand Down Expand Up @@ -108,7 +107,7 @@
LOGGER.info('Done!')

LOGGER.info('Loading Palettes...')
paletteLoader.load_palettes(GEN_OPTS['Directories']['palette'])
paletteLoader.load_palettes()
LOGGER.info('Done!')

# Check games for Portal 2's basemodui.txt file, so we can translate items.
Expand Down
121 changes: 100 additions & 21 deletions src/BEE2_config.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,75 @@
"""Modified version of ConfigParser that can be easily resaved.
"""Settings related logic for the application.
Functions can be registered with a name, which will be called to save/load
settings.
This also contains a version of ConfigParser that can be easily resaved.
It only saves if the values are modified.
Most functions are also altered to allow defaults instead of erroring.
"""
from configparser import ConfigParser, NoOptionError
from configparser import ConfigParser, NoOptionError, SectionProxy
from typing import Any, Mapping

from srctools import AtomicWriter, Property

import os
import utils
import srctools.logger


LOGGER = srctools.logger.get_logger(__name__)

# Functions for saving or loading application settings.
# Call with a block to load, or with no args to return the current
# values.
option_handler = utils.FuncLookup('OptionHandlers') # type: utils.FuncLookup


def get_curr_settings() -> Property:
"""Return a property tree defining the current options."""
props = Property('', [])

for opt_id, opt_func in option_handler.items():
opt_prop = opt_func() # type: Property
opt_prop.name = opt_id.title()
props.append(opt_prop)

return props


def apply_settings(props: Property):
"""Given a property tree, apply it to the widgets."""
for opt_prop in props:
try:
func = option_handler[opt_prop.name]
except KeyError:
LOGGER.warning('No handler for option type "{}"!', opt_prop.real_name)
else:
func(opt_prop)


def read_settings() -> None:
"""Read and apply the settings from disk."""
try:
file = open(utils.conf_location('config/config.vdf'), encoding='utf8')
except FileNotFoundError:
return
with file:
props = Property.parse(file)
apply_settings(props)


def write_settings() -> None:
"""Write the settings to disk."""
props = get_curr_settings()
props.name = None
with AtomicWriter(
str(utils.conf_location('config/config.vdf')),
is_bytes=False,
) as file:
for line in props.export():
file.write(line)


class ConfigFile(ConfigParser):
"""A version of ConfigParser which can easily save itself.
Expand All @@ -20,21 +79,40 @@ class ConfigFile(ConfigParser):
get_val, get_bool, and get_int are modified to return defaults instead
of erroring.
"""
def __init__(self, filename, root='../config', auto_load=True):
def __init__(
self,
filename: str,
*,
in_conf_folder: bool=True,
auto_load: bool=True,
) -> None:
"""Initialise the config file.
filename is the name of the config file, in the 'root' directory.
If auto_load is true, this file will immediately be read and parsed.
`filename` is the name of the config file, in the `root` directory.
If `auto_load` is true, this file will immediately be read and parsed.
If in_conf_folder is set, The folder is relative to the 'config/'
folder in the BEE2 folder.
"""
super().__init__()
self.filename = os.path.join(root, filename)
self.writer = srctools.AtomicWriter(self.filename)

self.has_changed = False

if auto_load:
self.load()
if filename is not None:
if in_conf_folder:
self.filename = utils.conf_location('config/' + filename)
else:
self.filename = filename

def load(self):
self.writer = AtomicWriter(self.filename)
self.has_changed = False

if auto_load:
self.load()
else:
self.filename = self.writer = None

def load(self) -> None:
"""Load config options from disk."""
if self.filename is None:
return

Expand All @@ -50,7 +128,7 @@ def load(self):
# We're not different to the file on disk..
self.has_changed = False

def save(self):
def save(self) -> None:
"""Write our values out to disk."""
LOGGER.info('Saving changes in config "{}"!', self.filename)
if self.filename is None:
Expand All @@ -60,12 +138,12 @@ def save(self):
self.write(conf)
self.has_changed = False

def save_check(self):
def save_check(self) -> None:
"""Check to see if we have different values, and save if needed."""
if self.has_changed:
self.save()

def set_defaults(self, def_settings):
def set_defaults(self, def_settings: Mapping[str, Mapping[str, Any]]) -> None:
"""Set the default values if the settings file has no values defined."""
for sect, values in def_settings.items():
if sect not in self:
Expand All @@ -89,15 +167,15 @@ def get_val(self, section: str, value: str, default: str) -> str:
self[section][value] = default
return default

def __getitem__(self, section):
def __getitem__(self, section: str) -> SectionProxy:
"""Allows setting/getting config[section][value]."""
try:
return super().__getitem__(section)
except KeyError:
self[section] = {}
return super().__getitem__(section)


def getboolean(self, section, value, default=False) -> bool:
def getboolean(self, section: str, value: str, default: bool=False) -> bool:
"""Get the value in the specified section, coercing to a Boolean.
If either does not exist, set to the default and return it.
Expand All @@ -114,7 +192,7 @@ def getboolean(self, section, value, default=False) -> bool:

get_bool = getboolean

def getint(self, section, value, default=0) -> int:
def getint(self, section: str, value: str, default: int=0) -> int:
"""Get the value in the specified section, coercing to a Integer.
If either does not exist, set to the default and return it.
Expand All @@ -130,15 +208,15 @@ def getint(self, section, value, default=0) -> int:

get_int = getint

def add_section(self, section):
def add_section(self, section: str) -> None:
self.has_changed = True
super().add_section(section)

def remove_section(self, section):
def remove_section(self, section: str) -> None:
self.has_changed = True
super().remove_section(section)

def set(self, section, option, value=None):
def set(self, section: str, option: str, value: str) -> None:
orig_val = self.get(section, option, fallback=None)
value = str(value)
if orig_val is None or orig_val != value:
Expand All @@ -149,6 +227,7 @@ def set(self, section, option, value=None):
remove_section.__doc__ = ConfigParser.remove_section.__doc__
set.__doc__ = ConfigParser.set.__doc__


# Define this here so app modules can easily access the config
# Don't load it though, since this is imported by VBSP too.
GEN_OPTS = ConfigFile('config.cfg', auto_load=False)
Loading

0 comments on commit de6d7c4

Please sign in to comment.