From 2a2a4dd5265401038795a81815141e277998c33f Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Thu, 8 May 2014 13:09:01 +0000 Subject: [PATCH] Begin the transition from INI to YAML * lib/mudpy/data.py(DataFile.load): Temporarily turn this method into a selector which calls different loader methods depending on the file extension so that old INI and new YAML files can be intermixed during the transition. (DataFile.load_yaml): This new loader method handles YAML files. (DataFile.load_mpy): This is basically the old DataFile.load method. (DataFile.save,DataFile.is_writeable): Add some TODO reminders for the YAML transition. * lib/mudpy/misc.py(Element.__init__,Element.facets,Element.get) (Element.getboolean): Add conditional branching around calls deeper into mudpy.data which need different behavior depending on the underlying file formats. * requirements.txt: Start tracking Python module dependencies in this new file, and add the pyyaml module as the first entry. --- lib/mudpy/data.py | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- lib/mudpy/misc.py | 81 +++++++++++++++++++++++++++++++++------------ requirements.txt | 1 + 3 files changed, 158 insertions(+), 22 deletions(-) create mode 100644 requirements.txt diff --git a/lib/mudpy/data.py b/lib/mudpy/data.py index ad171d8..db27d55 100644 --- a/lib/mudpy/data.py +++ b/lib/mudpy/data.py @@ -13,6 +13,7 @@ import stat import sys import mudpy +import yaml class DataFile: @@ -26,8 +27,101 @@ class DataFile: def load(self): """Read a file and create elements accordingly.""" - self.data = configparser.RawConfigParser() + # TODO(fungi): remove this indirection after the YAML transition + if self.filename.endswith('.yaml'): + self.load_yaml() + else: + self.load_mpy() + + def load_yaml(self): + """Read a file and create elements accordingly.""" + # TODO(fungi): remove this parameter after the YAML transition + self._format = 'yaml' self.modified = False + try: + self.data = yaml.load(open(self.filename)) + except FileNotFoundError: + # it's normal if the file is one which doesn't exist yet + try: + mudpy.misc.log("Couldn't read %s file." % self.filename, 6) + except NameError: + # happens when we're not far enough along in the init process + pass + if not hasattr(self.universe, "files"): + self.universe.files = {} + self.universe.files[self.filename] = self + includes = [] + if "__control__" in self.data: + if "include_files" in self.data["__control__"]: + for included in makelist( + self.data["__control__"]["include_files"]): + included = find_file( + included, + relative=self.filename, + universe=self.universe) + if included not in includes: + includes.append(included) + if "include_dirs" in self.data["__control__"]: + for included in [ + os.path.join(x, "__init__.mpy") for x in makelist( + self.data["__control__"]["include_dirs"] + ) + ]: + included = find_file( + included, + relative=self.filename, + universe=self.universe + ) + if included not in includes: + includes.append(included) + if "default_files" in self.data["__control__"]: + origins = makedict( + self.data["__control__"]["default_files"] + ) + for key in origins.keys(): + origins[key] = find_file( + origins[key], + relative=self.filename, + universe=self.universe + ) + if origins[key] not in includes: + includes.append(origins[key]) + self.universe.default_origins[key] = origins[key] + if key not in self.universe.categories: + self.universe.categories[key] = {} + if "private_files" in self.data["__control__"]: + for item in makelist( + self.data["__control__"]["private_files"] + ): + item = find_file( + item, + relative=self.filename, + universe=self.universe + ) + if item not in includes: + includes.append(item) + if item not in self.universe.private_files: + self.universe.private_files.append(item) + for element in self.data: + if element != "__control__": + mudpy.misc.Element(element, self.universe, self.filename) + for include_file in includes: + if not os.path.isabs(include_file): + include_file = find_file( + include_file, + relative=self.filename, + universe=self.universe + ) + if (include_file not in self.universe.files or not + self.universe.files[include_file].is_writeable()): + DataFile(include_file, self.universe) + + # TODO(fungi): remove this method after the YAML transition + def load_mpy(self): + """Read a file and create elements accordingly.""" + self._format = 'mpy' + self.modified = False + self.data = configparser.RawConfigParser() if os.access(self.filename, os.R_OK): self.data.read(self.filename) if not hasattr(self.universe, "files"): @@ -100,6 +194,7 @@ class DataFile: self.universe.files[include_file].is_writeable()): DataFile(include_file, self.universe) + # TODO(fungi): this should support writing YAML def save(self): """Write the data, if necessary.""" @@ -175,6 +270,7 @@ class DataFile: # unset the modified flag self.modified = False + # TODO(fungi): this should support writing YAML def is_writeable(self): """Returns True if the __control__ read_only is False.""" return not self.data.has_option( diff --git a/lib/mudpy/misc.py b/lib/mudpy/misc.py index f6a0f8e..1f3909b 100644 --- a/lib/mudpy/misc.py +++ b/lib/mudpy/misc.py @@ -74,8 +74,13 @@ class Element: self.origin = self.universe.files[filename] # add a data section to the origin if necessary - if not self.origin.data.has_section(self.key): - self.origin.data.add_section(self.key) + # TODO(fungi): remove this indirection after the YAML transition + if self.origin._format == "yaml": + if self.key not in self.origin.data: + self.origin.data[self.key] = {} + else: + if not self.origin.data.has_section(self.key): + self.origin.data.add_section(self.key) # add or replace this element in the universe self.universe.contents[self.key] = self @@ -95,10 +100,17 @@ class Element: def facets(self): """Return a list of non-inherited facets for this element.""" - if self.key in self.origin.data.sections(): - return self.origin.data.options(self.key) + # TODO(fungi): remove this indirection after the YAML transition + if self.origin._format == "yaml": + try: + return self.origin.data[self.key].keys() + except KeyError: + return [] else: - return [] + if self.key in self.origin.data.sections(): + return self.origin.data.options(self.key) + else: + return [] def has_facet(self, facet): """Return whether the non-inherited facet exists.""" @@ -127,31 +139,58 @@ class Element: """Retrieve values.""" if default is None: default = "" - if self.origin.data.has_option(self.key, facet): - raw_data = self.origin.data.get(self.key, facet) - if raw_data.startswith("u\"") or raw_data.startswith("u'"): - raw_data = raw_data[1:] - raw_data.strip("\"'") - return raw_data - elif self.has_facet("inherit"): - for ancestor in self.ancestry(): - if self.universe.contents[ancestor].has_facet(facet): - return self.universe.contents[ancestor].get(facet) + # TODO(fungi): remove this indirection after the YAML transition + if self.origin._format == "yaml": + try: + return self.origin.data[self.key][facet] + except KeyError: + pass + if self.has_facet("inherit"): + for ancestor in self.ancestry(): + if self.universe.contents[ancestor].has_facet(facet): + return self.universe.contents[ancestor].get(facet) + else: + return default else: - return default + if self.origin.data.has_option(self.key, facet): + raw_data = self.origin.data.get(self.key, facet) + if raw_data.startswith("u\"") or raw_data.startswith("u'"): + raw_data = raw_data[1:] + raw_data.strip("\"'") + return raw_data + elif self.has_facet("inherit"): + for ancestor in self.ancestry(): + if self.universe.contents[ancestor].has_facet(facet): + return self.universe.contents[ancestor].get(facet) + else: + return default def getboolean(self, facet, default=None): """Retrieve values as boolean type.""" if default is None: default = False - if self.origin.data.has_option(self.key, facet): - return self.origin.data.getboolean(self.key, facet) - elif self.has_facet("inherit"): + # TODO(fungi): remove this indirection after the YAML transition + if self.origin._format == "yaml": + try: + return bool(self.origin.data[self.key][facet]) + except KeyError: + pass for ancestor in self.ancestry(): - if self.universe.contents[ancestor].has_facet(facet): + try: return self.universe.contents[ancestor].getboolean(facet) - else: + except KeyError: + pass return default + else: + if self.origin.data.has_option(self.key, facet): + return self.origin.data.getboolean(self.key, facet) + elif self.has_facet("inherit"): + for ancestor in self.ancestry(): + if self.universe.contents[ancestor].has_facet(facet): + return self.universe.contents[ancestor].getboolean( + facet) + else: + return default def getint(self, facet, default=None): """Return values as int type.""" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c3726e8 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pyyaml -- 2.11.0