Begin the transition from INI to YAML
[mudpy.git] / lib / mudpy / misc.py
index 96957bc..1f3909b 100644 (file)
@@ -6,8 +6,6 @@
 # terms provided in the LICENSE file distributed with this software.
 
 import codecs
-import ctypes
-import ctypes.util
 import os
 import random
 import re
@@ -76,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
@@ -97,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."""
@@ -129,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."""
@@ -265,9 +302,9 @@ class Element:
 
     def update_location(self):
         """Make sure the location's contents contain this element."""
-        location = self.get("location")
-        if location in self.universe.contents:
-            self.universe.contents[location].contents[self.key] = self
+        area = self.get("location")
+        if area in self.universe.contents:
+            self.universe.contents[area].contents[self.key] = self
 
     def clean_contents(self):
         """Make sure the element's contents aren't bogus."""
@@ -275,15 +312,15 @@ class Element:
             if element.get("location") != self.key:
                 del self.contents[element.key]
 
-    def go_to(self, location):
-        """Relocate the element to a specific location."""
+    def go_to(self, area):
+        """Relocate the element to a specific area."""
         current = self.get("location")
         if current and self.key in self.universe.contents[current].contents:
             del universe.contents[current].contents[self.key]
-        if location in self.universe.contents:
-            self.set("location", location)
-        self.universe.contents[location].contents[self.key] = self
-        self.look_at(location)
+        if area in self.universe.contents:
+            self.set("location", area)
+        self.universe.contents[area].contents[self.key] = self
+        self.look_at(area)
 
     def go_home(self):
         """Relocate the element to its default location."""
@@ -368,9 +405,9 @@ class Element:
             self.send(message)
 
     def portals(self):
-        """Map the portal directions for a room to neighbors."""
+        """Map the portal directions for an area to neighbors."""
         portals = {}
-        if re.match("""^location:-?\d+,-?\d+,-?\d+$""", self.key):
+        if re.match("""^area:-?\d+,-?\d+,-?\d+$""", self.key):
             coordinates = [(int(x))
                            for x in self.key.split(":")[1].split(",")]
             directions = self.universe.categories["internal"]["directions"]
@@ -384,7 +421,7 @@ class Element:
             for portal in self.getlist("gridlinks"):
                 adjacent = map(lambda c, o: c + o,
                                coordinates, offsets[portal])
-                neighbor = "location:" + ",".join(
+                neighbor = "area:" + ",".join(
                     [(str(x)) for x in adjacent]
                 )
                 if neighbor in self.universe.contents:
@@ -481,13 +518,13 @@ class Universe:
 
         # go through all elements to clear out inactive avatar locations
         for element in self.contents.values():
-            location = element.get("location")
-            if element in inactive_avatars and location:
-                if location in self.contents and element.key in self.contents[
-                   location
+            area = element.get("location")
+            if element in inactive_avatars and area:
+                if area in self.contents and element.key in self.contents[
+                   area
                    ].contents:
-                    del self.contents[location].contents[element.key]
-                element.set("default_location", location)
+                    del self.contents[area].contents[element.key]
+                element.set("default_location", area)
                 element.remove_facet("location")
 
         # another pass to straighten out all the element contents
@@ -895,14 +932,13 @@ class User:
             try:
                 self.connection.send(self.output_queue[0])
                 del self.output_queue[0]
-            except:
+            except BrokenPipeError:
                 if self.account and self.account.get("name"):
                     account = self.account.get("name")
                 else:
                     account = "an unknown user"
-                log("Sending to %s raised an exception (broken pipe?)."
-                    % account, 7)
-                pass
+                log("Broken pipe sending to %s." % account, 7)
+                self.state = "disconnecting"
 
     def enqueue_input(self):
         """Process and enqueue any new input."""
@@ -910,7 +946,7 @@ class User:
         # check for some input
         try:
             raw_input = self.connection.recv(1024)
-        except:
+        except (BlockingIOError, OSError):
             raw_input = b""
 
         # we got something
@@ -1492,7 +1528,7 @@ def check_for_connection(listening_socket):
     # try to accept a new connection
     try:
         connection, address = listening_socket.accept()
-    except:
+    except BlockingIOError:
         return None
 
     # note that we got one
@@ -2068,7 +2104,7 @@ def command_look(actor, parameters):
 
 
 def command_say(actor, parameters):
-    """Speak to others in the same room."""
+    """Speak to others in the same area."""
 
     # check for replacement macros and escape them
     parameters = escape_macros(parameters)
@@ -2132,7 +2168,7 @@ def command_say(actor, parameters):
             # capitalize the first letter
             message = message[0].upper() + message[1:]
 
-    # tell the room
+    # tell the area
     if message:
         actor.echo_to_location(
             actor.get("name") + " " + action + "s, \"" + message + "\""
@@ -2233,8 +2269,9 @@ def command_show(actor, parameters):
         else:
             try:
                 message = repr(eval(" ".join(arguments[1:])))
-            except:
-                message = "Your expression raised an exception!"
+            except Exception as e:
+                message = ("$(red)Your expression raised an exception...$(eol)"
+                           "$(eol)$(bld)%s$(nrm)" % e)
     elif arguments[0] == "log":
         if len(arguments) == 4:
             if re.match("^\d+$", arguments[3]) and int(arguments[3]) >= 0:
@@ -2399,48 +2436,6 @@ def daemonize(universe):
     # only if this is what we're configured to do
     if universe.contents["internal:process"].getboolean("daemon"):
 
-        # if possible, we want to rename the process to the same as the script
-        new_argv = b"\x00".join(x.encode("utf-8") for x in sys.argv) + b"\x00"
-        short_argv0 = os.path.basename(sys.argv[0]).encode("utf-8") + b"\x00"
-
-        # attempt the linux way first
-        try:
-            argv_array = ctypes.POINTER(ctypes.c_char_p)
-            ctypes.pythonapi.Py_GetArgcArgv.argtypes = (
-                ctypes.POINTER(ctypes.c_int),
-                ctypes.POINTER(argv_array)
-            )
-            argc = argv_array()
-            ctypes.pythonapi.Py_GetArgcArgv(
-                ctypes.c_int(0),
-                ctypes.pointer(argc)
-            )
-            old_argv0_size = len(argc.contents.value)
-            ctypes.memset(argc.contents, 0, len(new_argv) + old_argv0_size)
-            ctypes.memmove(argc.contents, new_argv, len(new_argv))
-            ctypes.CDLL(ctypes.util.find_library("c")).prctl(
-                15,
-                short_argv0,
-                0,
-                0,
-                0
-            )
-
-        except:
-
-            # since that failed, maybe it's bsd?
-            try:
-
-                # much simpler, since bsd has a libc function call for this
-                ctypes.CDLL(ctypes.util.find_library("c")).setproctitle(
-                    new_argv
-                )
-
-            except:
-
-                # that didn't work either, so just log that we couldn't
-                log("Failed to rename the interpreter process (cosmetic).")
-
         # log before we start forking around, so the terminal gets the message
         log("Disassociating from the controlling terminal.")
 
@@ -2507,14 +2502,9 @@ def excepthook(excepttype, value, tracebackdata):
     # try to log it, if possible
     try:
         log(message, 9)
-    except:
-        pass
-
-    # try to write it to stderr, if possible
-    try:
-        sys.stderr.write(message)
-    except:
-        pass
+    except Exception as e:
+        # try to write it to stderr, if possible
+        sys.stderr.write(message + "\nException while logging...\n%s" % e)
 
 
 def sighook(what, where):