InternalLibrary.py :  » Web-Frameworks » Aquarium » aquarium-2.3 » aquarium » util » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Web Frameworks » Aquarium 
Aquarium » aquarium 2.3 » aquarium » util » InternalLibrary.py
"""This is the "standard library" for Aquarium's structure."""

__docformat__ = "restructuredtext"

# Created: Sat May  6 09:53:53 PDT 2000
# Author: Shannon -jj Behrens
# Email: jjinux@users.sourceforge.net
#
# Copyright (c) Shannon -jj Behrens.  All rights reserved.

from cStringIO import StringIO
import math
import os
import re
from stat import ST_MTIME
import sys
import time
import traceback
import types

from AquariumClass import AquariumClass
import aquarium.conf.AquariumProperties as properties
import HTTPResponses


class InternalLibrary(AquariumClass):

    """This is the "standard library" for Aquarium's structure."""

    def htmlEntities(self, s):
        """Return ``s`` after "HTML encoding" it (i.e. ``& -> &``, etc.).

        I'm leaving this here just in case I have to do charset specific
        entity encodings.

        """
        translations = {"&": "&amp;", '"': "&quot;", "<": "&lt;", ">": "&gt;"}
        l = list(str(s))
        for i in range(len(l)):
            c = l[i]
            l[i] = translations.get(c, c)
        return "".join(l)

    def javaScriptEscape(self, s, htmlent=True):
        """Prepare ``s`` for use in a JavaScript quoted string.

        Both ``"`` and ``'`` are escaped, so you can use the return value in
        either single or double quotes.

        Test cases::

            [('\x08', '\\b')    # backspace
             ('\x09', '\\t')    # horizontal tab
             ('\x0A', '\\n')    # line feed (new line)
             ('\x0B', '\\v')    # vertical tab
             ('\x0C', '\\f')    # form feed
             ('\x0D', '\\r')    # carriage return
             ('"', '\\"')       # double quote
             ("'", "\\'")       # single quote
             ('\\', '\\\\')]    # backslash

        """
        if not isinstance(s, unicode):
            s = str(s)                  # Never call str on unicode.
        esc_str = (
            s.replace('\\', '\\\\')
             .replace('\x08', '\\b')
             .replace('\x09', '\\t')
             .replace('\x0A', '\\n')
             .replace('\x0B', '\\v')
             .replace('\x0C', '\\f')
             .replace('\x0D', '\\r')
             .replace('"', '\\"')
             .replace("'", "\\'")
        )
        if htmlent:
            return self._ctx.htmlent(esc_str)
        return esc_str

    def javaScriptQuote(self, s, htmlent=True):
        """Escape ``s`` and wrap it in single quotes."""
        return "'%s'" % self.javaScriptEscape(s, htmlent)

    def validModuleName(self, name):
        """This is an alias for validModuleName_.

        .. _validModuleName:
           aquarium.util.InternalLibrary-module.html#validModuleName

        """
        return validModuleName(name)

    def aquariumFactory(self, moduleName, *args, **kargs):

        """Dynamically import and instantiate a class from Aquarium.

        This works for templates too.  In fact, this method will automatically
        compile templates as necessary.  Because it's difficult to distinguish
        a compiled template from a normal Python module, you should not create
        both a template and a normal Python module with the same name
        (excluding the extension) in the same directory as many bad things can
        happen.

        I will also recursively compile base templates if you're real nice to
        me and use lines that look like::

            #extends aquarium.foo.Bar

        Otherwise, this is a *very difficult* thing to do.  In fact, I'll go so
        far as to raise an exception if you try to extend something without
        using a module name that starts with "aquarium." (i.e. a module name
        that isn't fully specified).

        moduleName
          This is the name of the module relative to the aquarium package (e.g.
          ``screen.MyScreen``).

        ``*args``, ``**kargs``
          These will be passed to the class's constructor.  ``self._ctx`` will
          always be added to the front of ``*args`` unless you specify the
          keyword parameter ``aquariumFactoryNoContext=1`` (which will be
          removed).

        """

        # Add self._ctx to args, if appropriate.

        if (kargs.has_key("aquariumFactoryNoContext") and
            kargs["aquariumFactoryNoContext"]):
            del kargs["aquariumFactoryNoContext"]
        else:
            args = (self._ctx,) + args

        # Examine the moduleName.

        moduleName = "aquarium." + moduleName
        if not self.validModuleName(moduleName):
            raise ValueError("Invalid module name", moduleName)

        # Take care of compiling templates, if appropriate.  Make sure that
        # Cheetah is even installed, otherwise Aquarium just hangs and won't
        # even throw an exception.

        if (not sys.modules.has_key(moduleName) and
            properties.CHEETAH_COMPILE):
            import Cheetah
            self._findAndCompile(moduleName)

        # Now we're ready to do use __import__.

        name = moduleName.split(".")[-1]
        module = __import__(moduleName, {}, {}, [name])
        return getattr(module, name)(*args, **kargs)

    def _findAndCompile(self, moduleName):

        """Find and perhaps compile a class from Aquarium.

        moduleName
          Unlike ``aquariumFactory``, I expect this to start with "aquarium".

        """

        # Examine the moduleName, and create all the different versions of it
        # that we need.

        pieces = moduleName.split(".")
        package = ".".join(pieces[:-1])         # E.g. aquarium.screen.a
        moduleTypePieces = pieces[1:-1]         # E.g. ["screen", "a"]
        parentModule = pieces[-2]               # E.g. a
        name = pieces[-1]                       # E.g. A

        (packagePath, moduleCompileDir) = self._getModuleCompileDir(
            package, parentModule, moduleTypePieces)

        # Find the templateFile.  If you can't find it, assume it's just
        # a normal Python module and just return.

        for dir in packagePath:
            templateFile = os.path.join(dir, name + ".tmpl")
            if os.path.isfile(templateFile):
                break
        else:
            return

        # Where do we expect to find the compiledTemplate?

        if moduleCompileDir:
            compiledTemplate = os.path.join(moduleCompileDir, name + ".py")
        else:
            compiledTemplate = re.sub(r"\.tmpl$", ".py", templateFile)

        # Compile the template and its parent.

        if self._needsCompiling(templateFile, compiledTemplate):
            self._compile(moduleName, templateFile, compiledTemplate, name)
        self._compileParent(templateFile)

    def _getModuleCompileDir(self, package, parentModule, moduleTypePieces):
        """Return ``(packagePath, moduleCompileDir)``.

        Make use of ``properties.CHEETAH_COMPILE_DIRECTORY``.  If it is None,
        ``moduleCompileDir`` will also be None.

        """
        packagePath = __import__(package, {}, {}, parentModule).__path__
        if properties.CHEETAH_COMPILE_DIRECTORY:
            moduleCompileDir = os.path.join(
                properties.CHEETAH_COMPILE_DIRECTORY, *moduleTypePieces)
        else:
            moduleCompileDir = None
        return (packagePath, moduleCompileDir)

    def _needsCompiling(self, templateFile, compiledTemplate):
        """Does the template need to be compiled or recompiled?"""
        return (not os.path.isfile(compiledTemplate) or
                isModified(os.stat(compiledTemplate)[ST_MTIME],
                           os.stat(templateFile)[ST_MTIME]))

    def _compile(self, moduleName, templateFile, compiledTemplate, name):
        """Create any directories necessary and compile the template.

        The module will also contain a new attribute,
        ``__AQUARIUM_cheetahSrc__``, documenting where the original template
        was found.

        """
        from Cheetah.Compiler import Compiler
        dir = os.path.dirname(compiledTemplate)
        if not os.path.isdir(dir):
            os.makedirs(dir)
        f = open(compiledTemplate, "w")
        try:
            compiler = Compiler(file=templateFile, moduleName=name,
                                mainClassName=name)
            f.write(str(compiler))
            f.write("\n__AQUARIUM_cheetahSrc__ = r'%s'\n" % templateFile)
        finally:
            f.close()

    def _compileParent(self, templateFile):
        """Recursively update the parent class, as necessary."""
        f = open(templateFile)
        try:
            for line in f.readlines():
                matches = re.match(r"^#extends ((\w|\.)+)", line)
                if matches:
                    parentModuleName = matches.group(1)
                    if not parentModuleName.startswith("aquarium."):
                        msg = """\
It looks like you're trying to use "#extends" without a module name that starts
 with "aquarium." .  Since I can't handle that case, I'm going to go ahead and
 raise an exception.  I apologize for the inconvenience.  See
 InternalLibrary.aquariumFactory for more information.  By the way, you were
 trying to extend: %s."""
                        raise ImportError(msg % parentModuleName)
                    self._findAndCompile(parentModuleName)
        finally:
            f.close()

    def call(self, moduleName, *args, **kargs):
        """This is a convenience method for ``aquariumFactory().__call__()``.

        moduleName
          This is the name of the module relative to the aquarium package (e.g.
          ``screen.MyScreen``).

        ``*args``, ``**kargs``
          These will be passed to ``__call__``.

        If you need to pass additional arguments to ``aquariumFactory`` or if
        you need to call some other method than ``__call__``, don't use this
        method.

        """
        return self.aquariumFactory(moduleName)(*args, **kargs)

    def inverseExtend(self, boundMethod, *args, **kargs):
        """Iterate downward through a hierarchy calling a method at each step.

        In a sense, this is like the object oriented concept of extending a
        method, except the parent class wraps the child class instead of the
        other way around.  This method was inspired by Perl's Mason_.

        Just as with extending a method, you can pass whatever arguments you
        want to "super" (although here you're passing those arguments to the
        subclass via ``callNext``) and you can return whatever you want (the
        highest level method returns to the actual caller).

        If an instance has multiple superclasses, only the first is considered.

        boundMethod
          This is the bound method of the object you're interested in.

        ``*args``, ``**kargs``
          The arguments and keyword arguments to pass to the top-level method.

        You can call this method via something like this::

            inverseExtend(object.method, myArg, myOtherArg)

        When calling the method at each step, I'll call it like this::

            Class.method(object, callNext, *args, **kargs)

        However, the lowest level class's method has no ``callNext`` parameter,
        since it has no one else to call::

            Class.method(object, *args, **kargs)

        In the method::

            callNext(*args, **kargs)

        should be called when it is time to transfer control to the subclass.
        This may even be in the middle of the method.  Naturally, you don't
        have to pass ``*args``, ``**kargs``, but a common idiom is for the
        parent class to just receive ``*args`` and ``**kargs`` and pass them on
        unmodified.

        .. _Mason: http://www.masonhq.com

        """
        return _DescendClassHierarchyImpl(boundMethod)(*args, **kargs)

    def forward(self, screen, *args, **kargs):
        """Forward processing to a new screen.  This method does not return.

        Generate a ``Forward`` exception which
        aquarium.util.Aquarium.screenLoop_ class is prepared to catch.

        screen
          This is the module name of the screen relative to the screen
          directory.

        ``*args``, ``**kargs``
          The arguments to pass to the screen's ``__call__`` method.

        .. _aquarium.util.Aquarium.screenLoop:
            aquarium.util.Aquarium.Aquarium-class.html#screenLoop

        """
        raise Forward(screen, *args, **kargs)

    def redirect(self, url, httpResponse=HTTPResponses.TEMPORARY_REDIRECT):
        """Do an HTTP redirect.  This method does not return.

        url
          The URL to redirect the user to.

        httpResponse
          The HTTP response code.  See aquarium.util.HTTPResponses_.

        Use this method if you must redirect to a url not on this site.  In
        most cases, you'll want redirectSeeOther instead.

        (This works by doing a ``forward`` to aquarium.screen.redirect_.)

        .. _aquarium.util.HTTPResponses: aquarium.util.HTTPResponses-module.html
        .. _aquarium.screen.redirect:
           aquarium.screen.redirect.redirect-class.html

        """
        self.forward("redirect", url, httpResponse)

    def redirectSeeOther(self, screen, *args, **kargs):
        """Do an HTTP redirect using ``HTTPResponses.SEE_OTHER``.

        This is a convenience method for::

            import HTTPResponses
            url = ctx.url.screen(screen, *args, **kargs)
            ctx.iLib.redirect(url, HTTPResponses.SEE_OTHER)

        This method does not return.

        Sometimes you have a form (e.g. a login form), and after the user
        submits the form, you redirect him to some other screen within the
        site.  That's what the HTTP response "303 See Other" is made for, and
        hence, that's what this method is made for.

        """
        ctx = self._ctx
        url = ctx.url.screen(screen, *args, **kargs)
        ctx.iLib.redirect(url, HTTPResponses.SEE_OTHER)

    def redirectTemporary(self, screen, *args, **kargs):
        """Do an HTTP redirect using ``HTTPResponses.TEMPORARY_REDIRECT``.

        This is a convenience method for::

            import HTTPResponses
            url = ctx.url.screen(screen, *args, **kargs)
            ctx.iLib.redirect(url, HTTPResponses.TEMPORARY_REDIRECT)

        This method does not return.

        Use this method instead of redirectSeeOther if you want to use a
        TEMPORARY_REDIRECT instead of a SEE_OTHER redirect.  In most cases,
        you'll want redirectSeeOther instead.

        """
        ctx = self._ctx
        url = ctx.url.screen(screen, *args, **kargs)
        ctx.iLib.redirect(url, HTTPResponses.TEMPORARY_REDIRECT)


