Index: trac/env.py
===================================================================
--- trac/env.py	(revision 5999)
+++ trac/env.py	(working copy)
@@ -516,12 +516,16 @@
         env_cache_lock.acquire()
         try:
             env = env_cache.get(env_path)
+            if env and env.config.parse_if_needed():
+                # The environment configuration has changed, so shut it down
+                # and remove it from the cache so that it gets reinitialized
+                env.shutdown()
+                if hasattr(env.log, '_trac_handler'):
+                    env.log.removeHandler(env.log._trac_handler)
+                del env_cache[env_path]
+                env = None
             if env is None:
                 env = env_cache.setdefault(env_path, open_environment(env_path))
-            else:
-                # Re-parse the configuration file if it changed since the last
-                # the time it was parsed
-                env.config.parse_if_needed()
         finally:
             env_cache_lock.release()
     else:
Index: trac/admin/web_ui.py
===================================================================
--- trac/admin/web_ui.py	(revision 5999)
+++ trac/admin/web_ui.py	(working copy)
@@ -381,7 +381,7 @@
                 self._do_update(req)
             anchor = ''
             if req.args.has_key('plugin'):
-                anchor = '#no' + req.args.get('plugin')
+                anchor = '#no%d' % (int(req.args.get('plugin')) + 1)
             req.redirect(req.href.admin(cat, page) + anchor)
 
         return self._render_view(req)
@@ -426,6 +426,9 @@
 
         # TODO: Validate that the uploaded file is actually a valid Trac plugin
 
+        # Make the environment reset itself on the next request
+        self.env.config.touch()
+
     def _do_uninstall(self, req):
         """Uninstall a plugin."""
         plugin_filename = req.args.get('plugin_filename')
@@ -437,6 +440,9 @@
         self.log.info('Uninstalling plugin %s', plugin_filename)
         os.remove(plugin_path)
 
+        # Make the environment reset itself on the next request
+        self.env.config.touch()
+
     def _do_update(self, req):
         """Update component enablement."""
         components = req.args.getlist('component')
Index: trac/admin/templates/admin_logging.html
===================================================================
--- trac/admin/templates/admin_logging.html	(revision 5999)
+++ trac/admin/templates/admin_logging.html	(working copy)
@@ -52,9 +52,6 @@
         <div class="buttons">
           <input type="submit" value="Apply changes"/>
         </div>
-        <p class="help">
-          You may need to restart the server for these changes to take effect.
-        </p>
       </fieldset>
     </form>
   </body>
Index: trac/config.py
===================================================================
--- trac/config.py	(revision 5999)
+++ trac/config.py	(working copy)
@@ -174,13 +174,16 @@
             fileobj.close()
 
     def parse_if_needed(self):
-        # Load global configuration
         if not self.filename or not os.path.isfile(self.filename):
-            return
+            return False
+
+        changed = False
         modtime = os.path.getmtime(self.filename)
         if modtime > self._lastmtime:
+            self.parser._sections = {}
             self.parser.read(self.filename)
             self._lastmtime = modtime
+            changed = True
 
         if self.parser.has_option('inherit', 'file'):
             filename = self.parser.get('inherit', 'file')
@@ -189,12 +192,19 @@
                                         filename)
             if not self.parent or self.parent.filename != filename:
                 self.parent = Configuration(filename)
+                changed = True
             else:
-                self.parent.parse_if_needed()
+                changed |= self.parent.parse_if_needed()
         elif self.parent:
+            changed = True
             self.parent = None
 
+        return changed
 
+    def touch(self):
+        os.utime(self.filename, None)
+
+
 class Section(object):
     """Proxy for a specific configuration section.
     
@@ -292,7 +302,9 @@
         """
         if self.config.parser.has_option(self.name, name):
             path = self.config.parser.get(self.name, name)
-            if path and not os.path.isabs(path):
+            if not path:
+                return default
+            if not os.path.isabs(path):
                 path = os.path.join(os.path.dirname(self.config.filename),
                                     path)
             return os.path.normcase(os.path.realpath(path))
Index: trac/tests/env.py
===================================================================
--- trac/tests/env.py	(revision 5999)
+++ trac/tests/env.py	(working copy)
@@ -1,4 +1,5 @@
 from trac import db_default
+from trac.db import sqlite_backend
 from trac.env import Environment
 
 import os.path
Index: trac/log.py
===================================================================
--- trac/log.py	(revision 5999)
+++ trac/log.py	(working copy)
@@ -44,7 +44,7 @@
             format = '%(asctime)s ' + format
     datefmt = ''
     if logtype == 'stderr':
-        datefmt = '%X'        
+        datefmt = '%X'
     level = level.upper()
     if level in ('DEBUG', 'ALL'):
         logger.setLevel(logging.DEBUG)
@@ -58,6 +58,9 @@
         logger.setLevel(logging.WARNING)
     formatter = logging.Formatter(format, datefmt)
     hdlr.setFormatter(formatter)
-    logger.addHandler(hdlr) 
+    logger.addHandler(hdlr)
 
+    # Remember our handler so that we can remove it later
+    logger._trac_handler = hdlr 
+
     return logger
Index: trac/util/__init__.py
===================================================================
--- trac/util/__init__.py	(revision 5999)
+++ trac/util/__init__.py	(working copy)
@@ -228,6 +228,10 @@
         pkginfo = email.message_from_string(dist.get_metadata('PKG-INFO'))
         for attr in [key for key in attrs if key in pkginfo]:
             info[normalize(attr)] = pkginfo[attr]
+    except IOError, e:
+        err = 'Failed to read PKG-INFO file for %s: %s' % (dist, e)
+        for attr in attrs:
+            info[normalize(attr)] = err
     except email.Errors.MessageError, e:
         err = 'Failed to parse PKG-INFO file for %s: %s' % (dist, e)
         for attr in attrs:

