diff --git a/trac/config.py b/trac/config.py
--- a/trac/config.py
+++ b/trac/config.py
@@ -141,10 +141,10 @@
     def sections(self):
         """Return a list of section names."""
         sections = set(self.parser.sections())
-        parent = self.parent
-        while parent:
-            sections |= set(parent.parser.sections())
-            parent = parent.parent
+        if self.parent:
+            sections.update(self.parent.sections())
+        else:
+            sections.update(self.defaults().keys())
         return sorted(sections)
 
     def has_option(self, section, option):
@@ -153,20 +153,13 @@
         
         (since Trac 0.11)
         """
-        # Check project trac.ini
-        for file_option, val in self.options(section):
-            if file_option == option:
+        if self.parser.has_section(section):
+            if option in self.parser.options(section):
                 return True
-        # Check parent trac.ini
         if self.parent:
-            for parent_option, val in self.parent.options(section):
-                if parent_option == option:
-                    return True
-        # Check the registry
-        if (section, option) in Option.registry:
-            return True
-        # Not found
-        return False
+            return self.parent.has_option(section, option)
+        else:
+            return (section, option) in Option.registry
 
     def save(self):
         """Write the configuration options to the primary file."""
@@ -254,7 +247,7 @@
             return True
         if self.config.parent:
             return name in self.config.parent[self.name]
-        return False
+        return Option.registry.has_key((self.name, name))
 
     def __iter__(self):
         options = set()
@@ -266,6 +259,10 @@
             for option in self.config.parent[self.name]:
                 if option.lower() not in options:
                     yield option
+        else:
+            for section, option in Option.registry.keys():
+                if section == self.name and option.lower() not in options:
+                    yield option
 
     def __repr__(self):
         return '<Section [%s]>' % (self.name)
diff --git a/trac/tests/config.py b/trac/tests/config.py
--- a/trac/tests/config.py
+++ b/trac/tests/config.py
@@ -192,6 +192,11 @@
         self._write(['[a]', 'option = x', '[b]', 'option = y'])
         config = self._read()
         self.assertEquals(['a', 'b'], config.sections())
+        
+        class Foo(object):
+            option_c = Option('c', 'option', 'value')
+        
+        self.assertEquals(['a', 'b', 'c'], config.sections())
 
     def test_options(self):
         self._write(['[a]', 'option = x', '[b]', 'option = y'])
@@ -199,6 +204,12 @@
         self.assertEquals(('option', 'x'), iter(config.options('a')).next())
         self.assertEquals(('option', 'y'), iter(config.options('b')).next())
         self.assertRaises(StopIteration, iter(config.options('c')).next)
+        
+        class Foo(object):
+            option_a = Option('a', 'b', 'c')
+        
+        self.assertEquals([('option', 'x'), ('b', 'c')],
+                                                list(config.options('a')))
 
     def test_has_option(self):
         config = self._read()
@@ -207,6 +218,11 @@
         config = self._read()
         self.assertEquals(True, config.has_option('a', 'option'))
 
+        class Foo(object):
+            option_a = Option('a', 'option2', 'x2')
+        
+        self.assertEquals(True, config.has_option('a', 'option2'))
+
     def test_reparse(self):
         self._write(['[a]', 'option = x'])
         config = self._read()