def validModuleName(name):
    """Return true if the given module name is valid.

    This is valid: ``foo.bar``
    These are not: ``.foo.bar``, ``foo..bar``, ``foo/bar``, etc.

    Please note that externally, screens use "/"'s, but this function
    expects those to have been changed to "."'s.

    """
    return cacheByArgs(_validModuleNameUncached, name)


def _validModuleNameUncached(name):
    pieces = name.split(".")
    for i in pieces:
        if not re.match(r"^\w+$", i):
            return False
    return True


_lastModuleUpdate = time.time()
def clearModules():

    """Clear ``sys.modules`` of specific types of modules if one is stale.

    See ``properties.CLEAR_MODULES``.

    I took this method out of the ``InternalLibrary`` class so that you can
    call it *really* early, even before you create a ``Context`` to pass to
    ``InternalLibrary``.

    This function also calls ``clearGetTextTranslations``.

    History
    -------

    The problem that this method solves is simple:  if I change a file, I don't
    want to have to restart the server.  It's a simple problem, but it's tough
    to implement right.  To prevent repeating mistakes, here's what has failed
    in the past:

    * Remove all modules from ``sys.modules`` on every page load.

      - Some modules have state.

    * Delete only those modules that don't have state.

      - There's no convenient way to know which ones have state.

    * Use module attributes.

      - It's not convenient.

    * Delete only those modules that have grown stale.

      - If a parent class gets reloaded, child classes in other modules will
        need to get reloaded, but we don't know which modules those classes are
        in.

    * Look through all the modules for module references to the modules
      that'll get deleted and delete those too.

      - Its very common to only import the class, not the whole module.  Hence,
        we still don't know which modules have child classes that need to get
        reloaded.

    * Just clear out ``sys.modules`` of all modules of certain types on every
      page load.

      - Even a moderate amount of kiddie clicking will result in exceptions.
        I think the browsers hide the problem, but you'll see the exceptions
        in the logs.

    * Clear out ``sys.modules`` of all modules of certain types on every page
      load, but only if at least one of the modules is stale.

      - This is good because it handles the kiddie clicking case, and it also
        handles the super class case.

    * We need to handle stale templates too.  ``aquariumFactory`` use to do
      this, but it suffered from the same super class problem.  Hence, if a
      ``.tmpl`` has been updated, its corresponding ``.py`` must be deleted.
      The other ``.py`` files don't need to be deleted.  However, all the
      ``sys.modules`` need to be cleared, just in case some class was using a
      superclass from that ``.py``.

    """

    def unlinkPyPyc(file):
        """Given a ``.py`` or ``.pyc``, delete both."""
        toDelete = [file]
        if file.endswith(".py"):
            toDelete.append(file + "c")
        else:
            toDelete.append(file[:-1])
        for i in toDelete:
            if os.path.exists(i):
                os.unlink(i)

    global _lastModuleUpdate
    clearGetTextTranslations()
    if not properties.CLEAR_MODULES:
        return

    # Figure out which modules are subject to getting deleted.

    deleteTheseTypes = properties.CLEAR_MODULES
    if not isinstance(deleteTheseTypes, list):
        # Update Seamstress Exchange's properties file if you change this.
        deleteTheseTypes = ["aquarium.layout","aquarium.navigation",
                            "aquarium.screen", "aquarium.widget"]
    assert not "aquarium.util" in deleteTheseTypes, """
Sorry, it's not possible to reload aquarium.util modules.  Afterall,
aquarium.util.Aquarium and aquarium.util.InternalLibrary are the core of
Aquarium.  Please update AquariumProperties.CLEAR_MODULES."""
    deleteThese = [
        moduleName
        for moduleType in deleteTheseTypes
            for moduleName in sys.modules.keys()
                if (moduleName == moduleType or
                    moduleName.startswith(moduleType + "."))
    ]

    # Are there any stale modules?

    staleModules = False
    for i in deleteThese:
        module = sys.modules[i]
        try:
            file = module.__file__
        except AttributeError:
            continue
        if file.endswith(".pyc") and os.path.exists(file[:-1]):
            file = file[:-1]
        if not os.path.exists(file):
            staleModules = True
            continue
        fileMTime = os.stat(file)[ST_MTIME]
        if isModified(_lastModuleUpdate, fileMTime):
            staleModules = True

        # Delete any stale compiled templates.

        if (properties.CHEETAH_COMPILE and
            hasattr(module, "__AQUARIUM_cheetahSrc__")):
            template = module.__AQUARIUM_cheetahSrc__
            if os.path.exists(template):
                templateMTime = os.stat(template)[ST_MTIME]
                if isModified(fileMTime, templateMTime):
                    unlinkPyPyc(file)
                    staleModules = True

    # Delete the stale modules.  I do this in a separate loop because you
    # can't modify a dictionary during an iteration.

    if staleModules:
        for i in deleteThese:
            del sys.modules[i]
    _lastModuleUpdate = time.time()


