# Copyright (c) 2004 Divmod.
# See LICENSE for details.

"""Page, Fragment and other standard renderers.

This module contains classes and function responsible for rendering
dynamic content and a few useful mixin classes for inheriting common
functionality.

Mostly, you'll use the renderers:

 - B{Page} - Nevow's main resource type for rendering web pages and
   locating child resource.
 - B{Fragment} - useful for rendering more complex parts of a document
   that require a set of data_* and render_* methods.
 - B{sequence} - render each item in a sequence.
 - B{mapping} - publish a dictionary by filling slots
"""

from time import time as now
from io import StringIO
import random
import warnings

from zope.interface import implementer, providedBy

import twisted.python.components as tpc
from twisted.python.reflect import qual, accumulateClassList

from nevow.context import WovenContext, NodeNotFound, PageContext
from nevow import inevow, tags, flat, util, url
from nevow.util import log

import formless
from formless import iformless, annotate


def _getPreprocessors(inst):
    """
    Accumulate elements from the sequences bound at the C{preprocessors}
    attribute on all classes in the inheritance hierarchy of the class of
    C{inst}.  A C{preprocessors} attribute on the given instance overrides
    all preprocessors from the class inheritance hierarchy.
    """
    if 'preprocessors' in vars(inst):
        return inst.preprocessors
    preprocessors = []
    accumulateClassList(
        inst.__class__,
        'preprocessors',
        preprocessors)
    return preprocessors


@implementer(inevow.IRendererFactory)
class RenderFactory(object):


    def renderer(self, context, name):
        """Return a renderer with the given name.
        """

        # The named renderer can be parameterised, i.e. 'renderIt one,two,three'
        args = []
        if name.find(' ') != -1:
            name, args = name.split(None, 1)
            args = [arg.strip() for arg in args.split(',')]

        callable = getattr(self, 'render_%s' % name, None)
        if callable is None:
            warnings.warn(
                "Renderer %r missing on %s will result in an exception." % (
                    name, qual(type(self))),
                category=DeprecationWarning,
                stacklevel=1)
            callable = lambda *a, **kw: context.tag[
                "The renderer named '%s' was not found in %r." % (name, self)]

        if args:
            return callable(*args)

        return callable

    render_sequence = lambda self, context, data: sequence(context, data)
    render_mapping = lambda self, context, data: mapping(context, data)
    render_string = lambda self, context, data: string(context, data)
    render_xml = lambda self, context, data: context.tag.clear()[tags.xml(data)]
    render_data = lambda self, context, data_: data(context, data_)


@implementer(inevow.IMacroFactory)
class MacroFactory(object):


    def macro(self, ctx, name):
        """Return a macro with the given name.
        """
        # The named macro can be parameterized, i.e. 'macroFoo foo,bar,baz'
        args = []
        if name.find(' ') != -1:
            name, args = name.split(None, 1)
            args = [arg.strip() for arg in args.split(',')]

        callable = getattr(self, 'macro_%s' % name, None)
        if callable is None:
            callable = lambda ctx, *args: ctx.tag[
                "The macro named '%s' was not found in %r." % (name, self)]

        if args:
            ## Macros are expanded in TagSerializer by calling them with a single arg, the context
            return lambda ctx: callable(ctx, *args)

        return callable


class DataNotFoundError(Exception):
    """Raised when a data directive could not be resolved on the page or its
    original attribute by the DataFactory.
    """

@implementer(inevow.IContainer)
class DataFactory(object):

    def child(self, context, n):
        args = []
        if n.find(' ') != -1:
            name, args = n.split(None, 1)
            args = [arg.strip() for arg in args.split(',')]
        else:
            name = n

        callable = getattr(self, 'data_%s' % name, None)
        ## If this page doesn't have an appropriate data_* method...
        if callable is None:
            ## See if our self.original has an IContainer...
            container = inevow.IContainer(self.original, None)
            if container is None:
                raise DataNotFoundError("The data named %r was not found in %r." % (name, self))
            else:
                ## And delegate to it if so.
                return container.child(context, n)

        if args:
            return callable(*args)

        return callable


