traceback formatting
diff --git a/doc/build/genhtml.py b/doc/build/genhtml.py
index 2e08c09..02975c2 100644
--- a/doc/build/genhtml.py
+++ b/doc/build/genhtml.py
@@ -28,7 +28,7 @@
 template_dirs = ['./templates', './output']
 output = os.path.dirname(os.getcwd())
 
-lookup = TemplateLookup(template_dirs)
+lookup = TemplateLookup(template_dirs, module_directory='./modules')
 
 def genfile(name, toc):
     infile = name + ".html"
@@ -38,7 +38,12 @@
     outfile.write(lookup.get_template(infile).render(toc=toc, extension='html'))
     
 for filename in files:
-    genfile(filename, root)
+    try:
+        genfile(filename, root)
+    except Exception, e:
+        import mako.exceptions
+        print mako.exceptions.get_error_template().render(error=e)
+        break
 
 
         
diff --git a/doc/build/templates/base.html b/doc/build/templates/base.html
index c272b14..a0c4f6e 100644
--- a/doc/build/templates/base.html
+++ b/doc/build/templates/base.html
@@ -4,10 +4,10 @@
     # bootstrap TOC structure from request args, or pickled file if not present.
     import cPickle as pickle
     import os, time
-    print "base.myt generating from table of contents for file %s" % ("<todo: get the file>")
+    print "%s generating from table of contents for file %s" % (local.filename, self.filename)
     toc = context.get('toc')
     if toc is None:
-        filename = os.path.join(os.path.dirname(self.template.filename), 'table_of_contents.pickle')
+        filename = os.path.join(os.path.dirname(self.filename), 'table_of_contents.pickle')
         toc = pickle.load(file(filename))
     version = toc.version
     last_updated = toc.last_updated
@@ -23,8 +23,8 @@
 
 <div class="docheader">
 
-<h1><% toc.root.doctitle %></h1>
-<div class="">Version: <% version %>   Last Updated: <% time.strftime('%x %X', time.localtime(last_updated)) %></div>
+<h1>${toc.root.doctitle}</h1>
+<div class="">Version: ${version}   Last Updated: ${time.strftime('%x %X', time.localtime(last_updated))}</div>
 </div>
 
 ${next.body(toc=toc)}
diff --git a/doc/build/templates/nav.html b/doc/build/templates/nav.html
index 82c8cf0..bbc5480 100644
--- a/doc/build/templates/nav.html
+++ b/doc/build/templates/nav.html
@@ -55,11 +55,6 @@
 <div class="topnavmain">
 	<div class="topnavheader">${ item.description }</div>
 	<div class="topnavitems">
-	% if tocns is UNDEFINED:
-	    <%
-	        raise "its undefined"
-	    %>
-	% endif
 	${tocns.printtoc(root=item, current=None, full=True, anchor_toplevel=True)}
 	</div>
 </div>
diff --git a/lib/mako/codegen.py b/lib/mako/codegen.py
index a817a32..a0ec91d 100644
--- a/lib/mako/codegen.py
+++ b/lib/mako/codegen.py
@@ -20,11 +20,8 @@
     def render(self):
         buf = util.FastEncodingBuffer()
         printer = PythonPrinter(buf)
-
         _GenerateRenderMethod(printer, self, self.node)
-
         return buf.getvalue()
-
         
 class _GenerateRenderMethod(object):
     def __init__(self, printer, compiler, node):
@@ -87,12 +84,11 @@
         module_identifiers.declared = module_ident
         
         # module-level names, python code
-        self.printer.writeline("from mako import runtime")
+        self.printer.writeline("from mako import runtime, filters")
+        self.printer.writeline("UNDEFINED = runtime.UNDEFINED")
         self.printer.writeline("_magic_number = %s" % repr(MAGIC_NUMBER))
         self.printer.writeline("_modified_time = %s" % repr(time.time()))
         self.printer.writeline("_template_filename=%s" % repr(self.compiler.filename))
-        self.printer.writeline("UNDEFINED = runtime.UNDEFINED")
-        self.printer.writeline("from mako import filters")
 
         main_identifiers = module_identifiers.branch(self.node)
         module_identifiers.topleveldefs = module_identifiers.topleveldefs.union(main_identifiers.topleveldefs)
@@ -138,22 +134,22 @@
             self.printer.write_indented_block(n.text)
 
     def write_inherit(self, node):
-        self.printer.writeline("def _inherit(context):")
-        self.printer.writeline("generate_namespaces(context)")
+        self.printer.writeline("def _mako_inherit(context):")
+        self.printer.writeline("_mako_generate_namespaces(context)")
         self.printer.writeline("return runtime.inherit_from(context, %s)" % (repr(node.attributes['file'])))
         self.printer.writeline(None)
 
     def write_namespaces(self, namespaces):
         self.printer.writelines(
-            "def get_namespace(context, name):",
+            "def _mako_get_namespace(context, name):",
             "try:",
             "return context.namespaces[(render, name)]",
             "except KeyError:",
-            "generate_namespaces(context)",
+            "_mako_generate_namespaces(context)",
             "return context.namespaces[(render, name)]",
             None,None
             )
-        self.printer.writeline("def generate_namespaces(context):")
+        self.printer.writeline("def _mako_generate_namespaces(context):")
         for node in namespaces.values():
             self.write_source_comment(node)
             if len(node.nodes):
@@ -221,7 +217,7 @@
                 else:
                     self.write_inline_def(comp, identifiers)
             elif ident in self.compiler.namespaces:
-                self.printer.writeline("%s = get_namespace(context, %s)" % (ident, repr(ident)))
+                self.printer.writeline("%s = _mako_get_namespace(context, %s)" % (ident, repr(ident)))
             else:
                 if first is not None:
                     self.printer.writeline("%s = %s.get(%s, context.get(%s, UNDEFINED))" % (ident, first, repr(ident), repr(ident)))
@@ -230,7 +226,7 @@
         
     def write_source_comment(self, node):
         if self.last_source_line != node.lineno:
-            self.printer.writeline("# SOURCE LINE %d" % node.lineno, is_comment=True)
+            self.printer.writeline("# SOURCE LINE %d" % node.lineno)
             self.last_source_line = node.lineno
 
     def write_def_decl(self, node, identifiers):
@@ -331,7 +327,6 @@
             if not self.in_def and len(self.identifiers.locally_assigned) > 0:
                 # if we are the "template" def, fudge locally declared/modified variables into the "__locals" dictionary,
                 # which is used for def calls within the same template, to simulate "enclosing scope"
-                #self.printer.writeline('__locals.update(%s)' % (",".join(["%s=%s" % (x, x) for x in node.declared_identifiers()])))
                 self.printer.writeline('__locals.update(dict([(k, v) for k, v in locals().iteritems() if k in [%s]]))' % ','.join([repr(x) for x in node.declared_identifiers()]))
                 
     def visitIncludeTag(self, node):
diff --git a/lib/mako/exceptions.py b/lib/mako/exceptions.py
index b15f737..c9640e6 100644
--- a/lib/mako/exceptions.py
+++ b/lib/mako/exceptions.py
@@ -6,6 +6,8 @@
 
 """exception classes"""
 
+import traceback, sys, re
+
 class MakoException(Exception):
     pass
 
@@ -32,4 +34,73 @@
         self.filename = filename
         
 class TemplateLookupException(MakoException):
-    pass
\ No newline at end of file
+    pass
+    
+def rich_traceback():
+    """format a traceback from sys.exc_info() into 7-item tuples, containing
+    the regular four traceback tuple items, plus the original template 
+    filename, the line number adjusted relative to the template source, and
+    code line from that line number of the template."""
+    import mako.template
+    mods = {}
+    (type, value, trcback) = sys.exc_info()
+    rawrecords = traceback.extract_tb(trcback)
+    # this line extends the stack all the way back....shouldnt be needed...
+    # rawrecords = traceback.extract_stack() + rawrecords 
+    new_trcback = []
+    for filename, lineno, function, line in rawrecords:
+        try:
+            (line_map, template_lines) = mods[filename]
+        except KeyError:
+            try:
+                info = mako.template._get_module_info(filename)
+                module_source = info.code
+                template_source = info.source
+                template_filename = info.template_filename
+            except KeyError:
+                new_trcback.append((filename, lineno, function, line, None, None, None))
+                continue
+
+            template_ln = module_ln = 1
+            line_map = {}
+            for line in module_source.split("\n"):
+                match = re.match(r'\s*# SOURCE LINE (\d+)', line)
+                if match:
+                    template_ln = int(match.group(1))
+                else:
+                    template_ln += 1
+                module_ln += 1
+                line_map[module_ln] = template_ln
+            template_lines = [line for line in template_source.split("\n")]
+            mods[filename] = (line_map, template_lines)
+
+        template_ln = line_map[lineno]
+        template_line = template_lines[template_ln - 1]
+        
+        new_trcback.append((filename, lineno, function, line, template_filename, template_ln, template_line))
+    return (type, value, new_trcback)
+    
+# TODO: this is scratch, make a module for exception reporting templates
+def get_error_template():
+    import mako.template
+    return mako.template.Template("""
+<%!
+    from mako.exceptions import rich_traceback
+%>
+<html>
+<body>
+    Error !
+<%
+    (type, value, trcback) = rich_traceback()
+%>
+
+${str(type)} - ${value}
+
+% for (filename, lineno, function, line, template_filename, template_ln, template_line) in trcback:
+        % if template_line:
+        ${template_filename} ${template_ln} ${template_line} <br/>
+        % endif
+% endfor
+</body>
+</html>
+""")
\ No newline at end of file
diff --git a/lib/mako/lookup.py b/lib/mako/lookup.py
index c3bddb1..6babbc7 100644
--- a/lib/mako/lookup.py
+++ b/lib/mako/lookup.py
@@ -4,7 +4,7 @@
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
-import os, stat, posixpath, re
+import os, stat, posixpath
 from mako import exceptions, util
 from mako.template import Template
 
@@ -24,7 +24,7 @@
         raise NotImplementedError()
         
 class TemplateLookup(TemplateCollection):
-    def __init__(self, directories=None, module_directory=None, filesystem_checks=False, collection_size=60, format_exceptions=False, error_handler=None, output_encoding=None):
+    def __init__(self, directories=None, module_directory=None, filesystem_checks=False, collection_size=-1, format_exceptions=False, error_handler=None, output_encoding=None):
         self.directories = directories or []
         self.module_directory = module_directory
         self.filesystem_checks = filesystem_checks
@@ -50,9 +50,6 @@
                 else:
                     raise exceptions.TemplateLookupException("Cant locate template for uri '%s'" % uri)
 
-    def __ident_from_uri(self, uri):
-        return re.sub(r"\W", "_", uri)
-        
     def __load(self, filename, uri):
         self._mutex.acquire()
         try:
@@ -62,7 +59,7 @@
             except KeyError:
                 pass
             try:
-                self.__collection[uri] = Template(identifier=self.__ident_from_uri(uri), description=uri, filename=filename, lookup=self, **self.template_args)
+                self.__collection[uri] = Template(identifier=uri, description=uri, filename=filename, lookup=self, **self.template_args)
                 return self.__collection[uri]
             except:
                 self.__collection.pop(uri, None)
diff --git a/lib/mako/pygen.py b/lib/mako/pygen.py
index 310d1f3..24b1655 100644
--- a/lib/mako/pygen.py
+++ b/lib/mako/pygen.py
@@ -38,7 +38,7 @@
         self.stream.write(text)
         
     def write_indented_block(self, block):
-        """print a line or lines of python which already contains indentation.
+        """print a line or lines of python which already contain indentation.
         
         The indentation of the total block of lines will be adjusted to that of
         the current indent level.""" 
@@ -47,10 +47,11 @@
             self.line_buffer.append(l)
     
     def writelines(self, *lines):
+        """print a series of lines of python."""
         for line in lines:
             self.writeline(line)
                 
-    def writeline(self, line, is_comment=False):
+    def writeline(self, line):
         """print a line of python, indenting it according to the current indent level.
         
         this also adjusts the indentation counter according to the content of the line."""
@@ -69,6 +70,8 @@
         else:
             hastext = True
 
+        is_comment = line and len(line) and line[0] == '#'
+        
         # see if this line should decrease the indentation level
         if (not decreased_indent and 
             not is_comment and 
diff --git a/lib/mako/runtime.py b/lib/mako/runtime.py
index bf01269..15b35bb 100644
--- a/lib/mako/runtime.py
+++ b/lib/mako/runtime.py
@@ -9,14 +9,14 @@
 import inspect
         
 class Context(object):
-    """provides runtime namespace and output buffer for templates."""
+    """provides runtime namespace, output buffer, and various callstacks for templates."""
     def __init__(self, buffer, **data):
         self._buffer_stack = [buffer]
         self._data = data
         self._with_template = None
         self.namespaces = {}
         
-        # "capture" function to buffer def calls
+        # "capture" function which proxies to the generic "capture" function
         data['capture'] = lambda x, *args, **kwargs: capture(self, x, *args, **kwargs)
         
         # "caller" stack used by def calls with content
@@ -30,8 +30,10 @@
     def _put(self, key, value):
         self._data[key] = value
     def push_buffer(self):
+        """push a capturing buffer onto this Context."""
         self._buffer_stack.append(util.FastEncodingBuffer())
     def pop_buffer(self):
+        """pop the most recent capturing buffer from this Context."""
         return self._buffer_stack.pop()
     def get(self, key, default=None):
         return self._data.get(key, default)
@@ -52,6 +54,7 @@
         c._data.update(d)
         return c
     def clean_inheritance_tokens(self):
+        """create a new copy of this Context with tokens related to inheritance state removed."""
         c = self._copy()
         x = c._data
         x.pop('self', None)
@@ -66,28 +69,21 @@
         return getattr(self.target[-1], key)
         
 class Undefined(object):
-    """represtents undefined values"""
+    """represents an undefined value in a template."""
     def __str__(self):
         raise NameError("Undefined")
 UNDEFINED = Undefined()
-        
 
-class _AttrGateway(object):
-    def __init__(self,ns):
-        self.ns = ns
-    def __getattr__(self, key):
-        return getattr(self.ns.template.module, key)
-        
+   
 class Namespace(object):
     """provides access to collections of rendering methods, which can be local, from other templates, or from imported modules"""
     def __init__(self, name, context, module=None, template=None, templateuri=None, callables=None, inherits=None, populate_self=True):
         self.name = name
-        self.module = module
+        self._module = module
         if templateuri is not None:
             self.template = _lookup_template(context, templateuri)
         else:
             self.template = template
-        self.attributes = _AttrGateway(self)
         self.context = context
         self.inherits = inherits
         if callables is not None:
@@ -96,7 +92,10 @@
             self.callables = None
         if populate_self and self.template is not None:
             (lclcallable, self.context) = _populate_self_namespace(context, self.template, self_ns=self)
-        
+
+    module = property(lambda s:s._module or s.template.module)
+    filename = property(lambda s:s._module and s._module.__file__ or s.template.filename)
+    
     def __getattr__(self, key):
         if self.callables is not None:
             try:
@@ -113,9 +112,9 @@
                     callable_ = None
             if callable_ is not None:
                 return lambda *args, **kwargs:callable_(self.context, *args, **kwargs)
-        if self.module is not None:
+        if self._module is not None:
             try:
-                callable_ = getattr(self.module, key)
+                callable_ = getattr(self._module, key)
                 return lambda *args, **kwargs:callable_(self.context, *args, **kwargs)
             except AttributeError:
                 pass
@@ -124,6 +123,7 @@
         raise exceptions.RuntimeException("Namespace '%s' has no member '%s'" % (self.name, key))
 
 def capture(context, callable_, *args, **kwargs):
+    """execute the given template def, capturing the output into a buffer."""
     context.push_buffer()
     try:
         callable_(*args, **kwargs)
@@ -132,11 +132,14 @@
         return buf.getvalue()
         
 def include_file(context, uri, import_symbols):
+    """locate the template from the given uri and include it in the current output."""
     template = _lookup_template(context, uri)
     (callable_, ctx) = _populate_self_namespace(context.clean_inheritance_tokens(), template)
     callable_(ctx)
         
 def inherit_from(context, uri):
+    """called by the _inherit method in template modules to set up the inheritance chain at the start
+    of a template's execution."""
     template = _lookup_template(context, uri)
     self_ns = context['self']
     ih = self_ns
@@ -144,12 +147,12 @@
         ih = ih.inherits
     lclcontext = context.locals_({'next':ih})
     ih.inherits = Namespace("self:%s" % template.description, lclcontext, template = template, populate_self=False)
-    context._data['parent'] = ih.inherits
-    callable_ = getattr(template.module, '_inherit', None)
+    context._data['parent'] = lclcontext._data['local'] = ih.inherits
+    callable_ = getattr(template.module, '_mako_inherit', None)
     if callable_  is not None:
         return callable_(lclcontext)
     else:
-        gen_ns = getattr(template.module, 'generate_namespaces', None)
+        gen_ns = getattr(template.module, '_mako_generate_namespaces', None)
         if gen_ns is not None:
             gen_ns(context)
         return (template.callable_, lclcontext)
@@ -161,14 +164,14 @@
 def _populate_self_namespace(context, template, self_ns=None):
     if self_ns is None:
         self_ns = Namespace('self:%s' % template.description, context, template=template, populate_self=False)
-    context._data['self'] = self_ns
-    if hasattr(template.module, '_inherit'):
-        return template.module._inherit(context)
+    context._data['self'] = context._data['local'] = self_ns
+    if hasattr(template.module, '_mako_inherit'):
+        return template.module._mako_inherit(context)
     else:
         return (template.callable_, context)
 
 def _render(template, callable_, args, data, as_unicode=False):
-    """given a Template and a callable_ from that template, create a Context and return the string output."""
+    """create a Context and return the string output of the given template and template callable."""
     if as_unicode:
         buf = util.FastEncodingBuffer()
     elif template.output_encoding:
@@ -218,7 +221,7 @@
                 if not result:
                     raise error
             else:
-                # TODO
+                # TODO - friendly error formatting
                 source = _get_template_source(callable_)
                 raise error
     else:
diff --git a/lib/mako/template.py b/lib/mako/template.py
index 1787c01..4313013 100644
--- a/lib/mako/template.py
+++ b/lib/mako/template.py
@@ -10,50 +10,76 @@
 from mako.lexer import Lexer
 from mako.codegen import Compiler
 from mako import runtime, util, exceptions
-import imp, time, weakref, tempfile, shutil,  os, stat, posixpath, sys
+import imp, time, weakref, tempfile, shutil,  os, stat, posixpath, sys, re
 
 _modules = weakref.WeakValueDictionary()
 _inmemory_templates = weakref.WeakValueDictionary()
 
-class _ModuleMarker(object):
-    """enables weak-referencing to module instances"""
-    def __init__(self, module):
+class _ModuleInfo(object):
+    def __init__(self, module, module_filename, template, template_filename, module_source, template_source):
         self.module = module
-
+        self.module_filename = module_filename
+        self.template_filename = template_filename
+        self.module_source = module_source
+        self.template_source = template_source
+        _modules[module.__name__] = template._mmarker = self
+        if module_filename:
+            _modules[module_filename] = self
+    def _get_code(self):
+        if self.module_source is not None:
+            return self.module_source
+        else:
+            return file(self.module_filename).read()
+    code = property(_get_code)
+    def _get_source(self):
+        if self.template_source is not None:
+            return self.template_source
+        else:
+            return file(self.template_filename).read()
+    source = property(_get_source)
+    
 class Template(object):
     """a compiled template"""
-    def __init__(self, text=None,identifier=None, description=None, filename=None, format_exceptions=False, error_handler=None, lookup=None, output_encoding=None, module_directory=None):
+    def __init__(self, text=None, identifier=None, description=None, filename=None, format_exceptions=False, error_handler=None, lookup=None, output_encoding=None, module_directory=None):
         """construct a new Template instance using either literal template text, or a previously loaded template module
         
         text - textual template source, or None if a module is to be provided
         
-        identifier - the "id" of this template.  defaults to the identifier of the given module, or for text
-        the hex string of this Template's object id
+        identifier - the "id" of this template. defaults to the 
+        full filename given, or "memory:(hex id of this Template)" if no filename
         
-        filename - filename of the source template, compiled into the module.
+        filename - filename of the source template, if any
         
-        format_exceptions - if caught exceptions should be formatted as template output, including a stack
-        trace adjusted to the source template
+        format_exceptions - catch exceptions and format them into an error display template
         """
-        self.identifier = identifier or "memory:" + hex(id(self))
+        if identifier:
+            self.identifier = re.sub(r'\W', "_", identifier)
+        elif filename:
+            self.identifier = re.sub(r'\W', "_", filename)
+        else:
+            self.identifier = "memory:" + hex(id(self))
+            
         if text is not None:
             (code, module) = _compile_text(text, self.identifier, filename)
             _inmemory_templates[module.__name__] = self
             self._code = code
             self._source = text
+            _ModuleInfo(module, None, self, filename, code, text)
         elif filename is not None:
             if module_directory is not None:
-                path = posixpath.join(module_directory, identifier + ".py")
+                path = posixpath.join(module_directory, self.identifier + ".py")
                 filemtime = os.stat(filename)[stat.ST_MTIME]
                 if not os.access(path, os.F_OK) or os.stat(path)[stat.ST_MTIME] < filemtime:
                     util.verify_directory(module_directory)
                     _compile_module_file(file(filename).read(), identifier, filename, path)
                 module = imp.load_source(self.identifier, path, file(path))
                 del sys.modules[self.identifier]
+                _ModuleInfo(module, path, self, filename, None, None)
             else:
                 (code, module) = _compile_text(file(filename).read(), self.identifier, filename)
                 self._source = None
                 self._code = code
+                _ModuleInfo(module, None, self, filename, code, None)
         else:
             raise exceptions.RuntimeException("Template requires text or filename")
 
@@ -65,10 +91,9 @@
         self.error_handler = error_handler
         self.lookup = lookup
         self.output_encoding = output_encoding
-        _modules[module.__name__] = _ModuleMarker(module)
 
     source = property(lambda self:_get_template_source(self.callable_), doc="""return the template source code for this Template.""")
-    code = property(lambda self:_get_module_source(self.callable_), doc="""return the module source code for this Template""")
+    code = property(lambda self:_get_module_source_from_callable(self.callable_), doc="""return the module source code for this Template""")
         
     def render(self, *args, **data):
         """render the output of this template as a string.
@@ -105,13 +130,9 @@
 def _compile_text(text, identifier, filename):
     node = Lexer(text, filename).parse()
     source = Compiler(node, filename).render()
-#    if filename is not None:
-#        file(filename + ".py", "w").write(source)
-#    else:
-#        print source
     cid = identifier
     module = imp.new_module(cid)
-    code = compile(source, cid + " " + str(filename), 'exec')
+    code = compile(source, cid, 'exec')
     exec code in module.__dict__, module.__dict__
     return (source, module)
 
@@ -123,34 +144,9 @@
     os.close(dest)
     shutil.move(name, outputpath)
 
-def _get_template_source(callable_):
-    """return the source code for the template that produced the given rendering callable"""
-    name = callable_.func_globals['__name__']
-    try:
-        template = _inmemory_templates[name]
-        if template._source  is not None:
-            return template._source
-    except KeyError:
-        pass
-    module = _modules[name].module
-    filename = module._template_filename
-    if filename is None:
-        if not filename:
-            raise exceptions.RuntimeException("Cant get source code or template filename for template: %s" % name)
-    return file(filename).read()
-
-def _get_module_source(callable_):
-    name = callable_.func_globals['__name__']
-    try:
-        template = _inmemory_templates[name]
-        if template._code is not None:
-            return template._code
-    except KeyError:
-        pass
-    module = _modules[name].module
-    filename = module.__file__
-    if filename is None:
-        if not filename:
-            raise exceptions.RuntimeException("Cant get module source code or module filename for template: %s" % name)
-    return file(filename).read()
-                
+def _get_module_info_from_callable(callable_):
+    return _get_module_info(callable_.func_globals['__name__'])
+    
+def _get_module_info(filename):
+    return _modules[filename]
+