spyce.py :  » Web-Frameworks » Spyce » spyce-2.1 » 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 » Spyce 
Spyce » spyce 2.1 » spyce.py
#!/usr/bin/env python

__version__ = '2.1'
__release__ = '3'

DEBUG_ERROR = False

def DEBUG(s):
  if DEBUG_ERROR:
    sys.stderr.write('%s\n' % s)

##################################################
# SPYCE - Python-based HTML Scripting
# Copyright (c) 2002 Rimon Barr.
#
# Refer to LICENCE for legalese
#
# Name:        spyce
# Author:      Rimon Barr <rimon-AT-acm.org>
# Start date:  8 April 2002
# Purpose:     Python Server Pages
# WWW:         http://spyce.sourceforge.net/
##################################################

# note: doc string used in documentation: doc/index.spy
__doc__ = '''
Spyce is a server-side language that supports elegant and
efficient Python-based dynamic HTML generation.
Spyce allows embedding Python in pages similar to how JSP embeds Java,
but Spyce is far more than a JSP clone.  Out of the box, Spyce provides
development as rapid as other modern frameworks like Rails, but with an
cohesive design rather than a morass of special cases.
'''

import sys, os, copy, string, imp, Queue, time, traceback
import spyceCompile, spyceException
import spyceModule, spyceTag
import spyceLock, spyceCache, spyceUtil

##################################################
# Spyce engine globals
#

MAX_STACK = 100
WRAPPER_LIMIT = 3

# spyceServer object - one per engine instance
SPYCE_SERVER = None
def getServer(config=None):
  if not SPYCE_SERVER:
    # constructor sets SPYCE_SERVER
    if not config:
      import spycePreload
      config = spycePreload.getConfigModule() # guess
    DEBUG('creating server with config from ' + config.__file__)
    spyceServer(config)
  return SPYCE_SERVER

SPYCE_GLOBALS = None
def getServerGlobals():
  global SPYCE_GLOBALS
  return SPYCE_GLOBALS

SPYCE_LOADER = 'spyceLoader'
SPYCE_ENTRY = 'SPYCE_ENTRY'
DEFAULT_MODULES = ('request', 'response', 'stdout', 'error')

# handler ids can change when tag is recompiled, so we need to invalidate
# dependant pages when a tag changes
tag_dependencies = {}

##################################################
# Spyce core objects
#

class spyceServerObject:
  "serverObject placeholder"
  pass

