from os import sep, getcwd from os.path import join, exists, basename, dirname, expanduser from sys import argv, version_info from re import match from pprint import pformat if version_info[0] >= 3: from configparser import RawConfigParser else: from ConfigParser import RawConfigParser class Configuration(RawConfigParser): default_project_file_rel_path = "config" default_resource_paths = [".", "resource"] def __init__(self, project_file_rel_path=None, resource_path=None, type_declarations=None): RawConfigParser.__init__(self) self.project_file_rel_path = project_file_rel_path self.resource_path = resource_path self.modifiable = {} self.order = [] self.set_type_declarations(type_declarations) self.set_defaults() self.read_project_config_file() self.modify_defaults() self.print_debug(self) def set_type_declarations(self, type_declarations): if type_declarations is None: type_declarations = TypeDeclarations() self.type_declarations = type_declarations def translate_path(self, path): new = "" if path and path[0] == sep: new += sep return expanduser("{0}{1}".format(new, join(*path.split(sep)))) def set_defaults(self): add_section = self.add_section set_option = self.set section = "setup" add_section(section) set_option(section, "package-root", basename(getcwd()), False) set_option(section, "additional-packages", "", False) set_option(section, "title", "", False) set_option(section, "classifiers", "", False) set_option(section, "resource-search-path", "./, resource/", False) set_option(section, "installation-dir", "/usr/local/share/games/", False) set_option(section, "changelog", "changelog", False) set_option(section, "description-file", "", False) set_option(section, "init-script", "", False) set_option(section, "version", "", False) set_option(section, "summary", "", False) set_option(section, "license", "", False) set_option(section, "platforms", "", False) set_option(section, "contact-name", "", False) set_option(section, "contact-email", "", False) set_option(section, "url", "", False) set_option(section, "requirements", "", False) set_option(section, "main-object", "pgfw/Game.py", False) set_option(section, "resource-path-identifier", "resource_path", False) set_option(section, "special-char-placeholder", "_", False) set_option(section, "whitespace-placeholder", "-", False) set_option(section, "windows-dist-path", "dist/win/", False) set_option(section, "windows-icon-path", "", False) set_option(section, "boolean-true-lowercase", "yes, true, t, 1", False) set_option(section, "osx-includes", "", False) section = "display" add_section(section) set_option(section, "dimensions", "480, 360", False) set_option(section, "frame-duration", "40", False) set_option(section, "wait-duration", "2", False) set_option(section, "caption", "", False) set_option(section, "centered", "yes", False) set_option(section, "icon-path", "", False) set_option(section, "skip-frames", "no", False) set_option(section, "fullscreen", "no", False) set_option(section, "windowed-flag", "wi", False) set_option(section, "show-framerate", "no", False) set_option(section, "framerate-display-flag", "fr", False) set_option(section, "framerate-text-size", "16", False) set_option(section, "framerate-text-color", "0, 0, 0", False) set_option(section, "framerate-text-background", "255, 255, 255", False) set_option(section, "use-framebuffer", "no", False) section = "input" add_section(section) set_option(section, "release-suffix", "-release", False) set_option(section, "confirm-quit", "no", False) section = "sprite" add_section(section) set_option(section, "transparent-color", "magenta", False) section = "screen-captures" add_section(section) set_option(section, "rel-path", "caps", False) set_option(section, "file-name-format", "%Y%m%d%H%M%S", False) set_option(section, "file-extension", "png", False) section = "video-recordings" add_section(section) set_option(section, "enable", "no", False) set_option(section, "rel-path", "vids", False) set_option(section, "directory-name-format", "%Y%m%d%H%M%S", False) set_option(section, "file-extension", "png", False) set_option(section, "frame-format", "RGB", False) set_option(section, "framerate", "100", False) set_option(section, "temp-directory", "", False) section = "mouse" add_section(section) set_option(section, "visible", "yes", False) set_option(section, "double-click-time-limit", ".5", False) section = "keys" add_section(section) set_option(section, "up", "K_UP", False) set_option(section, "right", "K_RIGHT", False) set_option(section, "down", "K_DOWN", False) set_option(section, "left", "K_LEFT", False) set_option(section, "capture-screen", "K_F9", False) set_option(section, "toggle-fullscreen", "K_F11", False) set_option(section, "reset-game", "K_F8", False) set_option(section, "record-video", "K_F10", False) set_option(section, "volume-down", "K_F1", False) set_option(section, "volume-up", "K_F2", False) set_option(section, "volume-mute", "K_F3", False) set_option(section, "toggle-interpolator", "K_F7", False) set_option(section, "toggle-audio-panel", "K_F6", False) section = "joy" add_section(section) set_option(section, "advance", "7", False) set_option(section, "pause", "7", False) set_option(section, "select", "6", False) set_option(section, "single-xy", "no", False) set_option(section, "delay-axis", "0", False) set_option(section, "vertical-axis", "1", False) set_option(section, "horizontal-axis", "0", False) section = "event" add_section(section) set_option(section, "user-event-id", "USEREVENT", False) set_option(section, "command-id-offset", "1", False) set_option(section, "command-key", "command", False) set_option(section, "cancel-flag-key", "cancel", False) section = "audio" add_section(section) set_option(section, "sfx-default-path", "~/storage/audio/sfx/default", False) set_option(section, "sfx-repository-path", "~/storage/audio/sfx/all", False) set_option(section, "sfx-project-path", "sfx", False) set_option(section, "sfx-extensions", "wav, ogg, mp3", False) set_option(section, "bgm-repository-path", "~/storage/audio/bgm", False) set_option(section, "bgm-project-path", "bgm", False) set_option(section, "sfx-volume", "1.0", False) set_option(section, "panel-font", None, False) section = "interpolator-gui" add_section(section) set_option(section, "margin", "80", False) set_option(section, "marker-color", "255, 0, 0", False) set_option(section, "marker-size", "11", False) set_option(section, "curve-color", "0, 255, 0", False) set_option(section, "label-size", "16", False) set_option(section, "label-precision", "2", False) set_option(section, "axis-label-count", "8", False) set_option(section, "prompt-size", "380, 60", False) set_option(section, "prompt-border-color", "255, 0, 0", False) set_option(section, "prompt-border-width", "3", False) set_option(section, "prompt-character-limit", "21", False) set_option(section, "prompt-text-size", "42", False) set_option(section, "template-nodeset", "L 0 0, 1000 1", False) set_option(section, "template-nodeset-name", "template", False) set_option(section, "flat-y-range", "1", False) def add_section(self, name): if name not in self.order: self.order.append(name) RawConfigParser.add_section(self, name) def set(self, section, option, value, modifiable=True): if modifiable: if section not in self.order: self.order.append(section) if section not in self.modifiable: self.modifiable[section] = [] if option not in self.modifiable[section]: self.modifiable[section].append(option) RawConfigParser.set(self, section, option, value) def read_project_config_file(self): path = self.locate_project_config_file() if path: fp = open(path) self.set_modifiable(fp) fp.seek(0) self.readfp(fp) fp.seek(0) self.set_order(fp) fp.close() else: self.print_debug("No configuration file found") def locate_project_config_file(self): rel_path = self.project_file_rel_path if not rel_path: rel_path = self.default_project_file_rel_path if exists(rel_path) and not self.is_shared_mode(): return rel_path if self.resource_path: installed_path = join(self.resource_path, rel_path) if exists(installed_path): return installed_path def set_order(self, fp): self.order = order = [] for line in open(self.locate_project_config_file()): result = match("^\s*\[(.*)\]\s*$", line) if result: order.append(result.group(1)) def set_modifiable(self, fp): config = RawConfigParser() config.readfp(fp) modifiable = self.modifiable for section in config._sections: if section not in modifiable: modifiable[section] = [] for option in config._sections[section]: if option != "__name__" and option not in modifiable[section]: modifiable[section].append(option) def is_shared_mode(self): return "-s" in argv def print_debug(self, statement): if self.is_debug_mode(): print(statement) def is_debug_mode(self): return "-d" in argv def modify_defaults(self): self.set_installation_path() self.set_resource_search_path() self.set_screen_captures_path() self.set_video_recordings_path() self.set_data_exclusion_list() self.set_requirements() def set_installation_path(self): self.set("setup", "installation-path", join(self.get("setup", "installation-dir"), self.get("setup", "package-root")), False) def set_resource_search_path(self): section, option = "setup", "resource-search-path" search_path = self.get(section, option) if self.resource_path: search_path.append(self.resource_path) else: search_path.append(self.get("setup", "installation-path")) self.set(section, option, search_path, False) def get(self, section, option): value = RawConfigParser.get(self, section, option) if value is None: value = self.get_substitute(section, option) return self.cast_value(section, option, value) def get_substitute(self, section, option): if section == "display": if option == "caption": return self.get("setup", "title") def cast_value(self, section, option, value): pair = section, option types = self.type_declarations # if type(value) == str or type(value) == unicode: if type(value) == str: if pair in types["bool"]: return value.lower() in self.get("setup", "boolean-true-lowercase") elif pair in types["int"]: return int(value) elif pair in types["float"]: return float(value) elif pair in types["path"]: return self.translate_path(value) elif pair in types["list"]: if value == "": return [] else: return [member.strip() for member in value.split(types.list_member_sep)] elif pair in types["int-list"]: return [int(member) for member in value.split(types.list_member_sep)] elif pair in types["float-list"]: return [float(member) for member in value.split(types.list_member_sep)] elif pair in types["path-list"]: return [self.translate_path(member.strip()) for member in value.split(types.list_member_sep)] return value def set_screen_captures_path(self): section, option = "screen-captures", "path" if not self.has_option(section, option): self.set(section, option, join(self.build_home_path(), self.get(section, "rel-path")), False) def build_home_path(self): return join("~", "." + self.get("setup", "package-root")) def set_video_recordings_path(self): section, option = "video-recordings", "path" if not self.has_option(section, option): self.set(section, option, join(self.build_home_path(), self.get(section, "rel-path")), False) def set_data_exclusion_list(self): section, option = "setup", "data-exclude" exclude = [] if self.has_option(section, option): exclude = self.get(section, option) exclude += [".git*", "README", "build/", "dist/", "*.egg-info", "*.py", "MANIFEST*", "PKG-INFO", "*.pyc", "*.swp", "*~", self.get("setup", "changelog"), self.get("setup", "package-root"), self.get("setup", "init-script")] for location in self.get("setup", "additional-packages"): exclude.append(location) self.set(section, option, exclude, False) def set_requirements(self): section, option = "setup", "requirements" requirements = [] if self.has_option(section, option): requirements = self.get(section, option) if "pygame" not in requirements: requirements.append("pygame") self.set(section, option, requirements, False) def get_section(self, section): assignments = {} for option in self.options(section): assignments[option] = self.get(section, option) return assignments def __repr__(self): config = {} for section in self.sections(): config[section] = self.get_section(section) return pformat(config, 2, 1) def items(self, section): items = [] for option in self.options(section): items.append((option, self.get(section, option))) return items def write(self, fp=None): modifiable = self.modifiable use_main = fp is None if use_main: path = self.locate_project_config_file() if not path: path = join(self.resource_path or "", self.default_project_file_rel_path) fp = open(path, "w") break_line = False for section in self.order: if section in modifiable: break_line and fp.write("\n") fp.write("[%s]\n" % section) for option in modifiable[section]: if self.has_option(section, option): value = self.get(section, option) fp.write("%s = %s\n" % (option, self.get_raw_value(value))) break_line = True if use_main: fp.close() def get_raw_value(self, value): if isinstance(value, list): raw = "" for ii, value in enumerate(value): if ii: raw += ", " raw += str(value) else: raw = str(value) return raw def clear_section(self, section): if self.has_section(section): for option in self.options(section): self.remove_option(section, option) class TypeDeclarations(dict): list_member_sep = ',' defaults = { "display": {"int": ["frame-duration", "wait-duration", "framerate-text-size"], "bool": ["centered", "skip-frames", "fullscreen", "show-framerate", "use-framebuffer"], "int-list": ["dimensions", "framerate-text-color", "framerate-text-background"]}, "input": {"bool": "confirm-quit"}, "screen-captures": {"path": ["rel-path", "path"]}, "video-recordings": {"path": ["rel-path", "path"], "int": "framerate", "bool": "enable"}, "setup": {"list": ["classifiers", "resource-search-path", "requirements", "data-exclude", "additional-packages", "osx-includes", "boolean-true-lowercase"], "path": ["installation-dir", "changelog", "description-file", "main-object", "icon-path", "windows-dist-path", "package-root"]}, "mouse": {"float": "double-click-time-limit", "bool": "visible"}, "keys": {"list": ["up", "right", "down", "left"]}, "joy": {"int": ["advance", "pause", "select", "vertical-axis", "horizontal-axis"], "float": "delay-axis", "bool": "single-xy"}, "audio": { "list": "sfx-extensions", "path": "panel-font", "path-list": [ "sfx-default-path", "sfx-repository-path", "sfx-project-path", "bgm-repository-path", "bgm-project-path" ], "float": "sfx-volume" }, "event": {"int": "command-id-offset"}, "interpolator-gui": {"int": ["margin", "marker-size", "label-size", "axis-label-count", "label-precision", "prompt-border-width", "prompt-character-limit", "prompt-text-size", "flat-y-range"], "int-list": ["marker-color", "curve-color", "prompt-size", "prompt-border-color"]}, } additional_defaults = {} def __init__(self): dict.__init__(self, {"bool": [], "int": [], "float": [], "path": [], "list": [], "int-list": [], "float-list": [], "path-list": []}) self.add_chart(self.defaults) self.add_chart(self.additional_defaults) def add(self, cast, section, option): self[cast].append((section, option)) def add_chart(self, chart): for section, declarations in chart.items(): for cast, options in declarations.items(): if type(options) != list: options = [options] for option in options: self.add(cast, section, option)