diff --git a/CHANGES b/CHANGES
index 584c7525994beda8ac5a0e1335da34654cacddee..ba128551434f763fd0d76f93b2c0cde3c8ea0545 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,15 @@
 0.2.3
+- the <%namespacename:defname> syntax described at
+  http://techspot.zzzeek.org/?p=28 has now 
+  been added as a built in syntax, and is recommended
+  as a more modern syntax versus <%call expr="expression">.
+  The %call tag itself will always remain, 
+  with <%namespacename:defname> presenting a more HTML-like
+  alternative to calling defs, both plain and 
+  nested.  Many examples of the new syntax are in the
+  "Calling a def with embedded content" section
+  of the docs.
+  
 - added support for Jython 2.5.
 
 - cache module now uses Beaker's CacheManager
@@ -24,12 +35,14 @@
   been already persisted for that template.
   [ticket:92]
 
-- For cache changes to work fully, Beaker 1.0.4 is required.
+- For cache changes to work fully, Beaker 1.1 is required.
   1.0.1 and up will work as well with the exception of 
-  cache expiry.  Note that Beaker 1.0.4 is **required**
+  cache expiry.  Note that Beaker 1.1 is **required**
   for applications which use dynamically generated keys,
   since previous versions will permanently store state in memory 
-  for each individual key and thus use up all available memory.
+  for each individual key, thus consuming all available 
+  memory for an arbitrarily large number of distinct 
+  keys.
   
 - fixed the html_error_template not handling tracebacks from
   normal .py files with a magic encoding comment [ticket:88]
diff --git a/doc/build/content/defs.txt b/doc/build/content/defs.txt
index 2b89aa9f63b66286c6dbbd2ff4bc7cf025c89f57..4e5cd46f4e7e79ef7582623d1c7b60e4d05ea821 100644
--- a/doc/build/content/defs.txt
+++ b/doc/build/content/defs.txt
@@ -129,7 +129,9 @@ Assigning to a name inside of a def declares that name as local to the scope of
 
 A flip-side to def within def is a def call with content.  This is where you call a def, and at the same time declare a block of content (or multiple blocks) that can be used by the def being called.  The main point of such a call is to create custom, nestable tags, just like any other template language's custom-tag creation system - where the external tag controls the execution of the nested tags and can communicate state to them.  Only with Mako, you don't have to use any external Python modules, you can define arbitrarily nestable tags right in your templates.
 
-To achieve this, the target def is invoked using the `<%call>` tag instead of the normal `${}` syntax.  The target def then gets a variable `caller` placed in its context which contains a **namespace** containing the body and other defs defined within the `<%call>` tag.  The body itself is referenced by the method `body()`:
+To achieve this, the target def is invoked using the form `<%namepacename:defname>` instead of the normal `${}` syntax.  This syntax, introduced in Mako 0.2.3, is functionally equivalent another tag known as `call`, which takes the form `<%call expr='namespacename.defname(args)'>`.  While `%call` is available in all versions of Mako, the newer style is probably more familiar looking.  The `namespace` portion of the call is the name of the **namespace** in which the def is defined - in the most simple cases, this can be `local` or `self` to reference the current template's namespace (the difference between `local` and `self` is one of inheritance - see [namespaces_builtin](rel:namespaces_builtin) for details).
+
+When the target def is invoked, a variable `caller` is placed in its context which contains another namespace containing the body and other defs defined by the caller.  The body itself is referenced by the method `body()`.  Below, we build a `%def` that operates upon `caller.body()` to invoke the body of the custom tag:
 
     <%def name="buildtable()">
         <table>
@@ -139,9 +141,9 @@ To achieve this, the target def is invoked using the `<%call>` tag instead of th
         </table>
     </%def>
     
-    <%call expr="buildtable()">
+    <%self:buildtable>
         I am the table body.
-    </%call>
+    </%self:buildtable>
     
 This produces the output (whitespace formatted):
 
@@ -152,6 +154,20 @@ This produces the output (whitespace formatted):
         </td></tr>
     </table>
 
+Using the older `%call` syntax looks like:
+
+    <%def name="buildtable()">
+        <table>
+            <tr><td>
+                ${caller.body()}
+            </td></tr>
+        </table>
+    </%def>
+
+    <%call expr="buildtable()">
+        I am the table body.
+    </%call>
+
 The `body()` can be executed multiple times or not at all.  This means you can use def-call-with-content to build iterators, conditionals, etc:
 
     <%def name="lister(count)">
@@ -160,9 +176,9 @@ The `body()` can be executed multiple times or not at all.  This means you can u
         % endfor
     </%def>
     
-    <%call expr="lister(3)">
+    <%self:lister count="${3}">
         hi
-    </%call>
+    </%self:lister>
     
 Produces:
     
@@ -171,54 +187,62 @@ Produces:
     hi
     hi
 
+Notice above we pass `3` as a Python expression, so that it remains as an integer.
+
 A custom "conditional" tag:
     