_lastGetTextUpdate = time.time()
def clearGetTextTranslations():
    """Clear ``gettext`` of stale translations.

    Because translation instances can create a chain of interrelated fallbacks,
    if even one of them is stale, I clear them all.

    This setting is controlled by ``properties.CLEAR_GETTEXT``.  However, if
    that is undefined (which is likely since it's only mentioned here), I will
    use the value::

        properties.CLEAR_MODULES and properties.USE_GETTEXT

    """
    global _lastGetTextUpdate
    default = properties.CLEAR_MODULES and properties.USE_GETTEXT
    if not getattr(properties, "CLEAR_GETTEXT", default):
        return
    import gettext
    for file in gettext._translations.keys():
        if isModified(_lastGetTextUpdate, os.stat(file)[ST_MTIME]):
            gettext._translations.clear()
            break
    _lastGetTextUpdate = time.time()


def isModified(lastUpdate, currentValue):
    """Is ``math.floor(lastUpdate) < math.floor(currentValue)``?

    lastUpdate
      This is either a timestamp or a ``stat[ST_MTIME]``.

    currentValue
      This is a ``stat[ST_MTIME]``.

    ``math.floor`` is used because the resolution of ``time.time()`` is higher
    than that of ``stat[ST_MTIME]`` on some operating systems / filesystems.

    """
    return math.floor(lastUpdate) < math.floor(currentValue)


