Begin the transition from INI to YAML
authorJeremy Stanley <fungi@yuggoth.org>
Thu, 8 May 2014 13:09:01 +0000 (13:09 +0000)
committerJeremy Stanley <fungi@yuggoth.org>
Thu, 8 May 2014 13:09:01 +0000 (13:09 +0000)
* 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
lib/mudpy/misc.py
requirements.txt [new file with mode: 0644]

index ad171d8..db27d55 100644 (file)
@@ -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(
index f6a0f8e..1f3909b 100644 (file)
@@ -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 (file)
index 0000000..c3726e8
--- /dev/null
@@ -0,0 +1 @@
+pyyaml