-    <%def name="conditional(expr)">
-        % if expr:
+    <%def name="conditional(expression)">
+        % if expression:
             ${caller.body()}
         % endif
     </%def>
 
-    <%call expr="conditional(4==4)">
+    <%self:conditional expression="${4==4}">
         im the result
-    </%call>
+    </%self:conditional>
 
 Produces:
 
     {html}
     im the result
 
-But that's not all.  The `body()` function also can handle arguments, which will augment the local namespace of the body callable:
+But that's not all.  The `body()` function also can handle arguments, which will augment the local namespace of the body callable.  The caller must define the arguments which it expects to receive from its target def using the `args` attribute, which is a comma-separated list of argument names.  Below, our `<%def>` calls the `body()` of its caller, passing in an element of data from its argument:
 
     <%def name="layoutdata(somedata)">
         <table>
         % for item in somedata:
             <tr>
             % for col in item:
-                <td>${caller.body(col=col)}</td>\
+                <td>${caller.body(col=col)}</td>
             % endfor
             </tr>
         % endfor
         </table>
     </%def>
+
+    <%self:layoutdata somedata="${[[1,2,3],[4,5,6],[7,8,9]]}" args="col">\
+    Body data: ${col}\
+    </%self:layoutdata>
     
-    <%call expr="layoutdata([[1,2,3],[4,5,6],[7,8,9]])" args="col">
-        Body data: ${col}
-    </%call>
-    
-Produces (whitespace formatted):
+Produces:
 
     {html}
     <table>
-        <tr>
-            <td>Body data: 1</td><td>Body data: 2</td><td>Body data: 3</td>
-        </tr>
-        <tr>
-            <td>Body data: 4</td><td>Body data: 5</td><td>Body data: 6</td>
-        </tr>
-        <tr>
-            <td>Body data: 7</td><td>Body data: 8</td><td>Body data: 9</td>
-        </tr>
+       <tr>
+           <td>Body data: 1</td>
+           <td>Body data: 2</td>
+           <td>Body data: 3</td>
+       </tr>
+       <tr>
+           <td>Body data: 4</td>
+           <td>Body data: 5</td>
+           <td>Body data: 6</td>
+       </tr>
+       <tr>
+           <td>Body data: 7</td>
+           <td>Body data: 8</td>
+           <td>Body data: 9</td>
+       </tr>
     </table>
     
 You don't have to stick to calling just the `body()` function.  The caller can define any number of callables, allowing the `<%call>` tag to produce whole layouts:
@@ -239,7 +263,7 @@ You don't have to stick to calling just the `body()` function.  The caller can d
     </%def>
 
     ## calls the layout def
-    <%call expr="layout()">
+    <%self:layout>
         <%def name="header()">
             I am the header
         </%def>
@@ -251,24 +275,26 @@ You don't have to stick to calling just the `body()` function.  The caller can d
         </%def>
         
             this is the body
-    </%call>
+    </%self:layout>
     
-The above layout would produce (whitespace formatted):
+The above layout would produce:
 
     <div class="mainlayout">
-        <div class="header">
-            I am the header
-        </div>
-        <div class="sidebar">
-            <ul>
-                <li>sidebar 1</li>
-                <li>sidebar 2</li>
-            </ul>
-        </div>
-        <div class="content">
-            this is the body
-        </div>
+       <div class="header">
+       I am the header
+       </div>
+
+       <div class="sidebar">
+       <ul>
+           <li>sidebar 1</li>
+           <li>sidebar 2</li>
+       </ul>
+       </div>
+
+       <div class="content">
+       this is the body
+       </div>
     </div>
 
-The number of things you can do with `<%call>` is enormous.  You can create form widget libraries, such as an enclosing `<FORM>` tag and nested HTML input elements, or portable wrapping schemes using `<div>` or other elements.  You can create tags that interpret rows of data, such as from a database, providing the individual columns of each row to a `body()` callable which lays out the row any way it wants.  Basically anything you'd do with a "custom tag" or tag library in some other system, Mako provides via `<%def>`s or even plain Python callables which are called via `<%call>`.
+The number of things you can do with `<%call>` and/or the `<%namespacename:defname>` calling syntax is enormous.  You can create form widget libraries, such as an enclosing `<FORM>` tag and nested HTML input elements, or portable wrapping schemes using `<div>` or other elements.  You can create tags that interpret rows of data, such as from a database, providing the individual columns of each row to a `body()` callable which lays out the row any way it wants.  Basically anything you'd do with a "custom tag" or tag library in some other system, Mako provides via `<%def>`s and plain Python callables which are invoked via `<%namespacename:defname>` or `<%call>`.
 
diff --git a/doc/build/content/namespaces.txt b/doc/build/content/namespaces.txt
index 7c8ab4689ec79ce45ecf5f8efd08c3ae68d564d1..aab13d62a416fdda54f443f65c19454269e30299 100644
--- a/doc/build/content/namespaces.txt
+++ b/doc/build/content/namespaces.txt
@@ -10,8 +10,8 @@ If the file `components.html` defines these two components:
         this is comp1
     </%def>
     
