Finalize INI to YAML conversion
authorJeremy Stanley <fungi@yuggoth.org>
Sat, 13 Sep 2014 06:48:51 +0000 (06:48 +0000)
committerJeremy Stanley <fungi@yuggoth.org>
Sat, 13 Sep 2014 06:48:51 +0000 (06:48 +0000)
Convert the main configuration file to YAML and rip out any remnants
of INI support.

etc/mudpy.conf [deleted file]
etc/mudpy.yaml [new file with mode: 0644]
lib/mudpy/data.py
lib/mudpy/misc.py
share/menu.yaml

diff --git a/etc/mudpy.conf b/etc/mudpy.conf
deleted file mode 100644 (file)
index 7283f91..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-# Copyright (c) 2004-2014 Jeremy Stanley <fungi@yuggoth.org>. Permission
-# to use, copy, modify, and distribute this software is granted under
-# terms provided in the LICENSE file distributed with this software.
-
-[__control__]
-default_files = { "account": "account.mpy", "actor": "actor.mpy", "area": "area.yaml", "command": "command.yaml", "internal": "internal.mpy", "menu": "menu.yaml", "other": "other.mpy", "prop": "prop.yaml" }
-include_dirs = "sample"
-include_files = "archetype.yaml"
-private_files = "account.mpy"
-read_only = yes
-
-[internal:language]
-actions = { "?": "ask", ",": "begin", "-": "begin", ":": "begin", ";": "begin", "!": "exclaim", "...": "muse", ".": "say" }
-default_punctuation = .
-typos = { "i": "I", "i'd": "I'd", "i'll": "I'll", "i'm": "I'm", "teh": "the", "theyre": "they're", "youre": "you're" }
-
-[internal:limits]
-#default_admins = admin
-#default_backup_count = 10
-max_avatars = 7
-password_tries = 3
-
-[internal:logging]
-#file = mudpy.log
-max_log_lines = 1000
-stdout = yes
-#syslog = mudpy
-
-[internal:network]
-host = ::1
-#host = 127.0.0.1
-port = 6669
-
-[internal:process]
-#daemon = yes
-#pidfile = mudpy.pid
-
-[internal:storage]
-default_dir = "data"
-#root_path = "."
-search_path = [ "", "etc", "share", "data" ]
-
-[internal:time]
-definition_d = 24h
-definition_h = 60mi
-definition_mi = 10r
-definition_mo = 28d
-definition_r = 6
-definition_w = 7d
-definition_y = 12mo
-frequency_log = 6000
-frequency_save = 600
-linkdead = { "default": 6000, "entering_account_name": 600, "active": 6048000 }
-idle = { "default": 5000, "entering_account_name": 500, "active": 5040000 }
-increment = 0.1
-
-[internal:directions]
-down = { "vector": (0,0,-1), "exit": "downward", "enter": "above" }
-east = { "vector": (1,0,0), "exit": "to the east", "enter": "the west" }
-north = { "vector": (0,1,0), "exit": "to the north", "enter": "the south" }
-south = { "vector": (0,-1,0), "exit": "to the south", "enter": "the north" }
-up = { "vector": (0,0,1), "exit": "upward", "enter": "below" }
-west = { "vector": (-1,0,0), "exit": "to the west", "enter": "the east" }
-
diff --git a/etc/mudpy.yaml b/etc/mudpy.yaml
new file mode 100644 (file)
index 0000000..08832e6
--- /dev/null
@@ -0,0 +1,63 @@
+# Copyright (c) 2004-2014 Jeremy Stanley <fungi@yuggoth.org>. Permission
+# to use, copy, modify, and distribute this software is granted under
+# terms provided in the LICENSE file distributed with this software.
+
+__control__:
+    default_files: { "account": "account.yaml", "actor": "actor.yaml", "area": "area.yaml", "command": "command.yaml", "internal": "internal.yaml", "menu": "menu.yaml", "other": "other.yaml", "prop": "prop.yaml" }
+    include_dirs: [ "sample" ]
+    include_files: [ "archetype.yaml" ]
+    private_files: [ "account.yaml" ]
+    read_only: yes
+
+internal:language:
+    actions: { "?": "ask", ",": "begin", "-": "begin", ":": "begin", ";": "begin", "!": "exclaim", "...": "muse", ".": "say" }
+    default_punctuation: .
+    typos: { "i": "I", "i'd": "I'd", "i'll": "I'll", "i'm": "I'm", "teh": "the", "theyre": "they're", "youre": "you're" }
+
+internal:limits:
+    #default_admins: [ "admin" ]
+    #default_backup_count: 10
+    max_avatars: 7
+    password_tries: 3
+
+internal:logging:
+    #file: mudpy.log
+    max_log_lines: 1000
+    stdout: yes
+    #syslog: mudpy
+
+internal:network:
+    host: ::1
+    #host: 127.0.0.1
+    port: 6669
+
+internal:process:
+    #daemon: yes
+    #pidfile: mudpy.pid
+
+internal:storage:
+    default_dir: "data"
+    #root_path: "."
+    search_path: [ "", "etc", "share", "data" ]
+
+internal:time:
+    definition_d: 24h
+    definition_h: 60mi
+    definition_mi: 10r
+    definition_mo: 28d
+    definition_r: 6
+    definition_w: 7d
+    definition_y: 12mo
+    frequency_log: 6000
+    frequency_save: 600
+    linkdead: { "default": 6000, "entering_account_name": 600, "active": 6048000 }
+    idle: { "default": 5000, "entering_account_name": 500, "active": 5040000 }
+    increment: 0.1
+
+internal:directions:
+    down: { "vector": [0,0,-1], "exit": "downward", "enter": "above" }
+    east: { "vector": [1,0,0], "exit": "to the east", "enter": "the west" }
+    north: { "vector": [0,1,0], "exit": "to the north", "enter": "the south" }
+    south: { "vector": [0,-1,0], "exit": "to the south", "enter": "the north" }
+    up: { "vector": [0,0,1], "exit": "upward", "enter": "below" }
+    west: { "vector": [-1,0,0], "exit": "to the west", "enter": "the east" }
index 7629456..b7d173b 100644 (file)
@@ -6,11 +6,9 @@
 # terms provided in the LICENSE file distributed with this software.
 
 import codecs