class spyceServer:
  "One per server, stored in SPYCE_SERVER (above) at processing of first request."
  def __init__(self, config):
    global SPYCE_SERVER, SPYCE_GLOBALS
    # it's possible to call getServer recursively -- one way is from an import
    # in spyceconf.  This ensures all calls get the same object.
    SPYCE_SERVER = self 
    # server object
    self.serverobject = spyceServerObject()

    # http headers
    try: self.entry = os.environ[SPYCE_ENTRY]
    except: self.entry = 'UNKNOWN'

    self.config = config
    global DEBUG_ERROR, SPYCE_GLOBALS
    DEBUG_ERROR = self.config.debug
    self.globals = self.config.globals
    SPYCE_GLOBALS = self.globals # hack

    # spyce module search path
    self.path = self.config.path
    self.imports = self.config.imports

    # spyce module cache
    self.module_cache = {}
    self.module_coderefs = {}
    # page error handler
    pageerror = self.config.pageerrortemplate
    if pageerror[0]=='string':
      pageerror = pageerror[0], self.loadModule(pageerror[2], pageerror[1]+'.py')
    self.pageerror = pageerror
    # engine error handler
    self.error = self.config.errorhandler

    # spyce thread-safe stdout object
    if self.multithreaded():
      self.stdout = spyceUtil.ThreadedWriter(sys.stdout)
      sys.stdout = self.stdout
    else:
      self.stdout = None

    # spyce compilation cache
    raw_cache = self.config.cache
    if raw_cache in ('file',):
      raw_cache = spyceCache.fileCache(self.config.cachedir)
    elif raw_cache in ('mem', 'memory'):
      raw_cache = {}
    else:
      raise Exception('Unrecognized cache type ' + str(raw_cache))

    # spyce_cache needs to lock to avoid potential multiple spyceCode objects
    # for the same spyce file or string
    # (or, potentially corrupt compiled code in the case of a fileCache)
    cache_lock = self.createLock('spycecache')

    self.spyce_cache = spyceCache.semanticCache(raw_cache, spyceCacheValid, spyceCacheGenerate, cache_lock)

    # ensure that global tags at least import correctly
    checker = spyceTag.spyceTagChecker(self)
    for tagtuple in self.config.globaltags:
      L = list(tagtuple)
      L.append(None) # relfile for loadmodule
      try:
        checker.loadLib(*L)
      except:
        sys.stderr.write(spyceUtil.exceptionString() + '\n')
        raise Exception('Error loading global taglib %s' % (tagtuple,))

    # moved these here so imported modules can interact w/ getServer
    for i in self.config.imports:
      exec('import ' + i)

  def threaded(self):
    return 'spyceWWW' in sys.modules
  def multithreaded(self):
    return self.threaded() and self.config.minthreads > 1 and self.config.maxthreads > 1

  def createLock(self, lockname):
    if self.threaded():
      if self.multithreaded():
        return spyceLock.threadLock()
      else:
        return spyceLock.dummyLock()
    return spyceLock.fileLock(os.path.join(self.config.tmp, lockname))

  def _findModule(self, name, file, rel_file):
    """
    Find (and cache) the path a spyce module given by may be loaded from
    ("file" argument does not include any path information)
    rel_file is relevant because the "current directory" is always searched first.
    """
    if not file: file=name+'.py'
    key = name, file, rel_file
    try:
      pathkey = self.module_cache[key]
      path = pathkey[0]
    except KeyError:
      pathkey = None # 1st-level cache miss

    if not pathkey:
      if rel_file:
        # don't use path.append; we don't want to modify it for everybody!
        L = [os.path.dirname(rel_file)] + self.path
      else:
        L = self.path
      for path in L:
        f = None
        path = os.path.realpath(os.path.join(path, file))
        if os.path.exists(path) and os.access(path, os.R_OK):
          self.module_cache[key] = pathkey = (path, name)
          break
      if not pathkey:
        raise ImportError('unable to find module "%s" in path %s' % (file, L))
    return pathkey
    
  def loadModule(self, name, file=None, rel_file=None):
    """
    Find and load a spyce module, with caching.
    (I.e., return the class inheriting spyceModule from the python module
     in the given file -- the actual python module is not returned!)

    This is also used to load stuff that isn't actually a spyceModule --
    spyceTags and defaultErrorTemplate, for instance.

    Caching is performed
    at the name/actual filename [not file parameter] level.
    (This is done so that .spy files in different directories can ask
    for tags in their directory w/o worrying about name conflicts.)
    The 2nd level is the necessary one to avoid unnecessary reloads
    (which can break things that only expect to be loaded once);
    the first is just an optimization to avoid repeatedly walking the
    spyce path looking for the right source file.
    """
    pathkey = self._findModule(name, file, rel_file)
    path = pathkey[0]

    try:
      (mod, mtime) = self.module_cache[pathkey]
    except KeyError:
      DEBUG('cache miss for %s' % (pathkey,))
    else:
      if not self.config.check_mtime or mtime >= spyceCacheGetmtime(path):
        DEBUG('cache hit (%d) for %s in %s' % (mtime, name, path))
        return mod
      for callback in tag_dependencies.get(path, []):
        callback()
      # else continue w/ (re)load
      
    def loadModuleHelper(p=path, name=name, pathkey=pathkey):
      try:
        f = open(p)
        if (spyceUtil.isTagCollection(f)):
          realname = spyceCompile.SPYCE_LIBNAME
          code, coderefs, modrefs, tagrefs = \
              spyceCompile.spyceCompile(f.read(), p, '', getServer(), True)
          def clear():
            del self.module_cache[pathkey]
          for taglibsrc in tagrefs:
            tag_dependencies.setdefault(taglibsrc, []).append(clear)
          f.close()
          f = os.tmpfile()
          DEBUG('compiled tag library:\n%s' % code)
          f.write(code)
          f.flush()
          f.seek(0)
        else:
          realname = name

        try:
          imported = getattr(imp.load_source(SPYCE_LOADER, p, f), realname)
        except:
          sys.stderr.write('exception loading %s from %s:\n%s' % (name, p, spyceUtil.exceptionString()))
          ex = sys.exc_info()
          info = traceback.format_exception_only(ex[0], ex[1])[0][:-1]
          try:
            raise 'Error loading Spyce module %s -- %s' % (name, info)
          except:
            raise spyceException.spyceRuntimeException()
        try:
          imported.__file__ = p
        except AttributeError:
          pass # "module" being loaded was str or other __slots__ user
        self.module_cache[(p, name)] = (imported, spyceCacheGetmtime(p))
        try:
          self.module_coderefs[p] = coderefs
        except NameError:
          pass
        return imported
      finally:
        if f: f.close()

    dict = {'loadModuleHelper': loadModuleHelper}
    return loadModuleHelper()
  def fileHandler(self, request, response, filename, sig='', args=None, kwargs=None):
    return self.commonHandler(request, response, ('file', (filename, sig)), args, kwargs)
  def stringHandler(self, request, response, code, sig='', args=None, kwargs=None):
    return self.commonHandler(request, response, ('string', (code, sig)), args, kwargs)
  def commonHandler(self, request, response, spyceInfo, args=None, kwargs=None):
    "Handle a request. This method is threadsafe."
    start = time.time()
    try:
      thespyce = None
      theError = None
      try:
        spycecode = self.spyce_cache[spyceInfo]
        DEBUG('elapsed to get spycecode: %s' % (time.time() - start))
        thespyce = spycecode.newWrapper() # does own locking
        try:
          thespyce.spyceInit(request, response)
          DEBUG('elapsed to init wrapper: %s' % (time.time() - start))
          if args is None: args=[]
          if kwargs is None: kwargs={}
          parent_code = thespyce.spyceProcess(*args, **kwargs)
          if parent_code:
            return parent_code
        except spyceException.spyceRuntimeException, theError:
          DEBUG('caught RuntimeException')
          pass
      finally:
        if DEBUG_ERROR and theError:
          sys.stderr.write(spyceUtil.exceptionString() + '\n')
        if thespyce:
          thespyce.spyceDestroy(theError)
          spycecode.returnWrapper(thespyce)
          DEBUG('elapsed to finish: %s' % (time.time() - start))
    except spyceException.spyceDone: pass
    except spyceException.spyceRedirect, e:
      return spyceFileHandler(request, response, e.filename)
    except KeyboardInterrupt: raise
    except (spyceException.spyceNotFound, spyceException.spyceForbidden, 
        spyceException.spyceSyntaxError, spyceException.pythonSyntaxError, 
        SyntaxError), e:
      DEBUG('sending %s to errorhandler' % e)
      return self.error(self, request, response, e)
    except SystemExit: pass
    except:
      errorString = spyceUtil.exceptionString()
      try:
        import cgi
        response.clear()
        response.write('<html><pre>\n')
        response.write('Unexpected exception: (please report!)\n')
        response.write(cgi.escape(errorString))
        response.write('\n</pre></html>\n')
        response.returncode = response.RETURN_OK
      except:
        sys.stderr.write(errorString+'\n')
    return response.returncode

