Aquarium.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 » Aquarium.py
"""This class encapsulates the main flow of control for Aquarium."""

__docformat__ = "restructuredtext"

# Created: Tue Sep 12 21:33:55 PDT 2000
# Author: Shannon -jj Behrens
# Email: jjinux@users.sourceforge.net
#
# Copyright (c) Shannon -jj Behrens.  All rights reserved.

from sys import exc_info

import aquarium.conf.AquariumProperties as properties


class Aquarium:

    """This class encapsulates the main flow of control for Aquarium.

    Almost everything important here can be configured via
    ``AquariumProperties``.  Nonetheless, if you still need more control, feel
    free to subclass this class.  In the entry point of your Web server, you'll
    need to use your class instead of this class.  To replace some of the core
    Aquarium classes, subclass this, and override the appropriate factory
    methods of below.  The ``__call__``, ``getInitAndCleanupTaskList``, and
    ``afterInit`` methods are also very appropriate for extending or
    overriding.

    The following private variables are used:

    _ctx
      This is our reference to ``ctx``.

    _prevExc, _prevExcInfo
      These are used by ``screenLoop`` and ``catchDoubleException``.

    """

    def __init__(self, wsadaptor):
        """Accept ``wsadaptor``, and prepare for real work.

        This also calls aquarium.util.InternalLibrary.clearModules_.

        .. _aquarium.util.InternalLibrary.clearModules:
           aquarium.util.InternalLibrary-module.html#clearModules

        """
        if not hasattr(properties, "CLEAR_MODULES"):
            raise AttributeError("""\
No properties.CLEAR_MODULES attribute.  Is the packagePath correctly set?
See aquarium.PackagePath.""")
        from aquarium.util.InternalLibrary import clearModules
        clearModules()
        ctx = self._ctx = self.createContext()
        ctx.wsa = wsadaptor
        ctx.iLib = self.createInternalLibrary()

    def createContext(self):
        from aquarium.util.Context import Context
        return Context()

    def createInternalLibrary(self):
        from aquarium.util.InternalLibrary import InternalLibrary
        return InternalLibrary(self._ctx)

    def __call__(self):
        """Handle the request.

        This encapsulates the main flow of control.  It is not a violation of
        encapsulation to read the source of this method.

        See tryFinally_.

        .. _aquarium.util.TryFinally.tryFinally:
           aquarium.util.TryFinally-module.html#tryFinally

        """
        from TryFinally import tryFinally
        tryFinally(self.getInitAndCleanupTaskList())

    def getInitAndCleanupTaskList(self):
        """Return all the init and cleanup methods in a list of tuples.

        This encapsulates the main flow of control.  It is not a violation of
        encapsulation to read the source of this method.

        See tryFinally_.

        .. _aquarium.util.TryFinally.tryFinally:
           aquarium.util.TryFinally-module.html#tryFinally

        """
        assert (not hasattr(self, "initAll") and
                not hasattr(self, "cleanupAll")), """
The initAll and cleanupAll methods are no longer supported because they could
not guarantee proper resource cleanup.  Extending
aquarium.util.Aquarium.getInitAndCleanupTaskList is the new way to do this."""
        return [
            ((),                            (self.cleanupContext,)      ),
            ((self.initCookies,),                                       ),
            ((self.initUrlScheme,),                                     ),
            ((self.initForm,),                                          ),
            ((self.initDatabase,),          (self.cleanupDatabase,)     ),
            ((self.initSession,),           (self.cleanupSession,)      ),
            ((self.initGetText,),                                       ),
            ((self.afterInit,),                                         ),
        ]

    def initCookies(self):
        """Get data from cookies, or start a new one.

        Add the following to ``self._ctx``: ``cookiesVerified``,
        ``request.cookie``, ``response.cookie``.

        """
        ctx = self._ctx
        ctx.cookiesVerified = 0
        if properties.USE_COOKIES:
            import Cookie
            ctx.request.cookie = Cookie.SimpleCookie()
            ctx.response.cookie = Cookie.SimpleCookie()
            cookieValue = ctx.wsa.getCgiEnv().get("HTTP_COOKIE")
            if cookieValue:
                ctx.request.cookie.load(cookieValue)
                ctx.cookiesVerified = 1

    def initUrlScheme(self):
        """Initialize the urlscheme instance.

        Add the following to ``self._ctx``: ``url`` (an urlscheme).

        """
        ctx = self._ctx
        ctx.url = ctx.iLib.aquariumFactory("urlscheme." + properties.URL_SCHEME)

    def initForm(self):
        """Retrieve the data from the form.

        If an ``aquarium.util.InternalLibrary.FormValueError`` is raised,
        direct the user to the exception screen.

        Add the following to ``self._ctx``: ``form`` (an instance of
        aquarium.util.FormDict_).

        .. _aquarium.util.FormDict: aquarium.util.FormDict.FormDict-class.html

        """
        from aquarium.util.FormDict import FormDict
        from aquarium.util.InternalLibrary import FormValueError
        ctx = self._ctx
        try:
            ctx.form = FormDict(ctx.wsa.getForm())
        except FormValueError:
            self._ctx.form = {}
            self._ctx.screen = "exception"
            self._ctx.args = (exc_info(),)
            self._ctx.kargs = {}

    def initDatabase(self):
        """Connect to the database.

        Add the following to ``self._ctx``: ``dba``, ``db`` (via
        aquarium.database.DatabaseAssistant_).

        .. _aquarium.database.DatabaseAssistant:
           aquarium.database.DatabaseAssistant.DatabaseAssistant-class.html

        """
        ctx = self._ctx
        if properties.USE_DATABASE:
            dba = ctx.dba = ctx.iLib.aquariumFactory(
                "database." + properties.AQUARIUM_DATABASE_ASSISTANT_MODULE)
            dba.connect()

    def cleanupDatabase(self):
        """Close the connection to the database."""
        if properties.USE_DATABASE:
            self._ctx.dba.close()

    def initSession(self):
        """Get data from session, or start a new one.

        Some session containers subclass aquarium.util.AquariumClass, and some
        don't.  Pass a ``ctx`` as necessary.

        Add the following to ``self._ctx``: ``session``.

        """
        ctx = self._ctx
        if properties.USE_SESSIONS:
            if (properties.USE_COOKIES and properties.USE_SESSION_COOKIES and
                ctx.request.cookie.has_key("sid")):
                sid = ctx.request.cookie["sid"].value
            else:
                sid = ctx.form.get("sid")
            sessionModule = "session." + properties.SESSION_CONTAINER_MODULE
            try:
                sessionContainer = ctx.iLib.aquariumFactory(
                    sessionModule, aquariumFactoryNoContext=1)
            except TypeError:
                sessionContainer = ctx.iLib.aquariumFactory(sessionModule)
            ctx.session = sessionContainer.open(sid)
            if properties.USE_COOKIES and properties.USE_SESSION_COOKIES:
                self.initSessionCookie()

    def initSessionCookie(self):
        """Initialize the session cookie.

        This is called by ``initSession``.

        If HTTPS is currently being used and ``properties.USE_SECURE_COOKIES``
        is ``True`` (the default), the secure attribute will be set.  This is
        good if you're worried about security, but bad if you bounce back and
        forth between HTTP and HTTPS.

        """
        from urlparse import urlparse
        ctx = self._ctx
        PATH = 2
        path = urlparse(ctx.url.getRootUrl())[PATH]
        ctx.response.cookie["sid"] = ctx.session["sid"]
        ctx.response.cookie["sid"]["path"] = path
        expiration = ctx.session.getCookieExpiration()
        if expiration:
            ctx.response.cookie["sid"]["expires"] = expiration
        if (ctx.url.getDefaultScheme() == "https://" and
            getattr(properties, "USE_SECURE_COOKIES", True)):
            ctx.response.cookie["sid"]["secure"] = "secure"

    def cleanupSession(self):
        """Call ``save`` on the session."""
        if properties.USE_SESSIONS:
            self._ctx.session.save()

    def initGetText(self):

        """Initialize gettext.

        Use the class API so that "_" is not globally installed.

        Add the following to ``self._ctx``:  ``translation``, ``_``,
        ``gettext``, ``ngettext``.  If ``properties.USE_GETTEXT`` is False,
        ``gettext.NullTranslations``.  That way, your code will work even if
        you use ``_()`` with gettext turned off.

        """

        # It's unnecessary to cache translation instances.  The gettext module
        # already does that.

        ctx = self._ctx
        if properties.USE_GETTEXT:
            from gettext import translation
            ctx.translation = translation(
                languages=self.getLanguagePreferences(),
                *properties.GETTEXT_ARGS, **properties.GETTEXT_KWARGS)
        else:
            from gettext import NullTranslations
            ctx.translation = NullTranslations()
        ctx.gettext = ctx.translation.gettext
        ctx.ngettext = ctx.translation.ngettext
        ctx._ = ctx.gettext

    def getLanguagePreferences(self):
        """Return a list of preferred languages, most preferred first.

        The list may be empty.  By default, just use parseAcceptLanguage_ on
        the ``ACCEPT_LANGUAGE`` header.  The list will be passed to
        ``filterLanguagePreferences``.

        .. _parseAcceptLanguage:
           aquarium.parse.AcceptLanguage-module.html#parseAcceptLanguage

        """
        from aquarium.parse.AcceptLanguage import parseAcceptLanguage
        acceptLanguage = self._ctx.wsa.getCgiEnv().get("HTTP_ACCEPT_LANGUAGE")
        languages = parseAcceptLanguage(acceptLanguage)
        self.filterLanguagePreferences(languages)
        return languages

    def filterLanguagePreferences(self, languages):
        """Update the ``languages`` list by applying additional logic.

        If not None, use ``properties.GETTEXT_ULTIMATE_FALLBACK`` (which
        defaults to "en-us") as an ultimate fallback.  That means it gets
        appended to the list of languages if it isn't already there.  It also
        means that if it's in the list of languages, everything after it is
        deleted.

        This deleting behavior is strange but useful.  Normally, everything in
        the code is in "en-us".  However, the "en-us" translation catalog is
        usually empty.  If the user requests ``["en-us", "zh-cn"]`` and a
        translation isn't found for a string in "en-us", you don't want
        gettext to fallback to "zh-cn".  You want it to just use the string
        itself.  Hence, if a string isn't found in the
        ``properties.GETTEXT_ULTIMATE_FALLBACK`` catalog, the string in the
        source code will be used.

        """
        ultimate = getattr(properties, "GETTEXT_ULTIMATE_FALLBACK", "en-us")
        if not ultimate:
            return
        if ultimate not in languages:
            languages.append(ultimate)
        index = languages.index(ultimate)
        languages[index+1:] = []

    def cleanupContext(self):
        """Help the garbage collector by emptying ``self._ctx``.

        This breaks all the circular references.  By having everything garbage
        collected nicely, it insures that all files, such as session files, get
        closed properly.

        """
        ctx = self._ctx
        for i in dir(ctx):
            try:
                delattr(ctx, i)
            except AttributeError:
                pass                # __getattr__ attributes can't be deleted.

    def afterInit(self):
        """Continue after initialization.

        This encapsulates the main flow of control.  It is not a violation of
        encapsulation to read the source of this method.

        """
        ctx = self._ctx
        generator = self.screenLoop()
        if not hasattr(ctx.screenInstance, "getHeaders"):
            raise AttributeError("""\
%s instance has no attribute 'getHeaders'.
This is usually caused by forgetting to forward from a controller to a view."""%
                ctx.screenInstance.__class__.__name__)
        ctx.wsa.writeHeaders(ctx.screenInstance.getHeaders())
        for i in generator:
            ctx.wsa.write(self.applyFilters(i))

    def screenLoop(self):
        """Show the desired screen, and loop around to handle exceptions.

        By using a loop, the same code path can be used for exceptions.  In
        fact:

        * The aquarium.util.InternalLibrary.Forward_ exception results in
          looping around to show the desired screen to forward to.

        * ``ImportError``'s and ``AttributeError``'s while trying to import the
          screen result in the ``not_found`` screen.

        * Any other ``Exception`` results in the ``exception`` screen.

        Be especially wary of infinite loops since we're catching all
        exceptions and looping around.  Use ``handleDoubleException`` in these
        cases, but note that it too will raise an exception, by design.

        If a screen runs successfully to completion, if the return value is a
        generator, just return it.  If it is a string, wrap it in a generator
        and return it.

        Add the following to ``self._ctx``: ``screenList``.

        .. _aquarium.util.InternalLibrary.Forward:
           aquarium.util.InternalLibrary.Forward-class.html

        """
        from aquarium.util.InternalLibrary import Forward
        class _Continue(Exception): pass    # Can't use continue in an except.
        def yield_this(arg): yield arg      # Wrap arg in a generator.
        MAX_FORWARDS = 1024
        ctx = self._ctx
        if hasattr(ctx, "screen"):          # We had an exception during init.
            screen, args, kargs = ctx.screen, ctx.args, ctx.kargs
        else:
            whichScreen = ctx.url.whichScreen()
            if isinstance(whichScreen, tuple):
                screen, args, kargs = whichScreen
            else:
                screen, args, kargs = whichScreen, (), {}
        self._prevExc, self._prevExcInfo = None, None
        if not screen:
            screen = properties.DEFAULT_SCREEN
        if not ctx.iLib.validModuleName(screen):
            try:                            # Assertions might be turned off.
                raise AssertionError("Invalid screen name: %s" % `screen`)
            except:
                screen, args, kargs = "not_found", (exc_info(),), {}
        for _ignored in xrange(MAX_FORWARDS):
            try:
                ctx.screen = screen
                ctx.screenList = getattr(ctx, "screenList", []) + [ctx.screen]
                try:
                    ctx.screenInstance = ctx.iLib.aquariumFactory(
                        "screen." + screen)
                except (ImportError, AttributeError), e:
                    self.catchDoubleException(e)
                    screen, args, kargs = "not_found", (exc_info(),), {}
                    raise _Continue
                ret = ctx.iLib.inverseExtend(ctx.screenInstance.__call__,
                                             *args, **kargs)
                if not hasattr(ret, "next"):
                    ret = yield_this(ret)
                return ret
            except _Continue:
                pass
            except Forward, e:
                screen, args, kargs = e.screen, e.args, e.kargs
            except Exception, e:
                self.catchDoubleException(e)
                screen, args, kargs = "exception", (exc_info(),), {}
        raise OverflowError("Too many forwards")

    def catchDoubleException(self, e):
        """Call ``handleDoubleException`` as needed.

        ``handleDoubleException`` raises its own exception, so this method
        should only return normally on the first exception.

        """
        if type(self._prevExc) == type(e):
            self.handleDoubleException((self._prevExcInfo, exc_info()))
        else:
            self._prevExc, self._prevExcInfo = e, exc_info()

    def handleDoubleException(self, excInfos):
        """Handle the case of a double exception.

        This happens when a normal screen raises an exception, and then later,
        the exception screen raises an exception.

        """
        from aquarium.util.InternalLibrary import getExceptionStr
        raise Exception("""

An exception has occurred:

%s
While attempting to show the exception screen, another exception occurred:

%s
The system is unable to handle the exception at this level.  Another exception
will be raised combining the previous two exceptions.""" %
            tuple([getExceptionStr(*i) for i in excInfos]))

    def applyFilters(self, buf):
        """Filter and return the given ``buf``.

        Filters are not run from within the big try/except block because you'll
        probably want filters run for the exception screen as well.
        Furthermore, filters really shouldn't be expected to raise exceptions
        on a daily basis.

        """
        ctx = self._ctx
        if properties.USE_FILTERS:
            for i in properties.FILTER_MODULES:
                buf = ctx.iLib.aquariumFactory("filter." + i)(buf)
        return buf
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.