class FreeformChildMixin:
    """Mixin that handles locateChild for freeform segments."""
    def locateChild(self, ctx, segments):
        request = inevow.IRequest(ctx)
        ## The method or property name we are going to validate against/affect
        bindingName = None

        name = segments[0]
        if name.startswith('freeform_post!'):
            configurableName, bindingName = name.split('!')[1:3]
        elif name.startswith('freeform-action-post!'):
            configurableName, request.args['freeform-actee'] = name.split('!')[1:3]
            bindingName = request.args['freeform-action'][0]
        if bindingName:
            ctx.remember(self, inevow.IResource)
            ctx.remember(request, inevow.IRequest)
            cf = iformless.IConfigurableFactory(self)
            def checkC(c):
                if c is not None:
                    return self.webFormPost(request, self, c, ctx, bindingName, request.args)
            return util.maybeDeferred(cf.locateConfigurable, ctx, configurableName).addCallback(checkC)
        return NotFound

    def child_freeform_hand(self, ctx):
        carryoverHand = inevow.IHand(ctx, None)
        if carryoverHand is not None:
            inevow.ISession(ctx).setComponent(inevow.IHand, carryoverHand)
            return carryoverHand
        return inevow.IHand(inevow.ISession(ctx), None)


@implementer(iformless.IConfigurable)
class ConfigurableMixin(object):
    """
    A sane L{IConfigurable} implementation for L{Fragment} and L{Page}. 

    Methods starting with C{bind_} automatically expose corresponding method
    names.  C{bind_*} should return an L{IBinding} (L{PropertyBinding} or
    L{MethodBinding}), or, as a shortcut for L{MethodBinding}, a C{list} of
    two-C{tuples} like this::

        def bind_foo(self, ctx):
            return [('argName', String()), ('anotherArg', Integer())]

        def foo(self, argName, anotherArg):
            assert isinstance(argName, str)
            assert isinstance(anotherArg, int)
    """


    def getBindingNames(self, ctx):
        """Expose bind_* methods and attributes on this class.
        """
        for name in dir(self):
            if name.startswith('bind_'):
                yield name[len('bind_'):]

    def getBinding(self, ctx, name):
        """Massage bind_* methods and attributes into an
        IBinding. The bind_* method or attribute can either
        already implement IBinding or be a list of twoples
        which will be massaged into a MethodBinding as
        described in the ConfigurableMixin class docstring.
        """
        def _get_binding(binding):
            if callable(binding):
                binding = util.maybeDeferred(binding, ctx)
            return binding

        def _convert_list(binding):
            if isinstance(binding, list):
                binding = annotate.MethodBinding(
                    name, annotate.Method(arguments=[
                    annotate.Argument(n, v, v.id)
                    for (n, v) in binding]))
            return binding

        binding = util.maybeDeferred(getattr, self, 'bind_%s' % name)
        return binding.addCallback(_get_binding).addCallback(_convert_list)

    def getDefault(self, forBinding):
        """Get a default value for a given binding. If the
        binding is a Property, get the current value of
        that property off self. If not, simply return
        forBinding.default.
        """
        ## If it is a Property, get the value off self
        if not isinstance(forBinding, annotate.Argument):
            if hasattr(self, forBinding.name):
                return getattr(self, forBinding.name)
        return forBinding.default

    def postForm(self, ctx, bindingName, args):
        """Accept a form post to the given bindingName.
        The post arguments are given in args.

        This will invoke the IInputProcessor for the
        binding with the given name. If it succeeds, the
        property will be modified or the method will have
        been called. If it fails, a ValidateError exception
        will be raised.
        """
        def _callback(binding):
            ctx.remember(binding, iformless.IBinding)
            ctx.remember(self, iformless.IConfigurable)
            rv = iformless.IInputProcessor(binding).process(ctx, self, args)
            ctx.remember(rv, inevow.IHand)
            ctx.remember('%r success.' % bindingName, inevow.IStatusMessage)
            return rv
        return util.maybeDeferred(self.getBinding, ctx, 
                                  bindingName).addCallback(_callback)