class spyceRequest:
  """Underlying Spyce request object. All implementations (CGI, Apache...)
  should subclass and implement the methods marked 'not implemented'."""
  def __init__(self):
    self._in = None
    self._stack = []
  def read(self, limit=None):
    if limit:
      return self._in.read(limit)
    else:
      return self._in.read()
  def readline(self, limit=None):
    if limit:
      return self._in.readline(limit)
    else:
      return self._in.readline()
  def env(self, name=None):
    raise 'not implemented'
  def getHeader(self, type=None):
    raise 'not implemented'
  def getServerID(self):
    raise 'not implemented'

class spyceResponse:
  """Underlying Spyce response object. All implementations (CGI, Apache...)
  should subclass and implement the methods marked 'not implemented', and
  also properly define the RETURN codes."""
  RETURN_CONTINUE = 100
  RETURN_SWITCHING_PROTOCOLS = 101
  RETURN_OK = 200
  RETURN_CREATED = 201
  RETURN_ACCEPTED = 202
  RETURN_NON_AUTHORITATIVE_INFORMATION = 203
  RETURN_NO_CONTENT = 204
  RETURN_RESET_CONTENT = 205
  RETURN_PARTIAL_CONTENT = 206
  RETURN_MULTIPLE_CHOICES = 300
  RETURN_MOVED_PERMANENTLY = 301
  RETURN_MOVED_TEMPORARILY = 302
  RETURN_SEE_OTHER = 303
  RETURN_NOT_MODIFIED = 304
  RETURN_USE_PROXY = 305
  RETURN_TEMPORARY_REDIRECT = 307
  RETURN_BAD_REQUEST = 400
  RETURN_UNAUTHORIZED = 401
  RETURN_PAYMENT_REQUIRED = 402
  RETURN_FORBIDDEN = 403
  RETURN_NOT_FOUND = 404
  RETURN_METHOD_NOT_ALLOWED = 405
  RETURN_NOT_ACCEPTABLE = 406
  RETURN_PROXY_AUTHENTICATION_REQUIRED = 407
  RETURN_REQUEST_TIMEOUT = 408
  RETURN_CONFLICT = 409
  RETURN_GONE = 410
  RETURN_LENGTH_REQUIRED = 411
  RETURN_PRECONDITION_FAILED = 412
  RETURN_REQUEST_ENTITY_TOO_LARGE = 413
  RETURN_REQUEST_URI_TOO_LONG = 414
  RETURN_UNSUPPORTED_MEDIA_TYPE = 415
  RETURN_REQUEST_RANGE_NOT_SATISFIABLE = 416
  RETURN_EXPECTATION_FAILED = 417
  RETURN_INTERNAL_SERVER_ERROR = 500
  RETURN_NOT_IMPLEMENTED = 501
  RETURN_BAD_GATEWAY = 502
  RETURN_SERVICE_UNAVAILABLE = 503
  RETURN_GATEWAY_TIMEOUT = 504
  RETURN_HTTP_VERSION_NOT_SUPPORTED = 505
  RETURN_CODE = {
    RETURN_CONTINUE: 'CONTINUE',
    RETURN_SWITCHING_PROTOCOLS: 'SWITCHING PROTOCOLS',
    RETURN_OK: 'OK',
    RETURN_CREATED: 'CREATED',
    RETURN_ACCEPTED: 'ACCEPTED',
    RETURN_NON_AUTHORITATIVE_INFORMATION: 'NON AUTHORITATIVE INFORMATION',
    RETURN_NO_CONTENT: 'NO CONTENT',
    RETURN_RESET_CONTENT: 'RESET CONTENT',
    RETURN_PARTIAL_CONTENT: 'PARTIAL CONTENT',
    RETURN_MULTIPLE_CHOICES: 'MULTIPLE CHOICES',
    RETURN_MOVED_PERMANENTLY: 'MOVED PERMANENTLY',
    RETURN_MOVED_TEMPORARILY: 'MOVED TEMPORARILY',
    RETURN_SEE_OTHER: 'SEE OTHER',
    RETURN_NOT_MODIFIED: 'NOT MODIFIED',
    RETURN_USE_PROXY: 'USE PROXY',
    RETURN_TEMPORARY_REDIRECT: 'TEMPORARY REDIRECT',
    RETURN_BAD_REQUEST: 'BAD REQUEST',
    RETURN_UNAUTHORIZED: 'UNAUTHORIZED',
    RETURN_PAYMENT_REQUIRED: 'PAYMENT REQUIRED',
    RETURN_FORBIDDEN: 'FORBIDDEN',
    RETURN_NOT_FOUND: 'NOT FOUND',
    RETURN_METHOD_NOT_ALLOWED: 'METHOD NOT ALLOWED',
    RETURN_NOT_ACCEPTABLE: 'NOT ACCEPTABLE',
    RETURN_PROXY_AUTHENTICATION_REQUIRED: 'PROXY AUTHENTICATION REQUIRED',
    RETURN_REQUEST_TIMEOUT: 'REQUEST TIMEOUT',
    RETURN_CONFLICT: 'CONFLICT',
    RETURN_GONE: 'GONE',
    RETURN_LENGTH_REQUIRED: 'LENGTH REQUIRED',
    RETURN_PRECONDITION_FAILED: 'PRECONDITION FAILED',
    RETURN_REQUEST_ENTITY_TOO_LARGE: 'REQUEST ENTITY TOO LARGE',
    RETURN_REQUEST_URI_TOO_LONG: 'REQUEST URI TOO LONG',
    RETURN_UNSUPPORTED_MEDIA_TYPE: 'UNSUPPORTED MEDIA TYPE',
    RETURN_REQUEST_RANGE_NOT_SATISFIABLE: 'REQUEST RANGE NOT SATISFIABLE',
    RETURN_EXPECTATION_FAILED: 'EXPECTATION FAILED',
    RETURN_INTERNAL_SERVER_ERROR: 'INTERNAL SERVER ERROR',
    RETURN_NOT_IMPLEMENTED: 'NOT IMPLEMENTED',
    RETURN_BAD_GATEWAY: 'BAD GATEWAY',
    RETURN_SERVICE_UNAVAILABLE: 'SERVICE UNAVAILABLE',
    RETURN_GATEWAY_TIMEOUT: 'GATEWAY TIMEOUT',
    RETURN_HTTP_VERSION_NOT_SUPPORTED: 'HTTP VERSION NOT SUPPORTED',
  }
  def __init__(self):
    pass
  def write(self, s):
    raise 'not implemented'
  def writeErr(self, s):
    raise 'not implemented'
  def close(self):
    raise 'not implemented'
  def clear(self):
    raise 'not implemented'
  def sendHeaders(self):
    raise 'not implemented'
  def clearHeaders(self):
    raise 'not implemented'
  def setContentType(self, content_type):
    raise 'not implemented'
  def setReturnCode(self, code):
    raise 'not implemented'
  def addHeader(self, type, data, replace=0):
    raise 'not implemented'
  def flush(self):
    raise 'not implemented'
  def unbuffer(self):
    raise 'not implemented'