-import configparser
 import os
 import re
 import stat
-import sys
 
 import mudpy
 import yaml
@@ -28,16 +26,6 @@ class DataFile:
 
     def load(self):
         """Read a file and create elements accordingly."""
-        # 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))
@@ -73,18 +61,6 @@ class DataFile:
                     )
                     if included not in includes:
                         includes.append(included)
-                # TODO(fungi): remove this loop after the YAML transition
-                for included in [
-                    os.path.join(x, "__init__.mpy") for x in
-                        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 = self.data["__control__"]["default_files"]
                 for key in origins.keys():
@@ -123,103 +99,12 @@ class DataFile:
                     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"):
-            self.universe.files = {}
-        self.universe.files[self.filename] = self
-        includes = []
-        if self.data.has_option("__control__", "include_files"):
-            for included in makelist(
-                self.data.get("__control__", "include_files")
-            ):
-                included = find_file(
-                    included,
-                    relative=self.filename,
-                    universe=self.universe
-                )
-                if included not in includes:
-                    includes.append(included)
-        if self.data.has_option("__control__", "include_dirs"):
-            for included in [
-                os.path.join(x, "__init__.yaml") 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)
-            for included in [
-                os.path.join(x, "__init__.mpy") for x in makelist(
-                    self.data.get("__control__", "include_dirs")
-                )
-            ]:
-                included = find_file(
-                    included,
-                    relative=self.filename,
-                    universe=self.universe
-                )
-                if included not in includes:
-                    includes.append(included)
-        if self.data.has_option("__control__", "default_files"):
-            origins = makedict(
-                self.data.get("__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 self.data.has_option("__control__", "private_files"):
-            for item in makelist(
-                self.data.get("__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 section in self.data.sections():
-            if section != "__control__":
-                mudpy.misc.Element(section, 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): this should support writing YAML
     def save(self):
         """Write the data, if necessary."""
 
         # when modified, writeable and has content or the file exists
         if self.modified and self.is_writeable() and (
-           self.data.sections() or os.path.exists(self.filename)
+           self.data or os.path.exists(self.filename)
            ):
 
             # make parent directories if necessary
@@ -227,15 +112,15 @@ class DataFile:
                 os.makedirs(os.path.dirname(self.filename))
 
             # backup the file
-            if self.data.has_option("__control__", "backup_count"):
-                max_count = self.data.has_option(
-                    "__control__", "backup_count")
+            if "__control__" in self.data and "backup_count" in self.data[
+                    "__control__"]:
+                max_count = self.data["__control__"]["backup_count"]
             else:
                 max_count = self.universe.categories[
                     "internal"
                 ][
                     "limits"
-                ].getint("default_backup_count")
+                ].get("default_backup_count")
             if os.path.exists(self.filename) and max_count:
                 backups = []
                 for candidate in os.listdir(os.path.dirname(self.filename)):
@@ -268,21 +153,8 @@ class DataFile:
                ) != 0o0600:
                 os.chmod(self.filename, 0o0600)
 
-            # write it back sorted, instead of using configparser
-            sections = self.data.sections()
-            sections.sort()
-            for section in sections:
-                file_descriptor.write("[" + section + "]\n")
-                options = self.data.options(section)
-                options.sort()
-                for option in options:
-                    file_descriptor.write(
-                        option + " = " +
-                        self.data.get(section, option) + "\n"
-                    )
-                file_descriptor.write("\n")
-
-            # flush and close the file
+            # write, flush and close the file
+            file_descriptor.write(yaml.dump(self.data))
             file_descriptor.flush()
             file_descriptor.close()
 
@@ -291,18 +163,10 @@ class DataFile:
 
     def is_writeable(self):
         """Returns True if the __control__ read_only is False."""
-        # TODO(fungi): remove this indirection after the YAML transition
-        if self._format == "yaml":
-            try:
-                return not self.data["__control__"].get("read_only", False)
-            except KeyError:
-                return True
-        else:
-            return not self.data.has_option(
-                "__control__", "read_only"
-            ) or not self.data.getboolean(
-                "__control__", "read_only"
-            )
+        try:
+            return not self.data["__control__"].get("read_only", False)
+        except KeyError:
+            return True
 
 
 def find_file(
@@ -323,10 +187,6 @@ def find_file(
     if file_name and os.path.isabs(file_name):
         return os.path.realpath(file_name)
 
-    # when no file name is specified, look for <argv[0]>.conf
-    elif not file_name:
-        file_name = os.path.basename(sys.argv[0]) + ".conf"
-
     # if a universe was provided, try to get some defaults from there
     if universe:
 
@@ -338,7 +198,7 @@ def find_file(
             if not root_path:
                 root_path = storage.get("root_path").strip("\"'")
             if not search_path:
-                search_path = storage.getlist("search_path")
+                search_path = storage.get("search_path")
             if not default_dir:
                 default_dir = storage.get("default_dir").strip("\"'")
 
@@ -350,34 +210,19 @@ def find_file(
             data_file = universe.files[list(universe.files.keys())[0]].data
 
             # try for a fallback default directory
-            if not default_dir and data_file.has_option(
-               "internal:storage",
-               "default_dir"
-               ):
+            if not default_dir:
                 default_dir = data_file.get(
-                    "internal:storage",
-                    "default_dir"
-                ).strip("\"'")
+                    "internal:storage", "").get("default_dir", "")
 
             # try for a fallback root path
-            if not root_path and data_file.has_option(
-               "internal:storage",
-               "root_path"
-               ):
+            if not root_path:
                 root_path = data_file.get(
-                    "internal:storage",
-                    "root_path"
-                ).strip("\"'")
+                    "internal:storage", "").get("root_path", "")
 
             # try for a fallback search path
-            if not search_path and data_file.has_option(
-               "internal:storage",
-               "search_path"
-               ):
-                search_path = makelist(
-                    data_file.get("internal:storage",
-                                  "search_path").strip("\"'")
-                )
+            if not search_path:
+                search_path = data_file.get(
+                    "internal:storage", "").get("search_path", "")
 
         # another fallback root path, this time from the universe startdir
         if not root_path and hasattr(universe, "startdir"):
@@ -439,23 +284,3 @@ def find_file(
 
     # normalize the resulting file path and hand it back
     return file_name
-
-
-def makelist(value):
-    """Turn string into list type."""
-    if value[0] + value[-1] == "[]":
-        return eval(value)
-    elif value[0] + value[-1] == "\"\"":
-        return [value[1:-1]]
-    else:
-        return [value]
-
-
-def makedict(value):
-    """Turn string into dict type."""
-    if value[0] + value[-1] == "{}":
-        return eval(value)
-    elif value.find(":") > 0:
-        return eval("{" + value + "}")
-    else:
-        return {value: None}
index c6fd97d..2bec9bf 100644 (file)
@@ -74,13 +74,8 @@ class Element:
         self.origin = self.universe.files[filename]
 
         # add a data section to the origin if necessary
-        # 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)
+        if self.key not in self.origin.data:
+            self.origin.data[self.key] = {}
 
         # add or replace this element in the universe
         self.universe.contents[self.key] = self
@@ -93,24 +88,17 @@ class Element:
 
     def destroy(self):
         """Remove an element from the universe and destroy it."""
-        self.origin.data.remove_section(self.key)
+        del(self.origin.data[self.key])
         del self.universe.categories[self.category][self.subkey]
         del self.universe.contents[self.key]
         del self
 
     def facets(self):
         """Return a list of non-inherited facets for this element."""
-        # TODO(fungi): remove this indirection after the YAML transition
-        if self.origin._format == "yaml":
-            try:
-                return self.origin.data[self.key].keys()
-            except (AttributeError, KeyError):
-                return []
-        else:
-            if self.key in self.origin.data.sections():
-                return self.origin.data.options(self.key)
-            else:
-                return []
+        try:
+            return self.origin.data[self.key].keys()
+        except (AttributeError, KeyError):
+            return []
 
     def has_facet(self, facet):
         """Return whether the non-inherited facet exists."""
@@ -119,13 +107,15 @@ class Element:
     def remove_facet(self, facet):
         """Remove a facet from the element."""
         if self.has_facet(facet):
-            self.origin.data.remove_option(self.key, facet)
+            del(self.origin.data[self.key][facet])
             self.origin.modified = True
 
     def ancestry(self):
         """Return a list of the element's inheritance lineage."""
         if self.has_facet("inherit"):
-            ancestry = self.getlist("inherit")
+            ancestry = self.get("inherit")
+            if not ancestry:
+                ancestry = []
             for parent in ancestry[:]:
                 ancestors = self.universe.contents[parent].ancestry()
                 for ancestor in ancestors:
@@ -139,124 +129,32 @@ class Element:
         """Retrieve values."""
         if default is None:
             default = ""
-        # TODO(fungi): remove this indirection after the YAML transition
-        if self.origin._format == "yaml":
-            try:
-                return self.origin.data[self.key][facet]
-            except (KeyError, TypeError):
-                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:
-            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
-        # 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():
-                try:
-                    return self.universe.contents[ancestor].getboolean(facet)
-                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."""
-        if default is None:
-            default = 0
-        if self.origin.data.has_option(self.key, facet):
-            return self.origin.data.getint(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].getint(facet)
-        else:
-            return default
-
-    def getfloat(self, facet, default=None):
-        """Return values as float type."""
-        if default is None:
-            default = 0.0
-        if self.origin.data.has_option(self.key, facet):
-            return self.origin.data.getfloat(self.key, facet)
-        elif self.has_facet("inherit"):
+        try:
+            return self.origin.data[self.key][facet]
+        except (KeyError, TypeError):
+            pass
+        if self.has_facet("inherit"):
             for ancestor in self.ancestry():
                 if self.universe.contents[ancestor].has_facet(facet):
-                    return self.universe.contents[ancestor].getfloat(facet)
-        else:
-            return default
-
-    def getlist(self, facet, default=None):
-        """Return values as list type."""
-        if default is None:
-            default = []
-        value = self.get(facet)
-        if value:
-            if type(value) is list:
-                return value
-            else:
-                return mudpy.data.makelist(value)
-        else:
-            return default
-
-    def getdict(self, facet, default=None):
-        """Return values as dict type."""
-        if default is None:
-            default = {}
-        value = self.get(facet)
-        if value:
-            if type(value) is dict:
-                return value
-            else:
-                return mudpy.data.makedict(value)
+                    return self.universe.contents[ancestor].get(facet)
         else:
             return default
 
     def set(self, facet, value):
         """Set values."""
         if not self.has_facet(facet) or not self.get(facet) == value:
-            if not type(value) is str:
-                value = repr(value)
-            self.origin.data.set(self.key, facet, value)
+            if self.key not in self.origin.data:
+                self.origin.data[self.key] = {}
+            self.origin.data[self.key][facet] = value
             self.origin.modified = True
 
     def append(self, facet, value):
         """Append value to a list."""
-        if not type(value) is str:
-            value = repr(value)
-        newlist = self.getlist(facet)
+        newlist = self.get(facet)
+        if not newlist:
+            newlist = []
+        if type(newlist) is not list:
+            newlist = list(newlist)
         newlist.append(value)
         self.set(facet, newlist)
 
@@ -292,11 +190,11 @@ class Element:
             result = False
 
         # avatars of administrators can run any command
-        elif self.owner and self.owner.account.getboolean("administrator"):
+        elif self.owner and self.owner.account.get("administrator"):
             result = True
 
         # everyone can run non-administrative commands
-        elif not command.getboolean("administrative"):
+        elif not command.get("administrative"):
             result = True
 
         # otherwise the command cannot be run by this actor
@@ -344,7 +242,7 @@ class Element:
                 "internal"
             ][
                 "directions"
-            ].getdict(
+            ].get(
                 direction
             )[
                 "exit"
@@ -355,7 +253,7 @@ class Element:
                 "internal"
             ][
                 "directions"
-            ].getdict(
+            ].get(
                 direction
             )[
                 "exit"
@@ -373,7 +271,7 @@ class Element:
                 "internal"
             ][
                 "directions"
-            ].getdict(
+            ].get(
                 direction
             )[
                 "enter"
@@ -400,7 +298,7 @@ class Element:
             for element in self.universe.contents[
                 self.get("location")
             ].contents.values():
-                if element.getboolean("is_actor") and element is not self:
+                if element.get("is_actor") and element is not self:
                     message += "$(yel)" + element.get(
                         "name"
                     ) + " is here.$(nrm)$(eol)"
@@ -420,11 +318,11 @@ class Element:
             offsets = dict(
                 [
                     (
-                        x, directions.getdict(x)["vector"]
+                        x, directions.get(x)["vector"]
                     ) for x in directions.facets()
                 ]
             )
-            for portal in self.getlist("gridlinks"):
+            for portal in self.get("gridlinks"):
                 adjacent = map(lambda c, o: c + o,
                                coordinates, offsets[portal])
                 neighbor = "area:" + ",".join(
@@ -467,20 +365,17 @@ class Universe:
         self.loglines = []
         self.private_files = []
         self.reload_flag = False
+        self.setup_loglines = []
         self.startdir = os.getcwd()
         self.terminate_flag = False
         self.userlist = []
         if not filename:
             possible_filenames = [
-                ".mudpyrc",
-                ".mudpy/mudpyrc",
-                ".mudpy/mudpy.conf",
-                "mudpy.conf",
-                "etc/mudpy.conf",
-                "/usr/local/mudpy/mudpy.conf",
-                "/usr/local/mudpy/etc/mudpy.conf",
-                "/etc/mudpy/mudpy.conf",
-                "/etc/mudpy.conf"
+                "etc/mudpy.yaml",
+                "/usr/local/mudpy/etc/mudpy.yaml",
+                "/usr/local/etc/mudpy.yaml",
+                "/etc/mudpy/mudpy.yaml",
+                "/etc/mudpy.yaml"
             ]
             for filename in possible_filenames:
                 if os.access(filename, os.R_OK):
@@ -557,7 +452,7 @@ class Universe:
 
         # need to know the local address and port number for the listener
         host = self.categories["internal"]["network"].get("host")
-        port = self.categories["internal"]["network"].getint("port")
+        port = self.categories["internal"]["network"].get("port")
 
         # if no host was specified, bind to all local addresses (preferring
         # ipv6)
@@ -600,7 +495,7 @@ class Universe:
 
     def get_time(self):
         """Convenience method to get the elapsed time counter."""
-        return self.categories["internal"]["counters"].getint("elapsed")
+        return self.categories["internal"]["counters"].get("elapsed")
 
 
 class User:
@@ -647,7 +542,7 @@ class User:
     def check_idle(self):
         """Warn or disconnect idle users as appropriate."""
         idletime = universe.get_time() - self.last_input
-        linkdead_dict = universe.categories["internal"]["time"].getdict(
+        linkdead_dict = universe.categories["internal"]["time"].get(
             "linkdead"
         )
         if self.state in linkdead_dict:
@@ -671,7 +566,7 @@ class User:
             log(logline, 2)
             self.state = "disconnecting"
             self.menu_seen = False
-        idle_dict = universe.categories["internal"]["time"].getdict("idle")
+        idle_dict = universe.categories["internal"]["time"].get("idle")
         if self.state in idle_dict:
             idle_state = self.state
         else:
@@ -768,7 +663,7 @@ class User:
                "internal"
                ][
                 "limits"
-            ].getlist(
+            ].get(
                 "default_admins"
             ):
                 self.account.set("administrator", "True")
@@ -1037,14 +932,14 @@ class User:
         if self.avatar is universe.contents[avatar]:
             self.avatar = None
         universe.contents[avatar].destroy()
-        avatars = self.account.getlist("avatars")
+        avatars = self.account.get("avatars")
         avatars.remove(avatar)
         self.account.set("avatars", avatars)
 
     def activate_avatar_by_index(self, index):
         """Enter the world with a particular indexed avatar."""
         self.avatar = universe.contents[
-            self.account.getlist("avatars")[index]]
+            self.account.get("avatars")[index]]
         self.avatar.owner = self
         self.state = "active"
         self.avatar.go_home()
@@ -1067,7 +962,7 @@ class User:
 
     def destroy(self):
         """Destroy the user and associated avatars."""
-        for avatar in self.account.getlist("avatars"):
+        for avatar in self.account.get("avatars"):
             self.delete_avatar(avatar)
         self.account.destroy()
 
@@ -1091,7 +986,7 @@ def log(message, level=0):
 
     # a couple references we need
     file_name = universe.categories["internal"]["logging"].get("file")
-    max_log_lines = universe.categories["internal"]["logging"].getint(
+    max_log_lines = universe.categories["internal"]["logging"].get(
         "max_log_lines"
     )
     syslog_name = universe.categories["internal"]["logging"].get("syslog")
@@ -1113,7 +1008,7 @@ def log(message, level=0):
         file_descriptor.close()
 
     # send the timestamp and line to standard output
-    if universe.categories["internal"]["logging"].getboolean("stdout"):
+    if universe.categories["internal"]["logging"].get("stdout"):
         for line in lines:
             print(timestamp + " " + line)
 
@@ -1130,9 +1025,9 @@ def log(message, level=0):
 
     # display to connected administrators
     for user in universe.userlist:
-        if user.state == "active" and user.account.getboolean(
+        if user.state == "active" and user.account.get(
            "administrator"
-           ) and user.account.getint("loglevel") <= level:
+           ) and user.account.get("loglevel", 0) <= level:
             # iterate over every line in the message
             full_message = ""
             for line in lines:
@@ -1477,43 +1372,43 @@ def on_pulse():
         )
 
     # update the log every now and then
-    if not universe.categories["internal"]["counters"].getint("mark"):
+    if not universe.categories["internal"]["counters"].get("mark"):
         log(str(len(universe.userlist)) + " connection(s)")
         universe.categories["internal"]["counters"].set(
-            "mark", universe.categories["internal"]["time"].getint(
+            "mark", universe.categories["internal"]["time"].get(
                 "frequency_log"
             )
         )
     else:
         universe.categories["internal"]["counters"].set(
-            "mark", universe.categories["internal"]["counters"].getint(
+            "mark", universe.categories["internal"]["counters"].get(
                 "mark"
             ) - 1
         )
 
     # periodically save everything
-    if not universe.categories["internal"]["counters"].getint("save"):
+    if not universe.categories["internal"]["counters"].get("save"):
         universe.save()
         universe.categories["internal"]["counters"].set(
-            "save", universe.categories["internal"]["time"].getint(
+            "save", universe.categories["internal"]["time"].get(
                 "frequency_save"
             )
         )
     else:
         universe.categories["internal"]["counters"].set(
-            "save", universe.categories["internal"]["counters"].getint(
+            "save", universe.categories["internal"]["counters"].get(
                 "save"
             ) - 1
         )
 
     # pause for a configurable amount of time (decimal seconds)
     time.sleep(universe.categories["internal"]
-               ["time"].getfloat("increment"))
+               ["time"].get("increment"))
 
     # increase the elapsed increment counter
     universe.categories["internal"]["counters"].set(
-        "elapsed", universe.categories["internal"]["counters"].getint(
-            "elapsed"
+        "elapsed", universe.categories["internal"]["counters"].get(
+            "elapsed", 0
         ) + 1
     )
 
@@ -1588,7 +1483,7 @@ def get_menu(state, error=None, choices=None):
 
 def menu_echo_on(state):
     """True if echo is on, false if it is off."""
-    return universe.categories["menu"][state].getboolean("echo", True)
+    return universe.categories["menu"][state].get("echo", True)
 
 
 def get_echo_message(state):
@@ -1840,7 +1735,7 @@ def handler_checking_password(user):
         "internal"
     ][
         "limits"
-    ].getint(
+    ].get(
         "password_tries"
     ) - 1:
         user.password_tries += 1
@@ -1879,7 +1774,7 @@ def handler_entering_new_password(user):
         "internal"
     ][
         "limits"
-    ].getint(
+    ].get(
         "password_tries"
     ) - 1:
         user.password_tries += 1
@@ -1914,7 +1809,7 @@ def handler_verifying_new_password(user):
         "internal"
     ][
         "limits"
-    ].getint(
+    ].get(
         "password_tries"
     ) - 1:
         user.password_tries += 1
@@ -2034,7 +1929,7 @@ def command_help(actor, parameters):
             description = command.get("description")
             if not description:
                 description = "(no short description provided)"
-            if command.getboolean("administrative"):
+            if command.get("administrative"):
                 output = "$(red)"
             else:
                 output = "$(grn)"
@@ -2047,7 +1942,7 @@ def command_help(actor, parameters):
             output += help_text
 
             # list related commands
-            see_also = command.getlist("see_also")
+            see_also = command.get("see_also")
             if see_also:
                 really_see_also = ""
                 for item in see_also:
@@ -2056,7 +1951,7 @@ def command_help(actor, parameters):
                         if actor.can_run(command):
                             if really_see_also:
                                 really_see_also += ", "
-                            if command.getboolean("administrative"):
+                            if command.get("administrative"):
                                 really_see_also += "$(red)"
                             else:
                                 really_see_also += "$(grn)"
@@ -2081,7 +1976,7 @@ def command_help(actor, parameters):
                 description = command.get("description")
                 if not description:
                     description = "(no short description provided)"
-                if command.getboolean("administrative"):
+                if command.get("administrative"):
                     output += "   $(red)"
                 else:
                     output += "   $(grn)"
@@ -2130,7 +2025,7 @@ def command_say(actor, parameters):
     if message:
 
         # match the punctuation used, if any, to an action
-        actions = universe.categories["internal"]["language"].getdict(
+        actions = universe.categories["internal"]["language"].get(
             "actions"
         )
         default_punctuation = (
@@ -2157,7 +2052,7 @@ def command_say(actor, parameters):
             message = message[0].lower() + message[1:]
 
             # iterate over all words in message, replacing typos
-            typos = universe.categories["internal"]["language"].getdict(
+            typos = universe.categories["internal"]["language"].get(
                 "typos"
             )
             words = message.split()
@@ -2248,7 +2143,7 @@ def command_show(actor, parameters):
         elif arguments[1] in universe.files:
             message = ("These are the elements in the \"" + arguments[1]
                        + "\" file:$(eol)")
-            elements = universe.files[arguments[1]].data.sections()
+            elements = universe.files[arguments[1]].data.keys()
             elements.sort()
             for element in elements:
                 message += "$(eol)   $(grn)" + element + "$(nrm)"
@@ -2299,8 +2194,8 @@ def command_show(actor, parameters):
                 level = int(arguments[1])
             else:
                 level = -1
-        elif 0 <= actor.owner.account.getint("loglevel") <= 9:
-            level = actor.owner.account.getint("loglevel")
+        elif 0 <= actor.owner.account.get("loglevel") <= 9:
+            level = actor.owner.account.get("loglevel")
         else:
             level = 1
         if level > -1 and start > -1 and stop > -1:
@@ -2440,7 +2335,7 @@ def daemonize(universe):
     """Fork and disassociate from everything."""
 
     # only if this is what we're configured to do
-    if universe.contents["internal:process"].getboolean("daemon"):
+    if universe.contents["internal:process"].get("daemon"):
 
         # log before we start forking around, so the terminal gets the message
         log("Disassociating from the controlling terminal.")
index c022ee4..248592b 100644 (file)
@@ -70,7 +70,7 @@ menu:delete_account:
     prompt: Are you certain you wish to permanently delete your account?
 
 menu:delete_avatar:
-    action: user.delete_avatar(user.account.getlist("avatars")[int(choice)-1])
+    action: user.delete_avatar(user.account.get("avatars")[int(choice)-1])
     action_a: pass
     branch: main_utility
     branch_a: main_utility
@@ -111,9 +111,9 @@ menu:main_utility:
     choice_d: delete an unwanted avatar
     choice_l: leave example for now
     choice_p: permanently remove your account
-    demand_a: user.account.getlist("avatars")
-    demand_c: len(user.account.getlist("avatars")) < universe.categories["internal"]["limits"].getint("max_avatars")
-    demand_d: user.account.getlist("avatars")
+    demand_a: user.account.get("avatars")
+    demand_c: len(user.account.get("avatars")) < universe.categories["internal"]["limits"].get("max_avatars")
+    demand_d: user.account.get("avatars")
     description: $(red)$(inc:banner.txt)$(nrm)$(eol)$(eol)From here you can awaken, create and delete avatars. An avatar is your persona in the world of Example. You can also leave or permanently delete your account.
     prompt: What would you like to do?