Index: trac/env.py
===================================================================
--- trac/env.py	(revision 4591)
+++ trac/env.py	(working copy)
@@ -30,6 +30,10 @@
 __all__ = ['Environment', 'IEnvironmentSetupParticipant', 'open_environment']
 
 
+class InterfaceError(TracError):
+    """Raised when a component does not match its declared interfaces."""
+
+
 class IEnvironmentSetupParticipant(Interface):
     """Extension point interface for components that need to participate in the
     creation and upgrading of Trac environments, for example to create
@@ -74,6 +78,9 @@
         setups, usually involving running Trac behind a HTTP proxy, you may
         need to use this option to force Trac to use the correct URL.""")
 
+    check_interfaces = BoolOption('trac', 'check_interfaces', True,
+        """Whether to check plugin interface conformance.""")
+
     project_name = Option('project', 'name', 'My Project',
         """Name of the project.""")
 
@@ -171,6 +178,8 @@
         Every component activated through the `Environment` object gets three
         member variables: `env` (the environment object), `config` (the
         environment configuration) and `log` (a logger object)."""
+        if self.check_interfaces:
+            self._check_interfaces(component)
         component.env = self
         component.config = self.config
         component.log = self.log
@@ -408,6 +417,82 @@
         return self._abs_href
     abs_href = property(_get_abs_href, 'The application URL')
 
+    def _check_interfaces(self, component):
+        """Check that component conforms with its declared interfaces."""
+        import inspect
+        for interface in component._implements:
+            for method_name, imethod in \
+                    inspect.getmembers(interface, inspect.ismethod):
+                try:
+                    cmethod = getattr(component, method_name)
+                except AttributeError:
+                    method_signature = method_name + \
+                        inspect.formatargspec(*inspect.getargspec(
+                                              imethod))
+                    raise InterfaceError('Component %s does not have Interface ' 
+                                         'method %s.%s'
+                                         (self._format_class(component.__class__),
+                                          self._format_class(interface)),
+                                          method_signature)
+                self._compare_method(component, interface, imethod, cmethod)
+
+    def _compare_method(self, component, interface, imethod, cmethod):
+        import inspect
+        imethod_spec = inspect.getargspec(imethod)
+        if not imethod_spec[0] or imethod_spec[0][0] != 'self':
+            imethod_spec[0].insert(0, 'self')
+        cmethod_spec = inspect.getargspec(cmethod)
+
+        imethod_sig = imethod.__name__ + \
+            inspect.formatargspec(*imethod_spec)
+        cmethod_sig = cmethod.__name__ + \
+            inspect.formatargspec(*cmethod_spec)
+
+        component_fmt = self._format_class(component.__class__)
+        interface_fmt = self._format_class(interface)
+
+        # Can't check varargs, but log them
+        if cmethod_spec[2] or imethod_spec[2]:
+            if cmethod_spec[2]:
+                self.log.debug("Component method %s.%s contains varargs, can't "
+                               "check interface conformance."
+                               % (component_fmt, cmethod_sig))
+            if imethod_spec[2]:
+                self.log.debug("Interface method %s.%s contains varargs, can't "
+                               "check interface conformance for component %s."
+                               % (interface_fmt, imethod_sig, component_fmt))
+            return
+
+        if getattr(interface, 'deprecated', False):
+            self.log.warning('Interface %s used by component %s is deprecated '
+                             'and will be removed in the next version of Trac.'
+                             % (interface_fmt, component_fmt))
+
+        if getattr(imethod, 'deprecated', False):
+            self.log.warning('Interface method %s.%s used by component %s is '
+                             'deprecated and will be removed in the next '
+                             'version of Trac.' % (interface_fmt, imethod_sig,
+                                                   component_fmt))
+
+        # TODO This could be smarter. Possibly checking to see if there are
+        # keyword args in the interface indicating that some of the arguments
+        # are optional (this has occurred over time due to backwards
+        # compatibility).
+        if len(imethod_spec[0]) != len(cmethod_spec[0]):
+            self.log.error('Component method %s.%s does not match '
+                           'Interface method %s.%s'
+                           % (component_fmt, cmethod_sig, interface_fmt,
+                              imethod_sig))
+        if imethod_spec != cmethod_spec:
+            self.log.warning('Component method %s.%s arguments differ in '
+                             'name from the Interface method %s.%s'
+                             % (component_fmt, cmethod_sig, interface_fmt,
+                                imethod_sig))
+
+    def _format_class(self, cls):
+        return '%s.%s' % (cls.__module__, cls.__name__)
+
+
 
 class EnvironmentSetup(Component):
     implements(IEnvironmentSetupParticipant)