-    <%def name="comp2()">
-        this is comp2
+    <%def name="comp2(x)">
+        this is comp2, x is ${x}
     </%def>
     
 You can make another file, for example `index.html`, that pulls those two components into a namespace called `comp`:
@@ -20,7 +20,7 @@ You can make another file, for example `index.html`, that pulls those two compon
     <%namespace name="comp" file="components.html"/>
     
     Heres comp1:  ${comp.comp1()}
-    Heres comp2:  ${comp.comp2()}
+    Heres comp2:  ${comp.comp2(x=5)}
 
 The `comp` variable above is an instance of `mako.runtime.Namespace`, a **proxy object** which delivers method calls to the underlying template callable using the current context.
 
@@ -29,17 +29,50 @@ The `comp` variable above is an instance of `mako.runtime.Namespace`, a **proxy
     <%namespace file="components.html" import="comp1, comp2"/>
     
     Heres comp1:  ${comp1()}
-    Heres comp2:  ${comp2()}
+    Heres comp2:  ${comp2(x=5)}
     
 `import` also supports the "*" operator:
 
     <%namespace file="components.html" import="*"/>
 
     Heres comp1:  ${comp1()}
-    Heres comp2:  ${comp2()}
+    Heres comp2:  ${comp2(x=5)}
 
 The names imported by the `import` attribute take precedence over any names that exist within the current context.
 
+**Note** - in current versions of Mako, usage of "import='*'" is known to decrease performance of the template.  This will be fixed in a future release.
+
+### Ways to Call Namespaces {@name=howtocall}
+
+There are essentially four ways to call a function from a namespace.
+
+The "expression" format, as described previously.  Namespaces are just Python objects with functions on them, and can
+be used in expressions like any other function:
+
+    ${mynamespace.somefunction('some arg1', 'some arg2', arg3='some arg3', arg4='some arg4')}
+    
+Synonymous with the "expression" format is the "custom tag" format, when a "closed" tag is used.  This format, introduced in Mako 0.2.3, allows the usage of a "custom" Mako tag, with the function arguments passed in using named attributes:
+
+    <%mynamespace:somefunction arg1="some arg1" arg2="some arg2" arg3="some arg3" arg4="some arg4"/>
+    
+When using tags, the values of the arguments are taken as literal strings by default.  To embed Python expressions as arguments, use the embedded expression format:
+
+    <%mynamespace:somefunction arg1="${someobject.format()}" arg2="${somedef(5, 12)}"/>
+
+The "custom tag" format is intended mainly for namespace functions which recognize body content, which in Mako is known as a "def with embedded content":
+
+    <%mynamespace:somefunction arg1="some argument" args="x, y">
+        Some record: ${x}, ${y}
+    </%mynamespace:somefunction>
+
+The "classic" way to call defs with embedded content is the `<%call>` tag:
+
+    <%call expr="mynamespace.somefunction(arg1='some argument')" args="x, y">
+        Some record: ${x}, ${y}
+    </%call>
+
+For information on how to construct defs that embed content from the caller, see [defs_defswithcontent](rel:defs_defswithcontent).
+
 ### Namespaces from Regular Python Modules {@name=frommodules}
 
 Namespaces can also import regular Python functions from modules.  These callables need to take at least one argument, `context`:
diff --git a/doc/build/content/syntax.txt b/doc/build/content/syntax.txt
index 3ee2e1f73a80a0eabd5b49eb90e800466cbd5c20..9f997837530598ae1554d99df834b95b60c6bc5d 100644
--- a/doc/build/content/syntax.txt
+++ b/doc/build/content/syntax.txt
@@ -175,9 +175,19 @@ Inherit allows templates to arrange themselves in **inheritance chains**.  This
 
 When using the %inherit tag, control is passed to the topmost inherited template first, which then decides how to handle calling areas of content from its inheriting templates.  Mako offers a lot of flexbility in this area, including dynamic inheritance, content wrapping, and polymorphic method calls.  Check it out in [inheritance](rel:inheritance).
 
+#### <%namespacename:defname>
+
+As of Mako 0.2.3, any user-defined "tag" can be created against a namespace by using a tag with a name of the form *namespacename*:*defname*.  The closed and open formats of such a tag are equivalent to an inline expression and the `<%call>` tag, respectively.
+
+    <%mynamespace:somedef param="some value">
+        this is the body
+    </%mynamespace:somedef>
+
+To create custom tags which accept a body, see [defs_defswithcontent](rel:defs_defswithcontent).
+
 #### <%call>
 
-The call tag is used to call `<%defs>` with additional embedded content.  This tag is described in [defs_defswithcontent](rel:defs_defswithcontent).
+The call tag is the "classic" form of a user-defined tag, and is roughly equiavlent to the `<%namespacename:defname>` syntax described above.  This tag is also described in [defs_defswithcontent](rel:defs_defswithcontent).
 
 #### <%doc>
 