class spyceCode:
  '''Takes care of compiling the Spyce file, and generating a wrapper'''
  def __init__(self, code, key, filename=None, sig=''):
    # store variables
    self._filename = filename
    # generate code
    self._code, self._coderefs, self._modrefs, tagrefs = \
      spyceCompile.spyceCompile(code, filename, sig, getServer())
    def clear():
      del getServer().spyce_cache[key]
    for taglibsrc in tagrefs:
      tag_dependencies.setdefault(taglibsrc, []).append(clear)
    # wrapper instantiation is slow, so we keep a pool around
    self._wrapperQueue = Queue.Queue()
  # wrappers
  def newWrapper(self):
    """
    Get a wrapper for this code from queue, or make new one.
    Threadsafe thanke to queue object.
    """
    try: return self._wrapperQueue.get_nowait()
    except Queue.Empty: pass
    DEBUG('creating new wrapper for %s\n' % self._filename)
    return spyceWrapper(self)
  def returnWrapper(self, w):
    """
    Return wrapper back to queue after use
    (Mostly) Threadsafe thanke to queue object.
    ("Mostly" because we could actually store more wrappers than "limit"
    but this is not a problem worth introducing another lock to solve.)
    """
    if self._wrapperQueue.qsize() < WRAPPER_LIMIT:
      self._wrapperQueue.put(w)
  # serialization -- used by spyceCache.fileCache
  def __getstate__(self):
    return self._filename, self._code, self._coderefs, self._modrefs
  def __setstate__(self, state):
    self._filename, self._code, self._coderefs, self._modrefs = state
    # it's faster to recreate wrappers than to write them out/read back in
    self._wrapperQueue = Queue.Queue()
  # accessors
  def getCode(self):
    "Return processed Spyce (i.e. Python) code"
    return self._code
  def getFilename(self):
    "Return source filename, if it exists"
    return self._filename
  def getCodeRefs(self):
    "Return python-to-Spyce code line references"
    return self._coderefs
  def getModRefs(self):
    "Return list of Spyce modules imported by Spyce code"
    return self._modrefs

class spyceWrapper:
  """Wrapper object runs the entire show, bringing together the code, the
  Spyce environment, the request and response objects and the modules. 
  This object is generated by a spyceCode object. The common Spyce handler
  code calls the 'processing' functions. Module writers interact with this
  object via the spyceModuleAPI calls. This is arguably the trickiest portion
  of the Spyce so don't touch unless you know what you are doing."""
  def __init__(self, spycecode):
    # store variables
    self._spycecode = spycecode
    # api object
    self._api = self
    # module tracking
    self._modCache = {}
    self._modstarted = []
    self._modules = {}
    # insert compiled python code into the _codeenv context
    self._codeenv = {
      spyceCompile.SPYCE_WRAPPER: self._api,
      'db': getServer().config.db
      }
    try: exec self.getCode() in self._codeenv
    except SyntaxError: raise spyceException.pythonSyntaxError(self)
    # remember what freshly loaded context looked like, so we can
    # re-use this wrapper for others requests to the same Spyce.
    self._initialEnvKeys = self._codeenv.keys()
    # request, response
    self._response = self._request = None
    self._responseCallback = {}
    self._moduleCallback = {}
    self._parent = None
  def _startModule(self, name, file=None, as=None, force=0):
    "Initialise module for current request."
    if as==None: as=name
    if force or not self._codeenv.has_key(as):
      DEBUG(as+'.load')
      modclass = getServer().loadModule(name, file, self._spycecode.getFilename())
      mod = modclass(self._api)
      self.setModule(as, mod, 0)
      DEBUG(as+'.start')
      mod.start()
      self._modstarted.append((as, mod))
    else: mod = self._codeenv[as]
    return mod
  # spyce processing
  def spyceInit(self, request, response):
    "Initialise a Spyce for processing."
    self._parent = None
    self._request = request
    self._response = response
    for mod in DEFAULT_MODULES:
      self._startModule(mod)
    self._modstarteddefault = self._modstarted
    self._modstarted = []
    for (modname, modfrom, modas) in self.getModRefs():
      self._startModule(modname, modfrom, modas, 1)
    instance = self._codeenv[spyceCompile.SPYCE_CLASS]()
    self.process = getattr(instance, spyceCompile.SPYCE_PROCESS_FUNC)
  def spyceProcess(self, *args, **kwargs):
    """
    Process current request, including recursing to parent tags;
    returns parent return code, or None
    """
    if self._hasParent():
      self.getModules()['stdout'].push()
    try:
      self.spyceProcessSingle(*args, **kwargs)
    finally:
      if self._hasParent():
        result = self.getModules()['stdout'].pop()
    if self._hasParent():
      if self._parent: # may have been if-d out
        if len(self._request._stack) >= MAX_STACK:
          try:
            # spoof a runtimeexception
            raise 'Maximum stack depth exceeded! (infinite parent template loop?)'
          except:
            raise spyceException.spyceRuntimeException()
        else:
          err = self._finishUserModules()
          if err: raise err
          (src, childargs) = self._parent
          childargs['_body'] = result
          return spyceFileHandler(self._request, self._response, src, 'child', kwargs={'child': spyceUtil.attrdict(childargs)})
      else:
        # could have had a parent, but didn't: write body to the "real" outstream
        self._response.write(result)
    return None
  def spyceProcessSingle(self, *args, **kwargs):
    "Process the current Spyce request; no parent tag processing"
    path = sys.path
    try:
      # munge path so .spy can import from .py in current directory
      if self._spycecode.getFilename():
        path = copy.copy(sys.path)
        sys.path.append(os.path.dirname(self._spycecode.getFilename()))
      dict = { '_spyce_process': self.process,
        '_spyce_args': args, '_spyce_kwargs': kwargs, }
      exec '_spyce_result = _spyce_process(*_spyce_args, **_spyce_kwargs)' in dict
      return dict['_spyce_result']
    finally:
      sys.path = path
  def _finishUserModules(self, theError=None):
    try:
      self._modstarted.reverse()
      for as, mod in self._modstarted:
        try: 
          DEBUG(as+'.finish')
          mod.finish(theError)
        except spyceException.spyceDone: pass
        except spyceException.spyceRedirect, e:
          # We don't want to just return a new handler here because
          # (a) we want to finish out the other modules first,
          # (b) it's not a form of state management we want to encourage,
          # (as a user, figuring out which module's redirect takes precedence could get tricky)
          # (c) finish is designed for a module to cleanup after itself, not to
          # silently affect the page it was on ("explicit is better than implicit")
          try:
            raise Exception("Modules may not perform internal redirects in their start/finish methods.  (External redirects are permitted, if you really must do this.)")
          except:
            theError = spyceException.spyceRuntimeException(self._api)
        except KeyboardInterrupt: raise
        except SystemExit: pass
        except:
          # let error module show an error page when it finishes
          theError = spyceException.spyceRuntimeException(self._api)
    finally:
      self._modstarted = []
    return theError
  def spyceDestroy(self, theError=None):
    "Cleanup after the request processing."
    theError = self._finishUserModules(theError) or theError
    finishError = None
    try:
      # default modules
      self._modstarteddefault.reverse()
      for as, mod in self._modstarteddefault:
        try: 
          DEBUG(as+'.finish')
          mod.finish(theError)
        except: finishError = 1
      self._request = None
      self._response = None
      if finishError: raise
    finally:
      self.spyceCleanup()
    DEBUG('finished destroy for %s' % str(self._spycecode._filename))
  def spyceCleanup(self):
    "Sweep the Spyce environment."
    self._modstarteddefault = []
    # done by _finishUserModules: self._modstarted = [] 
    self._modules = {}
    # changes to existing keys stick around but other globals are nuked
    # we'd reset back to a virgin dict entirely but that's too expensive
    # -- in practice this is "good enough"
    for e in self._codeenv.keys():
      if e not in self._initialEnvKeys:
        del self._codeenv[e]
  def _hasParent(self):
    return self._codeenv[spyceCompile.SPYCE_CLASS]._has_parent
  # API methods
  def getStack(self):
    "Return spyce call stack (includes, parent templates, internal redirects)"
    return self._request._stack
  def getFilename(self):
    "Return filename of current Spyce"
    return self._spycecode.getFilename()
  def getCode(self):
    "Return processed Spyce (i.e. Python) code"
    return self._spycecode.getCode()
  def getCodeRefs(self):
    "Return python-to-Spyce code line references"
    return self._spycecode.getCodeRefs()
  def getModRefs(self):
    "Return list of import references in Spyce code"
    return self._spycecode.getModRefs()
  def getServerObject(self):
    "Return unique (per engine instance) server object"
    return getServer().serverobject
  def getServerGlobals(self):
    "Return server configuration globals"
    return getServer().globals
  def getServerID(self):
    "Return unique server identifier"
    return self._request.getServerID()
  def getPageError(self):
    "Return default page error value"
    return getServer().pageerror
  def getRequest(self):
    "Return internal request object"
    return self._request
  def getResponse(self):
    "Return internal response object"
    return self._response
  def setResponse(self, o):
    "Set internal response object"
    self._response = o
    for f in self._responseCallback.keys(): f()
  def registerResponseCallback(self, f):
    "Register a callback for when internal response changes"
    self._responseCallback[f] = 1
  def unregisterResponseCallback(self, f):
    "Unregister a callback for when internal response changes"
    try: del self._responseCallback[f]
    except KeyError: pass
  def getModules(self):
    "Return references to currently loaded modules"
    return self._modules
  def getModule(self, name, as=None):
    """Get module reference. The module is dynamically loaded and initialised
    if it does not exist (ie. if it was not explicitly imported, but requested
    by another module during processing)"""
    return self._startModule(name, as=as)
  def setModule(self, name, mod, notify=1):
    "Add existing module (by reference) to Spyce namespace (used for includes)"
    self._codeenv[name] = mod
    self._modules[name] = mod
    if notify:
      for f in self._moduleCallback.keys(): 
        f()
  def delModule(self, name, notify=1):
    "Add existing module (by reference) to Spyce namespace (used for includes)"
    del self._codeenv[name]
    del self._modules[name]
    if notify:
      for f in self._moduleCallback.keys(): 
        f()
  def getGlobals(self):
    "Return the Spyce global namespace dictionary"
    return self._codeenv
  def registerModuleCallback(self, f):
    "Register a callback for modules change"
    self._moduleCallback[f] = 1
  def unregisterModuleCallback(self, f):
    "Unregister a callback for modules change"
    try: del self._moduleCallback[f]
    except KeyError: pass
  def spyceFile(self, file):
    "Return a spyceCode object of a file"
    return getServer().spyce_cache[('file', file)]
  def spyceString(self, code):
    "Return a spyceCode object of a string"
    return getServer().spyce_cache[('string', code)]
  def spyceModule(self, name, file=None, rel_file=None):
    "Return Spyce module class"
    return getServer().loadModule(name, file, rel_file)
  def spyceTaglib(self, name, file=None, rel_file=None):
    "Return Spyce taglib class"
    return getServer().loadModule(name, file, rel_file)
  def setStdout(self, out):
    "Set the stdout stream (thread-safe)"
    serverout = getServer().stdout
    if serverout: serverout.setObject(out)
    else: sys.stdout = out
  def getStdout(self):
    "Get the stdout stream (thread-safe)"
    serverout = getServer().stdout
    if serverout: return serverout.getObject()
    else: return sys.stdout

