Index: genshi/core.py
===================================================================
--- genshi/core.py	(revision 828)
+++ genshi/core.py	(working copy)
@@ -419,11 +419,6 @@
     """
     __slots__ = []
 
-    def __new__(cls, text='', *args):
-        if args:
-            text %= tuple(map(escape, args))
-        return unicode.__new__(cls, text)
-
     def __add__(self, other):
         return Markup(unicode(self) + unicode(escape(other)))
 
@@ -431,7 +426,10 @@
         return Markup(unicode(escape(other)) + unicode(self))
 
     def __mod__(self, args):
-        if not isinstance(args, (list, tuple)):
+        if isinstance(args, dict):
+            return Markup(unicode.__mod__(self, dict(zip(args.keys(),
+                map(escape, args.values())))))
+        elif not isinstance(args, (list, tuple)):
             args = [args]
         return Markup(unicode.__mod__(self, tuple(map(escape, args))))
 
Index: genshi/tests/core.py
===================================================================
--- genshi/tests/core.py	(revision 828)
+++ genshi/tests/core.py	(working copy)
@@ -107,6 +107,11 @@
         assert type(markup) is Markup
         self.assertEquals('<b>&amp;</b> boo', markup)
 
+    def test_mod_mapping(self):
+        markup = Markup('<b>%(foo)s</b>') % {'foo': '&'}
+        assert type(markup) is Markup
+        self.assertEquals('<b>&amp;</b>', markup)
+
     def test_mul(self):
         markup = Markup('<b>foo</b>') * 2
         assert type(markup) is Markup
Index: genshi/output.py
===================================================================
--- genshi/output.py	(revision 828)
+++ genshi/output.py	(working copy)
@@ -244,7 +244,7 @@
                 if sysid:
                     buf.append(' "%s"')
                 buf.append('>\n')
-                yield Markup(u''.join(buf), *filter(None, data))
+                yield Markup(u''.join(buf)) % filter(None, data)
                 have_doctype = True
 
             elif kind is START_CDATA:
@@ -343,7 +343,7 @@
                 if sysid:
                     buf.append(' "%s"')
                 buf.append('>\n')
-                yield Markup(u''.join(buf), *filter(None, data))
+                yield Markup(u''.join(buf)) % filter(None, data)
                 have_doctype = True
 
             elif kind is START_CDATA:
@@ -446,7 +446,7 @@
                 if sysid:
                     buf.append(' "%s"')
                 buf.append('>\n')
-                yield Markup(u''.join(buf), *filter(None, data))
+                yield Markup(u''.join(buf)) % filter(None, data)
                 have_doctype = True
 
             elif kind is PI:
Index: genshi/_speedups.c
===================================================================
--- genshi/_speedups.c	(revision 828)
+++ genshi/_speedups.c	(working copy)
@@ -144,46 +144,6 @@
     return ret;
 }
 
-static PyObject *
-Markup_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
-{
-    PyObject *self, *text, *tmp, *args2;
-    int nargs, i;
-
-    nargs = PyTuple_GET_SIZE(args);
-    if (nargs < 2) {
-        return PyUnicode_Type.tp_new(type, args, NULL);
-    }
-
-    text = PyTuple_GET_ITEM(args, 0);
-    args2 = PyTuple_New(nargs - 1);
-    if (args2 == NULL) {
-        return NULL;
-    }
-    for (i = 1; i < nargs; i++) {
-        tmp = escape(PyTuple_GET_ITEM(args, i), 1);
-        if (tmp == NULL) {
-            Py_DECREF(args2);
-            return NULL;
-        }
-        PyTuple_SET_ITEM(args2, i - 1, tmp);
-    }
-    tmp = PyUnicode_Format(text, args2);
-    Py_DECREF(args2);
-    if (tmp == NULL) {
-        return NULL;
-    }
-    args = PyTuple_New(1);
-    if (args == NULL) {
-        Py_DECREF(tmp);
-        return NULL;
-    }
-    PyTuple_SET_ITEM(args, 0, tmp);
-    self = PyUnicode_Type.tp_new(type, args, NULL);
-    Py_DECREF(args);
-    return self;
-}
-
 PyDoc_STRVAR(escape__doc__,
 "Create a Markup instance from a string and escape special characters\n\
 it may contain (<, >, & and \").\n\
@@ -325,9 +285,41 @@
 Markup_mod(PyObject *self, PyObject *args)
 {
     PyObject *tmp, *tmp2, *ret, *args2;
-    int i, nargs;
+    int i, nargs, nkwds = 0;
+    PyObject *kwds = NULL;
 
-    if (PyTuple_Check(args)) {
+    if (PyDict_Check(args)) {
+        kwds = args;
+    }
+    if ( kwds ) {
+        nkwds = PyDict_Size(kwds);
+    }
+    if ( nkwds ) {
+        PyObject *kwcopy, *key, *value;
+        Py_ssize_t pos = 0;
+
+        kwcopy = PyDict_Copy( kwds );
+        if (kwcopy == NULL) {
+            return NULL;
+        }
+        while (PyDict_Next(kwcopy, &pos, &key, &value)) {
+            tmp = escape(value, 1);
+            if (tmp == NULL) {
+                Py_DECREF(kwcopy);
+                return NULL;
+            }
+            if (PyDict_SetItem(kwcopy, key, tmp) < 0) {
+                Py_DECREF(tmp);
+                Py_DECREF(kwcopy);
+                return NULL;
+            }
+        }
+        tmp = PyUnicode_Format(self, kwcopy);
+        Py_DECREF(kwcopy);
+        if (tmp == NULL) {
+            return NULL;
+        }
+    } else if (PyTuple_Check(args)) {
         nargs = PyTuple_GET_SIZE(args);
         args2 = PyTuple_New(nargs);
         if (args2 == NULL) {
@@ -589,7 +581,7 @@
     
     0,          /*tp_init*/
     0,          /*tp_alloc  will be set to PyType_GenericAlloc in module init*/
-    Markup_new, /*tp_new*/
+    0,          /*tp_new*/
     0,          /*tp_free  Low-level free-memory routine */
     0,          /*tp_is_gc For PyObject_IS_GC */
     0,          /*tp_bases*/