@implementer(iformless.IConfigurableFactory)
class ConfigurableFactory:
    """Locates configurables by looking for methods that start with
    configurable_ and end with the name of the configurable. The method
    should take a single arg (other than self) - the current context.
    """


    def locateConfigurable(self, context, name):
        """formless.webform.renderForms calls locateConfigurable on the IConfigurableFactory
        instance it retrieves from the context. It passes the "name" that was passed to it,
        so if renderForms() was placed in the DOM, locateConfigurable will be called with
        name = ''; if renderForms('foo') was placed in the DOM, locateConfigurable will
        be called with name = 'foo'.

        This default implementation of locateConfigurable looks for a configurable_* method
        corresponding to the name which was passed.
        """
        return util.maybeDeferred(getattr(self, 'configurable_%s'%name),
                                  context).addCallback(iformless.IConfigurable)

    def configurable_(self, context):
        """Configurable factory for use when self is a configurable;
        aka it implements IConfigurable or one or more TypedInterface
        subclasses. Usage:

        >>> class IFoo(TypedInterface):
        ...     def bar(): pass
        ...     bar = autocallable(bar)
        ...
        >>> @implementer(IFoo)
        ... class Foo(Page):
        ...
        ...     def bar():
        ...         print "bar called through the web!"
        ...
        ...     def render_forms(self, ctx, data):
        ...         return renderForms() # or renderForms('')
        ...
        ...     docFactory = stan(render_forms).
        """
        if [x for x in providedBy(self) if issubclass(x, annotate.TypedInterface)]:
            warnings.warn("[0.5] Subclassing TypedInterface to declare annotations is deprecated. Please provide bind_* methods on your Page or Fragment subclass instead.", DeprecationWarning)
            from formless import configurable
            return configurable.TypedInterfaceConfigurable(self)
        return self

    def configurable_original(self, ctx):
        """Configurable factory for use when self.original is a configurable;
        aka it implements IConfigurable or one or more TypedInterface
        subclasses. Usage:


        >>> class Foo(Page):
        ...     def __init__(self):
        ...         self.original = SomeConfigurable()
        ...     def render_forms(self, ctx, data):
        ...         return renderForms('original')
        ...     docFactory = stan(render_forms)
        """
        return self.original

_CARRYOVER = {}


def defaultsFactory(ctx):
    co = _CARRYOVER.get(
        ctx.tag.args.get('_nevow_carryover_', [None])[0], None)
    from formless import webform
    defaults = webform.FormDefaults()
    if co is not None:
        e = iformless.IFormErrors(co, {})
        for k, v in list(e.items()):
            defaults.getAllDefaults(k).update(v.partialForm)
    return defaults


def errorsFactory(ctx):
    co = _CARRYOVER.get(
        ctx.tag.args.get('_nevow_carryover_', [None])[0], None)
    from formless import webform
    errs = webform.FormErrors()
    if co is not None:
        e = iformless.IFormErrors(co, {})
        for k, v in list(e.items()):
            errs.updateErrors(k, v.errors)
            errs.setError(k, v.formErrorMessage)
    return errs


def handFactory(ctx):
    co = _CARRYOVER.get(
        ctx.tag.args.get('_nevow_carryover_', [None])[0], None)
    return inevow.IHand(co, None)


def statusFactory(ctx):
    co = _CARRYOVER.get(
        ctx.tag.args.get('_nevow_carryover_', [None])[0], None)
    return inevow.IStatusMessage(co, None)


def originalFactory(ctx):
    return ctx.tag

@implementer(inevow.IRenderer, inevow.IGettable)
class Fragment(DataFactory, RenderFactory, MacroFactory, ConfigurableMixin):
    """
    This class is deprecated because it relies on context objects
    U{which are being removed from Nevow<http://divmod.org/trac/wiki/WitherContext>}.

    @see: L{Element}
    """


    docFactory = None
    original = None

    def __init__(self, original=None, docFactory=None):
        if original is not None:
            self.original = original
        self.toremember = []
        if docFactory is not None:
            self.docFactory = docFactory

    def get(self, context):
        return self.original

    def rend(self, context, data):
        # Create a new context so the current context is not polluted with
        # remembrances.
        context = WovenContext(parent=context)

        # Remember me as lots of things
        self.rememberStuff(context)

        preprocessors = _getPreprocessors(self)

        # This tidbit is to enable us to include Page objects inside
        # stan expressions and render_* methods and the like. But
        # because of the way objects can get intertwined, we shouldn't
        # leave the pattern changed.
        old = self.docFactory.pattern
        self.docFactory.pattern = 'content'
        self.docFactory.precompiledDoc = None
        try:
            try:
                doc = self.docFactory.load(context, preprocessors)
            finally:
                self.docFactory.pattern = old
                self.docFactory.precompiledDoc = None
        except TypeError as e:
            # Avert your eyes now! I don't want to catch anything but IQ
            # adaption exceptions here but all I get is TypeError. This whole
            # section of code is a complete hack anyway so one more won't
            # matter until it's all removed. ;-).
            if 'nevow.inevow.IQ' not in str(e):
                raise
            doc = self.docFactory.load(context, preprocessors)
        except NodeNotFound:
            doc = self.docFactory.load(context, preprocessors)
        else:
            if old == 'content':
                warnings.warn(
                    """[v0.5] Using a Page with a 'content' pattern is
                               deprecated.""",
                    DeprecationWarning,
                    stacklevel=2)

        context.tag = tags.invisible[doc]
        return context

    def remember(self, obj, inter=None):
        """Remember an object for an interface on new PageContexts which are
        constructed around this Page. Whenever this Page is involved in object
        traversal in the future, all objects will be visible to .locate() calls
        at the level of a PageContext wrapped around this Page and all contexts
        below it.

        This does not affect existing Context instances.
        """
        self.toremember.append((obj, inter))

    def rememberStuff(self, ctx):
        ctx.remember(self, inevow.IRenderer)
        ctx.remember(self, inevow.IRendererFactory)
        ctx.remember(self, inevow.IMacroFactory)
        ctx.remember(self, inevow.IData)


