Source code for emsm.core.conf

#!/usr/bin/env python3

# The MIT License (MIT)
#
# Copyright (c) 2014-2018 <see AUTHORS.txt>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.


# Modules
# ------------------------------------------------

# std
import os
import logging
import configparser

# local
from .worlds import WorldWrapper


# Backward compatibility
# ------------------------------------------------

try:
    FileNotFoundError
except NameError:
    FileNotFoundError = OSError


# Data
# ------------------------------------------------

__all__ = [
    "ConfigParser",
    "MainConfiguration",
    "ServerConfiguration",
    "WorldConfiguration",
    "Configuration"
    ]

log = logging.getLogger(__file__)


# Classes
# ------------------------------------------------

[docs]class ConfigParser(configparser.ConfigParser): """ Extends the standard Python :class:`configparser.ConfigParser` by some useful methods. :param str path: The path to the configuration file. This file is used, when you call :meth:`read` or :meth:`write`. """ def __init__(self, path): """ """ super().__init__( allow_no_value = False, strict = True, empty_lines_in_values = False, interpolation=configparser.ExtendedInterpolation() ) self._path = path return None
[docs] def epilog(self): """ Returns a comment, which is written at the begin of a configuration file. """ return ""
[docs] def path(self): """ Returns the path of the configuration file. """ return self._path
[docs] def read(self): """ Reads the configuration from :meth:`path`. """ try: with open(self._path, "r") as file: super().read_file(file) except (FileNotFoundError, IOError): pass return None
[docs] def write(self): """ Writes the configuration into :meth:`path`. """ # Get the comment prefix. comment_prefix = self._comment_prefixes[0] # Convert the EPILOG to comment lines. epilog = self.epilog().split("\n") epilog = [comment_prefix + " " + line for line in epilog] epilog = "\n".join(epilog) + "\n\n" # Write the configuration into the file. with open(self._path, "w") as file: file.write(epilog) super().write(file) return None
[docs] def remove(self): """ Removes the configuration file from the file system. """ try: os.remove(self.path()) except FileNotFoundError: log.error( "could not remove the '%s' configuration file.", self.path() ) return None
[docs]class MainConfiguration(ConfigParser): """ Handles the :file:`main.conf` configuration file. This file includes the configuration for the EMSM Application and the plugins. The EMSM owns the ``[emsm]`` section and each plugin has its own section with the plugin name. .. code-block:: ini [emsm] user = minecraft timeout = 0 screenrc = [backups] include_server = ... # ... """ def __init__(self, path): """ """ super().__init__(path) # Add the default configuration for the EMSM. self.add_section("emsm") self["emsm"]["user"] = "minecraft" self["emsm"]["timeout"] = "0" self["emsm"]["screenrc"] = "" return None
[docs] def epilog(self): epilog = "\n".join([ "This file contains the settings for the EMSM core application and", "the plugins.", "", "The section of the EMSM looks like this per default:", "", "[emsm]", "user = minecraft", "timeout = -1", "screenrc = ", "", "The configuration section of each plugin is titled with the plugins", "name.", ]) return epilog
[docs]class ServerConfiguration(ConfigParser): """ Handles the *server.conf* configuration file, which allows the user to overwrite the default EMSM settings for a server wrapper like the *url* or the *start command*. .. seealso:: * :meth:`emsm.core.server.BaseServerWrapper.conf` * :meth:`emsm.core.conf.WorldConfiguration` """
[docs] def epilog(self): epilog = "\n".join([ "[server name]", "url = string", "start_command = string", "", "The EMSM comes with tested default settings for each server.", "so you should only overwrite these values, if you have to.", ]) return epilog
[docs]class WorldConfiguration(ConfigParser): """ Handles a configuration file for *one* world and allows the user to set custom configuration values for each plugin, server and the EMSM. :arg str path: """ def __init__(self, path): """ """ super().__init__(path) # Add the default options for the world. self.add_section("world") self["world"]["stop_timeout"] = "10" self["world"]["stop_delay"] = "5" self["world"]["stop_message"] = "The server is going down.\n"\ "Hope to see you soon." self["world"]["server"] = "vanilla 1.13" return None
[docs] def epilog(self): filename = os.path.basename(self.path()) world_name = filename[:-len(".world.conf")] epilog = "\n".join([ "This configuration file contains the configuration for the world", "", " **{world_name}**", "", "This file can be used to override global configuration values in ", "the *server.conf* and *main.conf* configuration files.", "", "[world]", "stop_timeout = int", "stop_message = string", "stop_delay = int", "server = a server in server.conf", "", "Custom options for the backups plugin:", "", "[plugin:backups]", "archive_format = bztar", "max_storage_size = 30", "", "Custom options for the vanilla 1.8 server:", "", "[server:vanilla 1.8]", "start_command = java -Xms512m -Xmx1G -jar {{server_exe}} nogui", "" ]).format(world_name=world_name) return epilog
[docs]class Configuration(object): """ Manages all configuration files of an EMSM application object. .. seealso:: * :meth:`emsm.core.application.Application.conf` * :meth:`emsm.core.paths.Pathsystem.conf_dir` """ def __init__(self, app): """ """ self._app = app self._dir = app.paths().conf() self._main = MainConfiguration(os.path.join(self._dir, "main.conf")) self._server = ServerConfiguration(os.path.join(self._dir, "server.conf")) # Load all *.world.conf configuration files # We ignore files, that start with an underscore. self._worlds = dict() if os.path.exists(self._dir): for name in os.listdir(self._dir): path = os.path.join(self._dir, name) if not os.path.isfile(path): continue if not name.endswith(".world.conf"): continue if name.startswith("_"): continue world_name = name[:-len(".world.conf")] self._worlds[world_name] = WorldConfiguration(path) WorldWrapper.world_uninstalled.connect(self.__remove_world) return None def __remove_world(self, world): """ Removes the :class:`WorldConfiguration` of *world* from the internal map. """ if world.name() in self._worlds: del self._worlds[world.name()] return None
[docs] def main(self): """ Returns the :class:`MainConfiguration`. """ return self._main
[docs] def server(self): """ Returns the :class:`ServerConfiguration`. """ return self._server
[docs] def worlds(self): """ Returns a list with all :class:`WorldConfiguration` objects. """ return list(self._worlds.values())
[docs] def world(self, name): """ Returns the :class:`WorldConfiguration` for the world with the name *name* and ``None``, if there is not such a world. """ return self._worlds.get(name)
[docs] def list_worlds(self): """ Returns a list with the names of all worlds, for which a configuration file has been found. """ return list(self._worlds.keys())
[docs] def read(self): """ Reads all configration files. """ log.info("reading configuration ...") # Don't change the order! self._main.read() self._server.read() for conf in self._worlds.values(): conf.read() return None
[docs] def write(self): """ Saves all configuration values. """ log.info("writing configuration ...") self._main.write() self._server.write() for conf in self._worlds.values(): conf.write() return None