    """Servlet factory for Kid templates.

    This allows you to run kid template files directly from within Webware.
    Compiled templates are cached either along with the templates or in the
    Cache/KidKit subdirectory of the WebKit working directory.

    Note that the Kid package itself is not part of Webware; you need to install
    it separately. You also need to install the ElementTree package to use Kid.

    CREDITS:

    * Kid has been developed by Ryan Tomayko (http://lesscode.org).
    * ElementTree has been developed by Fredrik Lundh (http://effbot.org).
    * KidKit was contributed by Winston Wolff (winstonw<at>stratolab.com).
      Based on the Cheetah servlet factory. No caching, fixed servlet hook.
    * Improved version contributed by Christoph Zwerschke (cito<at>online.de).
      Based on the PSP servlet factory. Supports caching and servlet hooks.

    """

    import os, sys, time
    from glob import glob
    from WebKit.ImportSpy import watchFile
    from WebKit.ServletFactory import ServletFactory
    from WebKit.Servlet import Servlet
    from WebKit.Page import Page
    defaultHook = Page.respond # the default hook for Kid servlets
    defaultOutput = 'html' # the default Kid output method

    from kid import load_template, output_methods
    from kid.compiler import KidFile

    from KidKit.Examples.KidExamplePage import KidExamplePage

    def kidClass(module):
        """Return a WebKit servlet class for a Kid template module."""

        try:
            hook = module.hook
        except AttributeError:
            hook = defaultHook
        assert type(hook) == type(defaultHook)
        ServletClass = hook.im_class
        assert issubclass(ServletClass, Servlet)
        writeMethod = hook.__name__
        try:
            output = module.output
        except:
            output = defaultOutput
        assert output in output_methods

        class KidServlet(ServletClass):
            """The base class for a Kid servlet."""

            _module = module
            _template = module.Template()
            _output = output

            def writeTemplate(self, *args, **kwargs):
                self._template.servlet = self
                response = self._response
                fragment = response.size() > 0
                self._template.write(self._response,
                    fragment=fragment, output=output)

        setattr(KidServlet, writeMethod, KidServlet.writeTemplate)
        return KidServlet

    class KidServletFactory(ServletFactory):
        """Servlet factory for Kid templates."""

        def __init__(self,application):
            ServletFactory.__init__(self, application)
            setting = application.setting
            defaultOutput = setting('KidOutputMethod')
            self._cacheTemplates = setting('CacheKidTemplates', True)
            self._useCache = setting('UseKidKitCache', False)
            if self._useCache:
                self._cacheSource = setting('CacheKidSource', True)
                self._clearCache = setting('ClearKidCacheOnStart', False)
                self._cacheDir = application.serverSidePath('Cache/KidKit')
                if self._clearCache:
                    self.clearFileCache()
            else:
                self._cacheSource = self._clearCache = False
                self._cacheDir = None
            t = ['_'] * 256
            from string import digits, letters
            for c in digits + letters:
                t[ord(c)] = c
            self._classNameTrans = ''.join(t)

        def uniqueness(self):
            return 'file'

        def extensions(self):
            return ['.kid']

        def flushCache(self):
            """Clean out the cache of classes in memory and on disk."""
            ServletFactory.flushCache(self)
            if self._clearCache:
                self.clearFileCache()

        def clearFileCache(self):
            """Clear class files stored on disk."""
            files = glob(os.path.join(self._cacheDir, '*.*'))
            map(os.remove, files)

        def computeClassName(self, pagename):
            """Generates a (hopefully) unique class/file name for each Kid file.

            Argument: pagename: the path to the Kid template file
            Returns: a unique name for the class generated fom this Kid file

            """
            # Compute class name by taking the path and substituting
            # underscores for all non-alphanumeric characters:
            return os.path.splitdrive(pagename)[1].translate(self._classNameTrans)

        def loadClass(self, transaction, path):
            """Load servlet class for the given Kid template."""
            classname = self.computeClassName(path)
            if self._cacheTemplates and self._useCache:
                # Cache the compiled templates separately:
                mtime = os.path.getmtime(path)
                classfile = os.path.join(self._cacheDir, classname + ".py")
                if not self._cacheSource:
                    classfile += __debug__ and 'c' or 'o'
                if not os.path.exists(classfile) \
                        or os.path.getmtime(classfile) != mtime:
                    kidFile = KidFile(path)
                    if self._cacheSource:
                        kidFile.dump_source(classfile)
                    else:
                        kidFile.dump_code(classfile)
                    # Set the modification time of the compiled file
                    # to be the same as the source file;
                    # that's how we'll know if it needs to be recompiled:
                    os.utime(classfile, (os.path.getatime(classfile), mtime))
                module = self.importAsPackage(transaction, classfile)
            else:
                # Let Kid care about the caching:
                module = load_template(path, cache=self._cacheTemplates)
            # Setting __orig_file__ here is already too late,
            module.__orig_file__ = path
            # so we need to tell ImportSpy explicitely about the file:
            watchFile(path)
            theClass = kidClass(module)
            theClass._orig_file = path
            theClass.__name__ = self.computeClassName(path)
            return theClass