##################################################
# Spyce cache
#

def spyceFileCacheValid(key, validity):
  "Determine whether compiled Spyce is valid"
  try: 
    filename, sig = key
  except:
    filename, sig = key, ''
  if not os.path.exists(filename):
    return 0
  if not os.access(filename, os.R_OK):
    return 0
  return not getServer().config.check_mtime or spyceCacheGetmtime(filename) == validity

def spyceFileCacheGenerate(key):
  "Generate new Spyce wrapper (recompiles)."
  try: filename, sig = key
  except: filename, sig = key, ''
  DEBUG('generating new spyceCode for %s' % filename)
  # ensure file exists and we have permissions
  if not os.path.exists(filename):
    raise spyceException.spyceNotFound(filename)
  if not os.access(filename, os.R_OK):
    raise spyceException.spyceForbidden(filename)
  # generate
  mtime = spyceCacheGetmtime(filename)
  f = None
  try:
    f = open(filename)
    code = f.read()
  finally:
    if f: f.close()
  s = spyceCode(code, key, filename=filename, sig=sig)
  return mtime, s

def spyceStringCacheValid(code, validity):
  return 1

def spyceStringCacheGenerate(key):
  try: 
    code, sig = key
  except:
    code, sig = key, ''
  s = spyceCode(code, key, sig=sig)
  return None, s