def passStringIO(toArg, f, *args, **kargs):
    """Call ``f`` with a ``StringIO`` instead of an output file.

    toArg
      This is the name of one of ``f``'s parameters.  Create a new StringIO
      object and pass it in for this parameter.

    f
      This is some function that takes an output file.

    args, kargs
      These will also be passed to ``f``.

    When ``f`` completes, serialize the StringIO to a string, and return that.

    This function is really useful when working with
    ``traceback.print_exception``.

    """
    buf = StringIO()
    try:
        kargs[toArg] = buf
        f(*args, **kargs)
        value = buf.getvalue()
        return value
    finally:
        buf.close()


def getExceptionStr(etype=None, value=None, tb=None, *args, **kargs):
    """This is a convenience method for ``traceback.print_exception``.

    etype, value, tb
      If you specify these, I'll call ``traceback.print_exception``.
      Otherwise, I'll call ``traceback.print_exc``.

    args
      These are additional arguments to ``print_exc`` or ``print_exception``.

    Instead of accepting an output file, I'll use a ``StringIO`` and then
    return the results as a string.  That's the one bit of convenience I add.

    """
    if etype is None:
        f = traceback.print_exc
    else:
        f = traceback.print_exception
        args = (etype, value, tb) + args
    return passStringIO("file", f, *args, **kargs)


