aboutsummaryrefslogtreecommitdiff
path: root/system/python/spyce/spyce.py
diff options
context:
space:
mode:
Diffstat (limited to 'system/python/spyce/spyce.py')
-rwxr-xr-xsystem/python/spyce/spyce.py674
1 files changed, 674 insertions, 0 deletions
diff --git a/system/python/spyce/spyce.py b/system/python/spyce/spyce.py
new file mode 100755
index 0000000000..3a21f36d16
--- /dev/null
+++ b/system/python/spyce/spyce.py
@@ -0,0 +1,674 @@
+#!/usr/bin/env python
+
+__version__ = '1.3.13'
+__release__ = '1'
+
+DEBUG_ERROR = 0
+
+##################################################
+# 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/
+# CVS: $Id$
+##################################################
+
+# note: doc string used in documentation: doc/index.spy
+__doc__ = '''SPYCE is a server-side language that supports simple and
+efficient Python-based dynamic HTML generation, otherwise called <i>Python
+Server Pages</i> (PSP). Those who are familiar with JSP, PHP, or ASP and like
+Python, should have a look at Spyce. Its modular design makes it very flexible
+and extensible. It can also be used as a command-line utility for static text
+pre-processing or as a web-server proxy.'''
+
+import sys, os, copy, string, imp
+import spyceConfig, spyceCompile, spyceException
+import spyceModule, spyceTag
+import spyceLock, spyceCache, spyceUtil
+
+##################################################
+# Spyce engine globals
+#
+
+# spyceServer object - one per engine instance
+SPYCE_SERVER = None
+def getServer(
+ config_file=None,
+ overide_www_port=None,
+ overide_www_root=None,
+ force=0):
+ global SPYCE_SERVER
+ if force or not SPYCE_SERVER:
+ SPYCE_SERVER = spyceServer(
+ config_file=config_file,
+ overide_www_root=overide_www_root,
+ overide_www_port=overide_www_port,
+ )
+ 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')
+
+##################################################
+# 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_file=None,
+ overide_www_root=None,
+ overide_www_port=None,
+ ):
+ global SPYCE_GLOBALS
+ # server object
+ self.serverobject = spyceServerObject()
+ # http headers
+ try: self.entry = os.environ[SPYCE_ENTRY]
+ except: self.entry = 'UNKNOWN'
+ self.spyceHeader = 'Spyce/%s_%s Python/%s' % (self.entry, str(__version__), sys.version[:3])
+ # configuration dictionary
+ self.config = spyceConfig.spyceConfig(
+ file=config_file,
+ overide_www_root=overide_www_root,
+ overide_www_port=overide_www_port,
+ )
+ # server globals/constants
+ self.globals = self.config.getSpyceGlobals()
+ SPYCE_GLOBALS = self.globals # hack
+ # now finish processing config file; this way imported modules have
+ # access to the globals
+ self.config.process ()
+ # spyce module search path
+ self.path = self.config.getSpycePath()
+ # concurrency mode
+ self.concurrency = self.config.getSpyceConcurrency()
+ # imports
+ self.imports = self.config.getSpyceImport()
+ # debug mode
+ self.debug = self.config.getSpyceDebug()
+ # spyce cache
+ type, info = self.config.getSpyceCache()
+ if type in ('file',):
+ type = spyceCache.fileCache(info)
+ elif type in ('mem', 'memory'):
+ type = spyceCache.memoryCache(info)
+ else: type = spyceCache.memoryCache()
+ if self.debug: type = None
+ self.spyce_cache = spyceCache.semanticCache(type, spyceCacheValid, spyceCacheGenerate)
+ # spyce module cache
+ self.module_cache = {}
+ if self.debug:
+ self.module_cache = None
+ # page error handler
+ pageerror = self.config.getSpycePageError()
+ if pageerror[0]=='string':
+ pageerror = pageerror[0], self.loadModule(pageerror[2], pageerror[1]+'.py')
+ self.pageerror = pageerror
+ # engine error handler
+ error = self.config.getSpyceError()
+ self.error = self.loadModule(error[1], error[0]+'.py')
+ # spyce thread-safe stdout object
+ if self.concurrency == spyceConfig.SPYCE_CONCURRENCY_THREAD:
+ self.stdout = spyceUtil.ThreadedWriter(sys.stdout)
+ self.lock = spyceLock.threadLock()
+ sys.stdout = self.stdout
+ else:
+ self.stdout = None
+ self.lock = spyceLock.dummyLock()
+ # set sys.stdout
+ def loadModule(self, name, file=None, rel_file=None):
+ "Find and load a module, with caching"
+ if not file: file=name+'.py'
+ key = name, file, rel_file
+ if self.module_cache!=None:
+ try: return self.module_cache[key]
+ except: pass # cache miss
+ def loadModuleHelper(file=file, rel_file=rel_file, path=self.path):
+ if rel_file: path = path + [os.path.dirname(rel_file)]
+ for p in path:
+ f=None
+ try:
+ p = os.path.join(p, file)
+ if os.path.exists(p) and os.access(p, os.R_OK):
+ f = open(p, 'r')
+ return imp.load_source(SPYCE_LOADER, p, f)
+ finally:
+ if f: f.close()
+ raise 'unable to find module "%s" in path: %s' % (file, path)
+ # load and cache module
+ dict = {'loadModuleHelper': loadModuleHelper}
+ exec 'foo = loadModuleHelper()' in dict
+ mod = eval('dict["foo"].%s' % name)
+ if self.module_cache!=None:
+ self.module_cache[key] = mod
+ return mod
+ 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 NOT thread safe."
+ try:
+ thespyce = theError = None
+ try:
+ spycecode = self.spyce_cache[spyceInfo]
+ thespyce = spycecode.newWrapper()
+ response.addHeader('X-Spyce', self.spyceHeader, 1)
+ try:
+ thespyce.spyceInit(request, response)
+ if args==None: args=[]
+ if kwargs==None: kwargs={}
+ apply(thespyce.spyceProcess, args, kwargs)
+ except spyceException.spyceRuntimeException, theError: pass
+ finally:
+ if DEBUG_ERROR and theError:
+ sys.stderr.write(`theError`+'\n')
+ if thespyce:
+ thespyce.spyceDestroy(theError)
+ spycecode.returnWrapper(thespyce)
+ 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:
+ 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
+ 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, filename=None, sig='', limit=3):
+ # store variables
+ self._filename = filename
+ self._limit = limit
+ # generate code
+ self._code, self._coderefs, self._modrefs = \
+ spyceCompile.spyceCompile(code, filename, sig, getServer())
+ self._wrapperQueue = []
+ self._wrappersMade = 0
+ # wrappers
+ def newWrapper(self):
+ "Get a wrapper for this code from queue, or make new one"
+ try: return self._wrapperQueue.pop()
+ except IndexError: pass
+ self._wrappersMade = self._wrappersMade + 1
+ return spyceWrapper(self)
+ def returnWrapper(self, w):
+ "Return wrapper back to queue after use"
+ if len(self._wrapperQueue)<self._limit:
+ self._wrapperQueue.append(w)
+ # serialization
+ def __getstate__(self):
+ return self._filename, self._code, self._coderefs, self._modrefs, self._limit
+ def __setstate__(self, state):
+ self._filename, self._code, self._coderefs, self._modrefs, self._limit = state
+ self._wrapperQueue = []
+ self._wrappersMade = 0
+ # 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 import references in 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. It is
+ NOT thread safe - new instances are generated as necessary by spyceCode!
+ 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 arguable 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 = {}
+ # compile python code
+ self._codeenv = { spyceCompile.SPYCE_WRAPPER: self._api }
+ try: exec self._spycecode.getCode() in self._codeenv
+ except SyntaxError: raise spyceException.pythonSyntaxError(self)
+ self._freshenv = self._codeenv.keys()
+ # spyce hooks
+ noop = lambda *args: None
+ self.process = self._codeenv[spyceCompile.SPYCE_PROCESS_FUNC]
+ try: self.init = self._codeenv[spyceCompile.SPYCE_INIT_FUNC]
+ except: self.init = noop
+ try: self.destroy = self._codeenv[spyceCompile.SPYCE_DESTROY_FUNC]
+ except: self.destroy = noop
+ # request, response
+ self._response = self._request = None
+ self._responseCallback = {}
+ self._moduleCallback = {}
+ 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):
+ modclass = getServer().loadModule(name, file, self._spycecode.getFilename())
+ mod = modclass(self._api)
+ self.setModule(as, mod, 0)
+ if DEBUG_ERROR:
+ sys.stderr.write(as+'.start\n')
+ 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._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)
+ exec '_spyce_start()' in { '_spyce_start': self.init }
+ def spyceProcess(self, *args, **kwargs):
+ "Process the current Spyce request."
+ path = sys.path
+ try:
+ 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 = apply(_spyce_process, _spyce_args, _spyce_kwargs)' in dict
+ return dict['_spyce_result']
+ finally:
+ sys.path = path
+ def spyceDestroy(self, theError=None):
+ "Cleanup after the request processing."
+ try:
+ exec '_spyce_finish()' in { '_spyce_finish': self.destroy }
+ # explicit modules
+ self._modstarted.reverse()
+ for as, mod in self._modstarted:
+ try:
+ if DEBUG_ERROR:
+ sys.stderr.write(as+'.finish\n')
+ mod.finish(theError)
+ except spyceException.spyceDone: pass
+ except spyceException.spyceRedirect, e:
+ return spyceFileHandler(self._request, self._response, e.filename)
+ except KeyboardInterrupt: raise
+ except SystemExit: pass
+ except: theError = spyceException.spyceRuntimeException(self._api)
+ finishError = None
+ # default modules
+ self._modstarteddefault.reverse()
+ for as, mod in self._modstarteddefault:
+ try:
+ if DEBUG_ERROR:
+ sys.stderr.write(as+'.finish\n')
+ mod.finish(theError)
+ except: finishError = 1
+ self._request = None
+ self._response = None
+ if finishError: raise
+ finally:
+ self.spyceCleanup()
+ def spyceCleanup(self):
+ "Sweep the Spyce environment."
+ self._modstarteddefault = []
+ self._modstarted = []
+ self._modules = {}
+ if self._freshenv!=None:
+ for e in self._codeenv.keys():
+ if e not in self._freshenv:
+ del self._codeenv[e]
+ # API methods
+ 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):
+ """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)
+ 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, ''
+ try:
+ if not os.path.exists(filename):
+ return 0
+ if not os.access(filename, os.R_OK):
+ return 0
+ return os.path.getmtime(filename) == validity
+ except KeyboardInterrupt: raise
+ except:
+ return 0
+
+def spyceFileCacheGenerate(key):
+ "Generate new Spyce wrapper (recompiles)."
+ try:
+ filename, sig = key
+ except:
+ filename, sig = key, ''
+ # 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 = os.path.getmtime(filename)
+ f = None
+ try:
+ f = open(filename)
+ code = f.read()
+ finally:
+ if f: f.close()
+ s = spyceCode(code, 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, 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)
+
+
+##################################################
+# Spyce common entry points
+#
+
+def spyceFileHandler(request, response, filename, sig='', args=None, kwargs=None, config_file=None):
+ return _spyceCommonHandler(request, response, ('file', (filename, sig)), args, kwargs, config_file)
+
+def spyceStringHandler(request, response, code, sig='', args=None, kwargs=None, config_file=None):
+ return _spyceCommonHandler(request, response, ('string', (code, sig)), args, kwargs, config_file)
+
+def _spyceCommonHandler(request, response, spyceInfo, args=None, kwargs=None, config_file=None):
+ return getServer(config_file).commonHandler(request, response, spyceInfo, args, kwargs)
+
+if __name__ == '__main__':
+ execfile('run_spyceCmd.py')
+