def spyceCacheValid((type, key), validity):
  return { 
    'string': spyceStringCacheValid,
    'file': spyceFileCacheValid,
  }[type](key, validity)

def spyceCacheGenerate((type, key)):
  return {
    'string': spyceStringCacheGenerate,
    'file': spyceFileCacheGenerate,
  }[type](key)

def spyceCacheGetmtime(fname):
  while os.path.islink(fname): # chase links
    fname = os.path.join(os.path.dirname(fname), os.readlink(fname))
  return os.path.getmtime(fname)

##################################################
# Spyce common entry points
#

def spyceFileHandler(request, response, filename, sig='', args=None, kwargs=None, config=None):
  filename = os.path.realpath(filename) # normalize for cache's benefit
  request._stack.append(filename)
  return _spyceCommonHandler(request, response, ('file', (filename, sig)), args, kwargs, config)

def spyceStringHandler(request, response, code, sig='', args=None, kwargs=None, config=None):
  request._stack.append('string')
  return _spyceCommonHandler(request, response, ('string', (code, sig)), args, kwargs, config)

def _spyceCommonHandler(request, response, spyceInfo, args=None, kwargs=None, config=None):
  return getServer(config).commonHandler(request, response, spyceInfo, args, kwargs)

if __name__ == '__main__':
  execfile(os.path.join(os.path.split(__file__)[0],'run_spyceCmd.py'))

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