class ChildLookupMixin(FreeformChildMixin):
    ##
    # IResource methods
    ##

    children = None
    def locateChild(self, ctx, segments):
        """Locate a child page of this one. ctx is a
        nevow.context.PageContext representing the parent Page, and segments
        is a tuple of each element in the URI. An tuple (page, segments) should be
        returned, where page is an instance of nevow.rend.Page and segments a tuple
        representing the remaining segments of the URI. If the child is not found, return
        NotFound instead.

        locateChild is designed to be easily overridden to perform fancy lookup tricks.
        However, the default locateChild is useful, and looks for children in three places,
        in this order:

         - in a dictionary, self.children
         - a member of self named child_<childname>. This can be either an
           attribute or a method. If an attribute, it should be an object which
           can be adapted to IResource. If a method, it should take the context
           and return an object which can be adapted to IResource.
         - by calling self.childFactory(ctx, name). Name is a single string instead
           of a tuple of strings. This should return an object that can be adapted
           to IResource.
        """

        if self.children is not None:
            r = self.children.get(segments[0], None)
            if r is not None:
                return r, segments[1:]

        w = getattr(self, 'child_%s'%segments[0], None)
        if w is not None:
            if inevow.IResource(w, None) is not None:
                return w, segments[1:]
            r = w(ctx)
            if r is not None:
                return r, segments[1:]

        r = self.childFactory(ctx, segments[0])
        if r is not None:
            return r, segments[1:]

        return FreeformChildMixin.locateChild(self, ctx, segments)

    def childFactory(self, ctx, name):
        """Used by locateChild to return children which are generated
        dynamically. Note that higher level interfaces use only locateChild,
        and only nevow.rend.Page.locateChild uses this.

        segment is a string representing one element of the URI. Request is a
        nevow.appserver.NevowRequest.

        The default implementation of this always returns None; it is intended
        to be overridden."""
        return None

    def putChild(self, name, child):
        if self.children is None:
            self.children = {}
        self.children[name] = child


