#!/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 # Start date: 8 April 2002 # Purpose: Python Server Pages # WWW: http://spyce.sourceforge.net/ # CVS: $Id: spyce.py 31144 2012-07-15 15:38:56Z buzz $ ################################################## # 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 Python Server Pages (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('
\n')
        response.write('Unexpected exception: (please report!)\n')
        response.write(cgi.escape(errorString))
        response.write('\n
\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)