diff --git a/lib/mako/codegen.py b/lib/mako/codegen.py
index 0f1be999c3511ffbc7ce903144054337870f2cae..f02b8de1520a193e0c10b968018d615ee8825df9 100644
--- a/lib/mako/codegen.py
+++ b/lib/mako/codegen.py
@@ -528,6 +528,12 @@ class _GenerateRenderMethod(object):
     def visitDefTag(self, node):
         pass
 
+    def visitCallNamespaceTag(self, node):
+        # TODO: we can put namespace-specific checks here, such
+        # as ensure the given namespace will be imported,
+        # pre-import the namespace, etc.
+        self.visitCallTag(node)
+        
     def visitCallTag(self, node):
         self.printer.writeline("def ccall(caller):")
         export = ['body']
@@ -583,7 +589,7 @@ class _GenerateRenderMethod(object):
             "try:")
         self.write_source_comment(node)
         self.printer.writelines(
-                "__M_writer(%s)" % self.create_filter_callable([], node.attributes['expr'], True),
+                "__M_writer(%s)" % self.create_filter_callable([], node.expression, True),
             "finally:",
                 "context.caller_stack.nextcaller = None",
             None
@@ -680,7 +686,10 @@ class _Identifiers(object):
         for ident in node.declared_identifiers():
             self.argument_declared.add(ident)
         self.check_declared(node)
-                    
+    
+    def visitCallNamespaceTag(self, node):
+        self.visitCallTag(node)
+        
     def visitCallTag(self, node):
         if node is self.node:
             for ident in node.undeclared_identifiers():
diff --git a/lib/mako/ext/pygmentplugin.py b/lib/mako/ext/pygmentplugin.py
index 4587573e99b39aa836e9f9b2df3c952f62a3ed68..09ffe127767baef9167cf6fe281df15bb6bce5b3 100644
--- a/lib/mako/ext/pygmentplugin.py
+++ b/lib/mako/ext/pygmentplugin.py
@@ -27,9 +27,9 @@ class MakoLexer(RegexLexer):
              (r'(\s*)(##[^\n]*)(\n|\Z)',
               bygroups(Text, Comment.Preproc, Other)),
               (r'''(?s)<%doc>.*?</%doc>''', Comment.Preproc),
-            (r'(<%)(def|call|namespace|text)', bygroups(Comment.Preproc, Name.Builtin), 'tag'),
-            (r'(</%)(def|call|namespace|text)(>)', bygroups(Comment.Preproc, Name.Builtin, Comment.Preproc)),
-            (r'<%(?=(include|inherit|namespace|page))', Comment.Preproc, 'ondeftags'),
+            (r'(<%)([\w\.\:]+)', bygroups(Comment.Preproc, Name.Builtin), 'tag'),
+            (r'(</%)([\w\.\:]+)(>)', bygroups(Comment.Preproc, Name.Builtin, Comment.Preproc)),
+            (r'<%(?=([\w\.\:]+))', Comment.Preproc, 'ondeftags'),
             (r'(<%(?:!?))(.*?)(%>)(?s)', bygroups(Comment.Preproc, using(PythonLexer), Comment.Preproc)),
             (r'(\$\{)(.*?)(\})',
              bygroups(Comment.Preproc, using(PythonLexer), Comment.Preproc)),
diff --git a/lib/mako/lexer.py b/lib/mako/lexer.py
index 785461c9f847582da74477a4ce68dc18a4f8f1fe..c5c39dff8882151b8b4418a02e2dcb6927dbb9d7 100644
--- a/lib/mako/lexer.py
+++ b/lib/mako/lexer.py
@@ -183,7 +183,7 @@ class Lexer(object):
         match = self.match(r'''
             \<%     # opening tag
             
-            (\w+)   # keyword
+            ([\w\.\:]+)   # keyword
             
             ((?:\s+\w+|=|".*?"|'.*?')*)  # attrname, = sign, string expression
             
diff --git a/lib/mako/parsetree.py b/lib/mako/parsetree.py
index 426dc95c6480150cf0253ae94a4bce9922a98d0a..84d454814263d9b0e3328d271ab854655a31ff07 100644
--- a/lib/mako/parsetree.py
+++ b/lib/mako/parsetree.py
@@ -16,11 +16,14 @@ class Node(object):
         self.lineno = lineno
         self.pos = pos
         self.filename = filename
-        
-    exception_kwargs = property(lambda self:{'source':self.source, 'lineno':self.lineno, 'pos':self.pos, 'filename':self.filename})
+    
+    def exception_kwargs(self):
+        return {'source':self.source, 'lineno':self.lineno, 'pos':self.pos, 'filename':self.filename}
+    exception_kwargs = property(exception_kwargs)
     
     def get_children(self):
         return []
+        
     def accept_visitor(self, visitor):
         def traverse(node):
             for n in node.get_children():
@@ -30,22 +33,29 @@ class Node(object):
 
 class TemplateNode(Node):
     """a 'container' node that stores the overall collection of nodes."""
+    
     def __init__(self, filename):
         super(TemplateNode, self).__init__('', 0, 0, filename)
         self.nodes = []
         self.page_attributes = {}
+        
     def get_children(self):
         return self.nodes
+        
     def __repr__(self):
-        return "TemplateNode(%s, %s)" % (repr(self.page_attributes), repr(self.nodes))
+        return "TemplateNode(%r, %r)" % (self.page_attributes, self.nodes)
         
 class ControlLine(Node):
     """defines a control line, a line-oriented python line or end tag.
     
-    % if foo:
-        (markup)
-    % endif
+    e.g.::
+
+        % if foo:
+            (markup)
+        % endif
+    
     """
+
     def __init__(self, keyword, isend, text, **kwargs):
         super(ControlLine, self).__init__(**kwargs)
         self.text = text
@@ -57,54 +67,77 @@ class ControlLine(Node):
             self._undeclared_identifiers = []
         else:
             code = ast.PythonFragment(text, **self.exception_kwargs)
-            (self._declared_identifiers, self._undeclared_identifiers) = (code.declared_identifiers, code.undeclared_identifiers)
+            self._declared_identifiers = code.declared_identifiers 
+            self._undeclared_identifiers = code.undeclared_identifiers
+
     def declared_identifiers(self):
         return self._declared_identifiers
+
     def undeclared_identifiers(self):
         return self._undeclared_identifiers
+        
     def is_ternary(self, keyword):
         """return true if the given keyword is a ternary keyword for this ControlLine"""
+        
         return keyword in {
             'if':util.Set(['else', 'elif']),
             'try':util.Set(['except', 'finally']),
             'for':util.Set(['else'])
         }.get(self.keyword, [])
+        
     def __repr__(self):
-        return "ControlLine(%s, %s, %s, %s)" % (repr(self.keyword), repr(self.text), repr(self.isend), repr((self.lineno, self.pos)))
+        return "ControlLine(%r, %r, %r, %r)" % (
+            self.keyword, 
+            self.text, 
+            self.isend, 
+            (self.lineno, self.pos)
+        )
 
 class Text(Node):
     """defines plain text in the template."""
+    
     def __init__(self, content, **kwargs):
         super(Text, self).__init__(**kwargs)
         self.content = content
+        
     def __repr__(self):
-        return "Text(%s, %s)" % (repr(self.content), repr((self.lineno, self.pos)))
+        return "Text(%r, %r)" % (self.content, (self.lineno, self.pos))
         
 class Code(Node):
     """defines a Python code block, either inline or module level.
     
-    inline:
-    <%
-        x = 12
-    %>
+    e.g.::
+
+        inline:
+        <%
+            x = 12
+        %>
     
-    module level:
-    <%!
-        import logger
-    %>
+        module level:
+        <%!
+            import logger
+        %>
     
     """
+
     def __init__(self, text, ismodule, **kwargs):
         super(Code, self).__init__(**kwargs)
         self.text = text
         self.ismodule = ismodule
         self.code = ast.PythonCode(text, **self.exception_kwargs)
+
     def declared_identifiers(self):
         return self.code.declared_identifiers
+
     def undeclared_identifiers(self):
         return self.code.undeclared_identifiers
+
     def __repr__(self):
-        return "Code(%s, %s, %s)" % (repr(self.text), repr(self.ismodule), repr((self.lineno, self.pos)))
+        return "Code(%r, %r, %r)" % (
+            self.text, 
+            self.ismodule, 
+            (self.lineno, self.pos)
+        )
         
 class Comment(Node):
     """defines a comment line.
@@ -112,11 +145,13 @@ class Comment(Node):
     # this is a comment
     
     """
+    
     def __init__(self, text, **kwargs):
         super(Comment, self).__init__(**kwargs)
         self.text = text
+
     def __repr__(self):
-        return "Comment(%s, %s)" % (repr(self.text), repr((self.lineno, self.pos)))
+        return "Comment(%r, %r)" % (self.text, (self.lineno, self.pos))
         
 class Expression(Node):
     """defines an inline expression.
@@ -124,32 +159,57 @@ class Expression(Node):
     ${x+y}
     
     """
+    
     def __init__(self, text, escapes, **kwargs):
         super(Expression, self).__init__(**kwargs)
         self.text = text
         self.escapes = escapes
         self.escapes_code = ast.ArgumentList(escapes, **self.exception_kwargs)
         self.code = ast.PythonCode(text, **self.exception_kwargs)
+
     def declared_identifiers(self):
         return []
+
     def undeclared_identifiers(self):
         # TODO: make the "filter" shortcut list configurable at parse/gen time
-        return self.code.undeclared_identifiers.union(self.escapes_code.undeclared_identifiers.difference(util.Set(filters.DEFAULT_ESCAPES.keys())))
+        return self.code.undeclared_identifiers.union(
+                self.escapes_code.undeclared_identifiers.difference(
+                    util.Set(filters.DEFAULT_ESCAPES.keys())
+                )
+            )
+
     def __repr__(self):
-        return "Expression(%s, %s, %s)" % (repr(self.text), repr(self.escapes_code.args), repr((self.lineno, self.pos)))
+        return "Expression(%r, %r, %r)" % (
+            self.text, 
+            self.escapes_code.args, 
+            (self.lineno, self.pos)
+        )
         
 class _TagMeta(type):
     """metaclass to allow Tag to produce a subclass according to its keyword"""
+    
     _classmap = {}
+    
     def __init__(cls, clsname, bases, dict):
         if cls.__keyword__ is not None:
             cls._classmap[cls.__keyword__] = cls
             super(_TagMeta, cls).__init__(clsname, bases, dict)
+            
     def __call__(cls, keyword, attributes, **kwargs):
+        if ":" in keyword:
+            ns, defname = keyword.split(':')
+            return type.__call__(CallNamespaceTag, ns, defname, attributes, **kwargs)
+
         try:
             cls = _TagMeta._classmap[keyword]
         except KeyError:
-            raise exceptions.CompileException("No such tag: '%s'" % keyword, source=kwargs['source'], lineno=kwargs['lineno'], pos=kwargs['pos'], filename=kwargs['filename'])
+            raise exceptions.CompileException(
+                "No such tag: '%s'" % keyword, 
+                source=kwargs['source'], 
+                lineno=kwargs['lineno'], 
+                pos=kwargs['pos'], 
+                filename=kwargs['filename']
+            )
         return type.__call__(cls, keyword, attributes, **kwargs)
         
 class Tag(Node):
@@ -160,9 +220,12 @@ class Tag(Node):
     <%someothertag>
         stuff
     </%someothertag>
+    
     """
+    
     __metaclass__ = _TagMeta
     __keyword__ = None
+    
     def __init__(self, keyword, attributes, expressions, nonexpressions, required, **kwargs):
         """construct a new Tag instance.
         
@@ -176,20 +239,27 @@ class Tag(Node):
         
         nonexpressions - a util.Set of identifiers that are legal attributes, which cannot contain embedded expressions
         
-        **kwargs - other arguments passed to the Node superclass (lineno, pos)"""
+        **kwargs - other arguments passed to the Node superclass (lineno, pos)
+        
+        """
         super(Tag, self).__init__(**kwargs)
         self.keyword = keyword
         self.attributes = attributes
         self._parse_attributes(expressions, nonexpressions)
         missing = [r for r in required if r not in self.parsed_attributes]
         if len(missing):
-            raise exceptions.CompileException("Missing attribute(s): %s" % ",".join([repr(m) for m in missing]), **self.exception_kwargs)
+            raise exceptions.CompileException(
+                "Missing attribute(s): %s" % ",".join([repr(m) for m in missing]), 
+                **self.exception_kwargs)
         self.parent = None
         self.nodes = []
+        
     def is_root(self):
         return self.parent is None
+        
     def get_children(self):
         return self.nodes
+        
     def _parse_attributes(self, expressions, nonexpressions):
         undeclared_identifiers = util.Set()
         self.parsed_attributes = {}
@@ -208,57 +278,82 @@ class Tag(Node):
                 self.parsed_attributes[key] = " + ".join(expr)
             elif key in nonexpressions:
                 if re.search(r'${.+?}', self.attributes[key]):
-                    raise exceptions.CompileException("Attibute '%s' in tag '%s' does not allow embedded expressions"  %(key, self.keyword), **self.exception_kwargs)
+                    raise exceptions.CompileException(
+                            "Attibute '%s' in tag '%s' does not allow embedded expressions"  % (key, self.keyword), 
+                            **self.exception_kwargs)
                 self.parsed_attributes[key] = repr(self.attributes[key])
             else:
                 raise exceptions.CompileException("Invalid attribute for tag '%s': '%s'" %(self.keyword, key), **self.exception_kwargs)
         self.expression_undeclared_identifiers = undeclared_identifiers
+
     def declared_identifiers(self):
         return []
+
     def undeclared_identifiers(self):
         return self.expression_undeclared_identifiers
+
     def __repr__(self):
-        return "%s(%s, %s, %s, %s)" % (self.__class__.__name__, repr(self.keyword), repr(self.attributes), repr((self.lineno, self.pos)), repr([repr(x) for x in self.nodes]))
+        return "%s(%r, %r, %r, %r)" % (self.__class__.__name__, 
+                                        self.keyword, 
+                                        self.attributes, 
+                                        (self.lineno, self.pos), 
+                                        [repr(x) for x in self.nodes]
+                                    )
         
 class IncludeTag(Tag):
     __keyword__ = 'include'
+
     def __init__(self, keyword, attributes, **kwargs):
         super(IncludeTag, self).__init__(keyword, attributes, ('file', 'import', 'args'), (), ('file',), **kwargs)
         self.page_args = ast.PythonCode("__DUMMY(%s)" % attributes.get('args', ''), **self.exception_kwargs)
+
     def declared_identifiers(self):
         return []
+
     def undeclared_identifiers(self):
         identifiers = self.page_args.undeclared_identifiers.difference(util.Set(["__DUMMY"]))
         return identifiers.union(super(IncludeTag, self).undeclared_identifiers())
     
 class NamespaceTag(Tag):
     __keyword__ = 'namespace'
+
     def __init__(self, keyword, attributes, **kwargs):
         super(NamespaceTag, self).__init__(keyword, attributes, (), ('name','inheritable','file','import','module'), (), **kwargs)
         self.name = attributes.get('name', '__anon_%s' % hex(abs(id(self))))
         if not 'name' in attributes and not 'import' in attributes:
             raise exceptions.CompileException("'name' and/or 'import' attributes are required for <%namespace>", **self.exception_kwargs)
+
     def declared_identifiers(self):
         return []
 
 class TextTag(Tag):
     __keyword__ = 'text'
+
     def __init__(self, keyword, attributes, **kwargs):
         super(TextTag, self).__init__(keyword, attributes, (), ('filter'), (), **kwargs)
         self.filter_args = ast.ArgumentList(attributes.get('filter', ''), **self.exception_kwargs)
         
 class DefTag(Tag):
     __keyword__ = 'def'
+
     def __init__(self, keyword, attributes, **kwargs):
-        super(DefTag, self).__init__(keyword, attributes, ('buffered', 'cached', 'cache_key', 'cache_timeout', 'cache_type', 'cache_dir', 'cache_url'), ('name','filter'), ('name',), **kwargs)
+        super(DefTag, self).__init__(
+                keyword, 
+                attributes, 
+                ('buffered', 'cached', 'cache_key', 'cache_timeout', 'cache_type', 'cache_dir', 'cache_url'), 
+                ('name','filter'), 
+                ('name',), 
+                **kwargs)
         name = attributes['name']
         if re.match(r'^[\w_]+$',name):
             raise exceptions.CompileException("Missing parenthesis in %def", **self.exception_kwargs)
         self.function_decl = ast.FunctionDecl("def " + name + ":pass", **self.exception_kwargs)
         self.name = self.function_decl.funcname
         self.filter_args = ast.ArgumentList(attributes.get('filter', ''), **self.exception_kwargs)
+
     def declared_identifiers(self):
         return self.function_decl.argnames
+
     def undeclared_identifiers(self):
         res = []
         for c in self.function_decl.defaults:
@@ -267,26 +362,59 @@ class DefTag(Tag):
 
 class CallTag(Tag):
     __keyword__ = 'call'
+
     def __init__(self, keyword, attributes, **kwargs):
         super(CallTag, self).__init__(keyword, attributes, ('args'), ('expr',), ('expr',), **kwargs)
-        self.code = ast.PythonCode(attributes['expr'], **self.exception_kwargs)
+        self.expression = attributes['expr']
+        self.code = ast.PythonCode(self.expression, **self.exception_kwargs)
+        self.body_decl = ast.FunctionArgs(attributes.get('args', ''), **self.exception_kwargs)
+
+    def declared_identifiers(self):
+        return self.code.declared_identifiers.union(self.body_decl.argnames)
+
+    def undeclared_identifiers(self):
+        return self.code.undeclared_identifiers
+
+class CallNamespaceTag(Tag):
+
+    def __init__(self, namespace, defname, attributes, **kwargs):
+        super(CallNamespaceTag, self).__init__(
+                    namespace + ":" + defname, 
+                    attributes, 
+                    tuple(attributes.keys()) + ('args', ), 
+                    (), 
+                    (), 
+                    **kwargs)
+        self.expression = "%s.%s(%s)" % (namespace, defname, ",".join(["%s=%s" % (k, v) for k, v in self.parsed_attributes.iteritems() if k != 'args']))
+        self.code = ast.PythonCode(self.expression, **self.exception_kwargs)
         self.body_decl = ast.FunctionArgs(attributes.get('args', ''), **self.exception_kwargs)
+
     def declared_identifiers(self):
         return self.code.declared_identifiers.union(self.body_decl.argnames)
+
     def undeclared_identifiers(self):
         return self.code.undeclared_identifiers
 
 class InheritTag(Tag):
     __keyword__ = 'inherit'
+
     def __init__(self, keyword, attributes, **kwargs):
         super(InheritTag, self).__init__(keyword, attributes, ('file',), (), ('file',), **kwargs)
 
 class PageTag(Tag):
     __keyword__ = 'page'
+
     def __init__(self, keyword, attributes, **kwargs):
-        super(PageTag, self).__init__(keyword, attributes, ('cached', 'cache_key', 'cache_timeout', 'cache_type', 'cache_dir', 'cache_url', 'args', 'expression_filter'), (), (), **kwargs)
+        super(PageTag, self).__init__(
+                keyword, 
+                attributes, 
+                ('cached', 'cache_key', 'cache_timeout', 'cache_type', 'cache_dir', 'cache_url', 'args', 'expression_filter'), 
+                (), 
+                (), 
+                **kwargs)
         self.body_decl = ast.FunctionArgs(attributes.get('args', ''), **self.exception_kwargs)
         self.filter_args = ast.ArgumentList(attributes.get('expression_filter', ''), **self.exception_kwargs)
+
     def declared_identifiers(self):
         return self.body_decl.argnames
         
diff --git a/test/lexer.py b/test/lexer.py
index ef315852843173726e402a49f2ebf171840c7230..ce990f27d645bd2097b1f06d9273c05efa919c7d 100644
--- a/test/lexer.py
+++ b/test/lexer.py
@@ -122,7 +122,25 @@ class LexerTest(unittest.TestCase):
             assert False
         except exceptions.CompileException, e:
             assert str(e) == "Missing parenthesis in %def at line: 2 char: 9"
-                
+    
+    def test_ns_tag_closed(self):
+        template = """
+        
+            <%self:go x="1" y="2" z="${'hi' + ' ' + 'there'}"/>
+        """
+        nodes = Lexer(template).parse()
+        assert repr(nodes) == r"""TemplateNode({}, [Text(u'\n        \n            ', (1, 1)), CallNamespaceTag(u'self:go', {u'y': u'2', u'x': u'1', u'z': u"${'hi' + ' ' + 'there'}"}, (3, 13), []), Text(u'\n        ', (3, 64))])"""
+
+    def test_ns_tag_open(self):
+        template = """
+        
+            <%self:go x="1" y="${process()}">
+                this is the body
+            </%self:go>
+        """
+        nodes = Lexer(template).parse()
+        assert repr(nodes) == r"""TemplateNode({}, [Text(u'\n        \n            ', (1, 1)), CallNamespaceTag(u'self:go', {u'y': u'${process()}', u'x': u'1'}, (3, 13), ["Text(u'\\n                this is the body\\n            ', (3, 46))"]), Text(u'\n        ', (5, 24))])"""
+    
     def test_expr_in_attribute(self):
         """test some slightly trickier expressions.
         
diff --git a/test/namespace.py b/test/namespace.py
index 76052c673a98f5d06232c75df35697c004428e50..7196312ba1c245d6823068780c3847bf853bd6a3 100644
--- a/test/namespace.py
+++ b/test/namespace.py
@@ -370,6 +370,81 @@ class NamespaceTest(unittest.TestCase):
             "base lala",
             "foo lala",
         ]
+    
+    
+    def test_custom_tag_1(self):
+        template = Template("""
+        
+            <%def name="foo(x, y)">
+                foo: ${x} ${y}
+            </%def>
+            
+            <%self:foo x="5" y="${7+8}"/>
+        """)
+        assert result_lines(template.render()) == ['foo: 5 15']
+
+    def test_custom_tag_2(self):
+        collection = lookup.TemplateLookup()
+        collection.put_string("base.html", """
+            <%def name="foo(x, y)">
+                foo: ${x} ${y}
+            </%def>
+            
+            <%def name="bat(g)"><%
+                return "the bat! %s" % g
+            %></%def>
+            
+            <%def name="bar(x)">
+                ${caller.body(z=x)}
+            </%def>
+        """)
+        
+        collection.put_string("index.html", """
+            <%namespace name="myns" file="base.html"/>
+            
+            <%myns:foo x="${'some x'}" y="some y"/>
+            
+            <%myns:bar x="${myns.bat(10)}" args="z">
+                record: ${z}
+            </%myns:bar>
+        
+        """)
+        
+        assert result_lines(collection.get_template("index.html").render()) == [
+            'foo: some x some y', 
+            'record: the bat! 10'
+        ]
+        
+    def test_custom_tag_3(self):
+        collection = lookup.TemplateLookup()
+        collection.put_string("base.html", """
+            <%namespace name="foo" file="ns.html" inheritable="True"/>
+
+            ${next.body()}
+    """)
+        collection.put_string("ns.html", """
+            <%def name="bar()">
+                this is ns.html->bar
+                caller body: ${caller.body()}
+            </%def>
+        """)
+
+        collection.put_string("index.html", """
+            <%inherit file="base.html"/>
+
+            this is index
+            <%self.foo:bar>
+                call body
+            </%self.foo:bar>
+        """)
+        
+        assert result_lines(collection.get_template("index.html").render()) == [
+            "this is index",
+            "this is ns.html->bar",
+            "caller body:",
+            "call body"
+        ]
+        
         
     def test_ccall(self):
         collection = lookup.TemplateLookup()