@implementer(inevow.IResource)
class Page(Fragment, ConfigurableFactory, ChildLookupMixin):
    """A page is the main Nevow resource and renders a document loaded
    via the document factory (docFactory).
    """


    buffered = False

    beforeRender = None
    afterRender = None
    addSlash = None

    flattenFactory = lambda self, *args: flat.flattenFactory(*args)

    def renderHTTP(self, ctx):
        if self.beforeRender is not None:
            return util.maybeDeferred(self.beforeRender,ctx).addCallback(
                    lambda result,ctx: self._renderHTTP(ctx),ctx)
        return self._renderHTTP(ctx)

    def _renderHTTP(self, ctx):
        request = inevow.IRequest(ctx)
        ## XXX request is really ctx now, change the name here
        if self.addSlash and inevow.ICurrentSegments(ctx)[-1] != '':
            request.redirect(str(request.URLPath().child('')))
            return ''

        log.msg(http_render=None, uri=request.uri)

        self.rememberStuff(ctx)

        def finishRequest():
            carryover = request.args.get('_nevow_carryover_', [None])[0]
            if carryover is not None and carryover in _CARRYOVER:
                del _CARRYOVER[carryover]
            if self.afterRender is not None:
                return util.maybeDeferred(self.afterRender,ctx)

        if self.buffered:
            io = StringIO()
            writer = io.write
            def finisher(result):
                request.write(io.getvalue())
                return util.maybeDeferred(finishRequest).addCallback(lambda r: result)
        else:
            writer = request.write
            def finisher(result):
                return util.maybeDeferred(finishRequest).addCallback(lambda r: result)

        preprocessors = _getPreprocessors(self)
        doc = self.docFactory.load(ctx, preprocessors)
        ctx =  WovenContext(ctx, tags.invisible[doc])

        return self.flattenFactory(doc, ctx, writer, finisher)

    def rememberStuff(self, ctx):
        Fragment.rememberStuff(self, ctx)
        ctx.remember(self, inevow.IResource)

    def renderString(self, ctx=None):
        """Render this page outside of the context of a web request, returning
        a Deferred which will result in a string.

        If twisted is not installed, this method will return a string result immediately,
        and this method is equivalent to renderSynchronously.
        """
        io = StringIO()
        writer = io.write

        def finisher(result):
            return io.getvalue()

        ctx = PageContext(parent=ctx, tag=self)
        self.rememberStuff(ctx)
        doc = self.docFactory.load(ctx)
        ctx =  WovenContext(ctx, tags.invisible[doc])

        return self.flattenFactory(doc, ctx, writer, finisher)

    def renderSynchronously(self, ctx=None):
        """Render this page synchronously, returning a string result immediately.
        Raise an exception if a Deferred is required to complete the rendering
        process.
        """
        io = StringIO()

        ctx = PageContext(parent=ctx, tag=self)
        self.rememberStuff(ctx)
        doc = self.docFactory.load(ctx)
        ctx =  WovenContext(ctx, tags.invisible[doc])

        def raiseAlways(item):
            raise NotImplementedError("renderSynchronously can not support"
                " rendering: %s" % (item, ))

        list(flat.iterflatten(doc, ctx, io.write, raiseAlways))

        return io.getvalue()

    def child_(self, ctx):
        """When addSlash is True, a page rendered at a url with no
        trailing slash and a page rendered at a url with a trailing
        slash will be identical. addSlash is useful for the root
        resource of a site or directory-like resources.
        """
        # Only allow an empty child, by default, if it's on the end
        # and we're a directoryish resource (addSlash = True)
        if self.addSlash and len(inevow.IRemainingSegments(ctx)) == 1:
            return self
        return None

    def webFormPost(self, request, res, configurable, ctx, bindingName, args):
        """Accept a web form post, either redisplaying the original form (with
        errors) if validation fails, or redirecting to the appropriate location after
        the post succeeds. This hook exists specifically for formless.

        New in 0.5, _nevow_carryover_ is only used if an autocallable method
        returns a result that needs to be carried over.

        New in 0.5, autocallables may return a nevow.url.URL or URLOverlay
        instance rather than setting IRedirectAfterPost on the request.

        New in 0.5, autocallables may return a Page instance to have that Page
        instance rendered at the post target URL with no redirects at all. Useful
        for multi-step wizards.
        """
        def redirectAfterPost(aspects):
            hand = aspects.get(inevow.IHand)
            refpath = None
            if hand is not None:
                if isinstance(hand, Page):
                    refpath = url.here
                    if 'freeform_hand' not in inevow.IRequest(ctx).prepath:
                        refpath = refpath.child('freeform_hand')
                if isinstance(hand, (url.URL, url.URLOverlay)):
                    refpath, hand = hand, None

            if refpath is None:
                redirectAfterPost = request.getComponent(iformless.IRedirectAfterPost, None)
                if redirectAfterPost is None:
                    ref = request.getHeader('referer')
                    if ref:
                        refpath = url.URL.fromString(ref)
                    else:
                        refpath = url.here
                else:
                    warnings.warn("[0.5] IRedirectAfterPost is deprecated. Return a URL instance from your autocallable instead.", DeprecationWarning, 2)
                    ## Use the redirectAfterPost url
                    ref = str(redirectAfterPost)
                    refpath = url.URL.fromString(ref)

            if hand is not None or aspects.get(iformless.IFormErrors) is not None:
                magicCookie = '%s%s%s' % (now(),request.getClientIP(),random.random())
                refpath = refpath.replace('_nevow_carryover_', magicCookie)
                _CARRYOVER[magicCookie] = C = tpc.Componentized()
                for k, v in aspects.items():
                    C.setComponent(k, v)

            destination = flat.flatten(refpath, ctx)
            request.redirect(destination)
            from nevow import static
            return static.Data('You posted a form to %s' % bindingName, 'text/plain'), ()

        return util.maybeDeferred(
            configurable.postForm, ctx, bindingName, args
        ).addCallback(
            self.onPostSuccess, request, ctx, bindingName, redirectAfterPost
        ).addErrback(
            self.onPostFailure, request, ctx, bindingName, redirectAfterPost
        )

    def onPostSuccess(self, result, request, ctx, bindingName, redirectAfterPost):
        if result is None:
            message = "%s success." % formless.nameToLabel(bindingName)
        else:
            message = result

        return redirectAfterPost({inevow.IHand: result, inevow.IStatusMessage: message})

    def onPostFailure(self, reason, request, ctx, bindingName, redirectAfterPost):
        reason.trap(formless.ValidateError)
        return redirectAfterPost({iformless.IFormErrors: {bindingName: reason.value}})