def cacheByArgs(f, *args):
    """Cache the results of ``f``.  Vary by ``args``.

    The cache will be stored as an attribute of ``f`` named ``_cacheByArgs``.

    Note that everything in ``args`` must be hashable (this implies
    non-modifiable) because ``args`` is used as a dictionary key.  For this
    same reason, I can't support ``**kargs`` (which would probably be pretty
    slow anyway).

    Since the cache is attached to the function, it lives as long as the
    function does.  If you need to throw the cache away on every request, use a
    new lambda for every request.

    Use this function very carefully.  Make sure you're not over-zealous in
    your cacheing; some things should not be cached.  Also, make sure your
    cache doesn't just grow infinitely; you should test to make sure you don't
    create a memory leak.  When you do test, remember to turn sessions off, or
    else you'll mistake the creation of a lot of sessions for a memory leak.

    """
    if not hasattr(f, "_cacheByArgs"):
        f.__dict__["_cacheByArgs"] = {}
    cache = f._cacheByArgs
    if not cache.has_key(args):
        cache[args] = f(*args)
    return cache[args]


class _DescendClassHierarchyImpl:

    """Implement the inverseExtend method.

    There are many ways to implement this method.  One involves the use of
    yield.  It doesn't require a ``callNext`` parameter, but it is not possible
    to use such an implementation in a function that has a return statement
    (which Cheetah methods always do, unfortunately).  Another involves the use
    of a closure.  This is elegant, but not viable if you are stuck with an old
    version of Python.  The implementation here is based on a class that has a
    __call__ method and can thus pretend to be a closure.  I may switch back to
    the closure version of this code once they upgrade the version of Python at
    work.

    Do not use this class directly, rather use
    aquarium.util.InternalLibrary.inverseExtend_.

    The following private variables are used:

    _obj
      This is the instance that the bound method belongs to.

    _methods
      This will contain the methods from the *least* senior classes first
      because it will be treated as a stack in ``__call__``.

    .. _aquarium.util.InternalLibrary.inverseExtend:
       aquarium.util.InternalLibrary.InternalLibrary-class.html#inverseExtend

    """

    def __init__(self, boundMethod):

        """Build all the necessary data structures."""

        self._obj = boundMethod.im_self
        methodName = boundMethod.im_func.__name__

        # Figure out the classes in the class hierarchy.  "classes" will
        # contain the most senior classes first.

        Class = self._obj.__class__
        classes = [Class]
        while Class.__bases__:
            Class = Class.__bases__[0]
            classes.insert(0, Class)

        # Skip classes that don't define the method.  Be careful with getattr
        # since it automatically looks in parent classes.  Be careful of
        # Python's new-style classes which return method-wrapper objects when
        # it's not possible to return an actual method.

        last = None
        self._methods = []
        for Class in classes:
            if hasattr(Class, methodName):
                method = getattr(Class, methodName)
                if type(method) == types.MethodType and method != last:
                    last = method
                    self._methods.insert(0, method)

    def __call__(self, *args, **kargs):
        """This is like ``super()``, but it calls the subclass's method.

        See aquarium.util.InternalLibrary.inverseExtend_.  Warning, this is a
        bound method that acts like a closure.

        .. _aquarium.util.InternalLibrary.inverseExtend:
           aquarium.util.InternalLibrary.InternalLibrary-class.html#inverseExtend

        """
        method = self._methods.pop()
        if len(self._methods):
            return method(self._obj, self.__call__, *args, **kargs)
        else:
            return method(self._obj, *args, **kargs)


class Forward(Exception):

    """Raise an instance of this class to make Aquarium do a forward.

    Actually, you should use aquarium.util.InternalLibrary.forward_ to do that
    for you.

    The following attributes are used:

    screen
      The module name of the screen relative to the ``screen`` directory
    ``*args``, ``**kargs``
      The arguments to pass to the screen's ``__call__`` method.

    .. _aquarium.util.InternalLibrary.forward:
       aquarium.util.InternalLibrary.InternalLibrary-class.html#forward

    """

    def __init__(self, screen, *args, **kargs):
        """Just accept the parameters."""
        Exception.__init__(self)
        self.screen = screen
        self.args = args
        self.kargs = kargs


class FormValueError(ValueError):

    """Subclass ``ValueError`` for form errors that shouldn't occur.

    I.e. this is for errors that are only the result of:

    a) the user hacking stuff.
    b) the programmer messing up.

    """

    pass
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.