def sequence(context, data):
    """Renders each item in the sequence using patterns found in the
    children of the element.

    Sequence recognises the following patterns:

     - header: Rendered at the start, before the first item. If multiple
       header patterns are provided they are rendered together in the
       order they were defined.

     - footer: Just like the header only renderer at the end, after the
       last item.

     - item: Rendered once for each item in the sequence. If multiple
       item patterns are provided then the pattern is cycled in the
       order defined.

     - divider: Rendered once between each item in the sequence. Multiple
       divider patterns are cycled.

     - empty: Rendered instead of item and divider patterns when the
       sequence contains no items.

    Example::

     <table nevow:render="sequence" nevow:data="peopleSeq">
       <tr nevow:pattern="header">
         <th>name</th>
         <th>email</th>
       </tr>
       <tr nevow:pattern="item" class="odd">
         <td>name goes here</td>
         <td>email goes here</td>
       </tr>
       <tr nevow:pattern="item" class="even">
         <td>name goes here</td>
         <td>email goes here</td>
       </tr>
       <tr nevow:pattern="empty">
         <td colspan="2"><em>they've all gone!</em></td>
       </tr>
     </table>

    """
    tag = context.tag
    headers = tag.allPatterns('header')
    pattern = tag.patternGenerator('item')
    divider = tag.patternGenerator('divider', default=tags.invisible)
    content = [(pattern(data=element), divider(data=element)) for element in data]
    if not content:
        content = tag.allPatterns('empty')
    else:
        ## No divider after the last thing.
        content[-1] = content[-1][0]
    footers = tag.allPatterns('footer')

    return tag.clear()[ headers, content, footers ]


def mapping(context, data):
    """Fills any slots in the element's children with data from a
    dictionary. The dict keys are used as the slot names, the dict
    values are used as filling.

    Example::

     <tr nevow:render="mapping" nevow:data="personDict">
       <td><nevow:slot name="name"/></td>
       <td><nevow:slot name="email"/></td>
     </tr>
    """
    for k, v in list(data.items()):
        context.fillSlots(k, v)
    return context.tag


def string(context, data):
    return context.tag.clear()[str(data)]


def data(context, data):
    """Replace the tag's content with the current data.
    """
    return context.tag.clear()[data]


@implementer(inevow.IResource)
class FourOhFour:
    """A simple 404 (not found) page.
    """

    notFound = "<html><head><title>Page Not Found</title></head><body>Sorry, but I couldn't find the object you requested.</body></html>"
    original = None

    def locateChild(self, ctx, segments):
        return NotFound

    def renderHTTP(self, ctx):
        inevow.IRequest(ctx).setResponseCode(404)
        # Look for an application-remembered handler
        try:
            notFoundHandler = ctx.locate(inevow.ICanHandleNotFound)
        except KeyError as e:
            return self.notFound
        # Call the application-remembered handler but if there are any errors
        # then log it and fallback to the standard message.
        try:
            return notFoundHandler.renderHTTP_notFound(PageContext(parent=ctx, tag=notFoundHandler))
        except:
            log.err()
            return self.notFound

    def __bool__(self):
        return False
    __nonzero__ = __bool__


# Not found singleton
NotFound = None, ()
