home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 January / maximum-cd-2011-01.iso / DiscContents / xbmc-9.11.exe / system / python / spyce / spyce.py < prev    next >
Encoding:
Python Source  |  2009-12-23  |  23.8 KB  |  675 lines

  1. #!/usr/bin/env python
  2.  
  3. __version__ = '1.3.13'
  4. __release__ = '1'
  5.  
  6. DEBUG_ERROR = 0
  7.  
  8. ##################################################
  9. # SPYCE - Python-based HTML Scripting
  10. # Copyright (c) 2002 Rimon Barr.
  11. #
  12. # Refer to LICENCE for legalese
  13. #
  14. # Name:        spyce
  15. # Author:      Rimon Barr <rimon-AT-acm.org>
  16. # Start date:  8 April 2002
  17. # Purpose:     Python Server Pages
  18. # WWW:         http://spyce.sourceforge.net/
  19. # CVS:         $Id: spyce.py 16779 2008-12-31 04:41:27Z monkeyman_67156 $
  20. ##################################################
  21.  
  22. # note: doc string used in documentation: doc/index.spy
  23. __doc__ = '''SPYCE is a server-side language that supports simple and
  24. efficient Python-based dynamic HTML generation, otherwise called <i>Python
  25. Server Pages</i> (PSP). Those who are familiar with JSP, PHP, or ASP and like
  26. Python, should have a look at Spyce. Its modular design makes it very flexible
  27. and extensible. It can also be used as a command-line utility for static text
  28. pre-processing or as a web-server proxy.'''
  29.  
  30. import sys, os, copy, string, imp
  31. import spyceConfig, spyceCompile, spyceException
  32. import spyceModule, spyceTag
  33. import spyceLock, spyceCache, spyceUtil
  34.  
  35. ##################################################
  36. # Spyce engine globals
  37. #
  38.  
  39. # spyceServer object - one per engine instance
  40. SPYCE_SERVER = None
  41. def getServer(
  42.     config_file=None, 
  43.     overide_www_port=None,
  44.     overide_www_root=None,
  45.     force=0):
  46.   global SPYCE_SERVER
  47.   if force or not SPYCE_SERVER:
  48.     SPYCE_SERVER = spyceServer(
  49.       config_file=config_file,
  50.       overide_www_root=overide_www_root,
  51.       overide_www_port=overide_www_port,
  52.     )
  53.   return SPYCE_SERVER
  54.  
  55. SPYCE_GLOBALS = None
  56. def getServerGlobals():
  57.   global SPYCE_GLOBALS
  58.   return SPYCE_GLOBALS
  59.  
  60. SPYCE_LOADER = 'spyceLoader'
  61. SPYCE_ENTRY = 'SPYCE_ENTRY'
  62. DEFAULT_MODULES = ('request', 'response', 'stdout', 'error')
  63.  
  64. ##################################################
  65. # Spyce core objects
  66. #
  67.  
  68. class spyceServerObject:
  69.   "serverObject placeholder"
  70.   pass
  71.  
  72. class spyceServer:
  73.   "One per server, stored in SPYCE_SERVER (above) at processing of first request."
  74.   def __init__(self, 
  75.       config_file=None,
  76.       overide_www_root=None,
  77.       overide_www_port=None,
  78.     ):
  79.     global SPYCE_GLOBALS
  80.     # server object
  81.     self.serverobject = spyceServerObject()
  82.     # http headers
  83.     try: self.entry = os.environ[SPYCE_ENTRY]
  84.     except: self.entry = 'UNKNOWN'
  85.     self.spyceHeader = 'Spyce/%s_%s Python/%s' % (self.entry, str(__version__), sys.version[:3])
  86.     # configuration dictionary
  87.     self.config = spyceConfig.spyceConfig(
  88.       file=config_file,
  89.       overide_www_root=overide_www_root,
  90.       overide_www_port=overide_www_port,
  91.     )
  92.     # server globals/constants
  93.     self.globals = self.config.getSpyceGlobals()
  94.     SPYCE_GLOBALS = self.globals # hack
  95.     # now finish processing config file; this way imported modules have
  96.     # access to the globals
  97.     self.config.process ()
  98.     # spyce module search path
  99.     self.path = self.config.getSpycePath()
  100.     # concurrency mode
  101.     self.concurrency = self.config.getSpyceConcurrency()
  102.     # imports
  103.     self.imports = self.config.getSpyceImport()
  104.     # debug mode
  105.     self.debug = self.config.getSpyceDebug()
  106.     # spyce cache
  107.     type, info = self.config.getSpyceCache()
  108.     if type in ('file',):
  109.       type = spyceCache.fileCache(info)
  110.     elif type in ('mem', 'memory'):
  111.       type = spyceCache.memoryCache(info)
  112.     else: type = spyceCache.memoryCache()
  113.     if self.debug: type = None
  114.     self.spyce_cache = spyceCache.semanticCache(type, spyceCacheValid, spyceCacheGenerate)
  115.     # spyce module cache
  116.     self.module_cache = {}
  117.     if self.debug:
  118.       self.module_cache = None
  119.     # page error handler
  120.     pageerror = self.config.getSpycePageError()
  121.     if pageerror[0]=='string':
  122.       pageerror = pageerror[0], self.loadModule(pageerror[2], pageerror[1]+'.py')
  123.     self.pageerror = pageerror
  124.     # engine error handler
  125.     error = self.config.getSpyceError()
  126.     self.error = self.loadModule(error[1], error[0]+'.py')
  127.     # spyce thread-safe stdout object
  128.     if self.concurrency == spyceConfig.SPYCE_CONCURRENCY_THREAD:
  129.       self.stdout = spyceUtil.ThreadedWriter(sys.stdout)
  130.       self.lock = spyceLock.threadLock()
  131.       sys.stdout = self.stdout
  132.     else:
  133.       self.stdout = None
  134.       self.lock = spyceLock.dummyLock()
  135.     # set sys.stdout
  136.   def loadModule(self, name, file=None, rel_file=None):
  137.     "Find and load a module, with caching"
  138.     if not file: file=name+'.py'
  139.     key = name, file, rel_file
  140.     if self.module_cache!=None:
  141.       try: return self.module_cache[key]
  142.       except: pass # cache miss
  143.     def loadModuleHelper(file=file, rel_file=rel_file, path=self.path):
  144.       if rel_file: path = path + [os.path.dirname(rel_file)]
  145.       for p in path:
  146.         f=None
  147.         try:
  148.           p = os.path.join(p, file)
  149.           if os.path.exists(p) and os.access(p, os.R_OK):
  150.             f = open(p, 'r')
  151.             return imp.load_source(SPYCE_LOADER, p, f)
  152.         finally:
  153.           if f: f.close()
  154.       raise 'unable to find module "%s" in path: %s' % (file, path)
  155.     # load and cache module
  156.     dict = {'loadModuleHelper': loadModuleHelper}
  157.     exec 'foo = loadModuleHelper()' in dict
  158.     mod = eval('dict["foo"].%s' % name)
  159.     if self.module_cache!=None:
  160.       self.module_cache[key] = mod
  161.     return mod
  162.   def fileHandler(self, request, response, filename, sig='', args=None, kwargs=None):
  163.     return self.commonHandler(request, response, ('file', (filename, sig)), args, kwargs)
  164.   def stringHandler(self, request, response, code, sig='', args=None, kwargs=None):
  165.     return self.commonHandler(request, response, ('string', (code, sig)), args, kwargs)
  166.   def commonHandler(self, request, response, spyceInfo, args=None, kwargs=None):
  167.     "Handle a request. This method is NOT thread safe."
  168.     try:
  169.       thespyce = theError = None
  170.       try:
  171.         spycecode = self.spyce_cache[spyceInfo]
  172.         thespyce = spycecode.newWrapper()
  173.         response.addHeader('X-Spyce', self.spyceHeader, 1)
  174.         try:
  175.           thespyce.spyceInit(request, response)
  176.           if args==None: args=[]
  177.           if kwargs==None: kwargs={}
  178.           apply(thespyce.spyceProcess, args, kwargs)
  179.         except spyceException.spyceRuntimeException, theError: pass
  180.       finally:
  181.         if DEBUG_ERROR and theError:
  182.           sys.stderr.write(`theError`+'\n')
  183.         if thespyce:
  184.           thespyce.spyceDestroy(theError)
  185.           spycecode.returnWrapper(thespyce)
  186.     except spyceException.spyceDone: pass
  187.     except spyceException.spyceRedirect, e:
  188.       return spyceFileHandler(request, response, e.filename)
  189.     except KeyboardInterrupt: raise
  190.     except (spyceException.spyceNotFound, spyceException.spyceForbidden, 
  191.         spyceException.spyceSyntaxError, spyceException.pythonSyntaxError, 
  192.         SyntaxError), e:
  193.       return self.error(self, request, response, e)
  194.     except SystemExit: pass
  195.     except:
  196.       errorString = spyceUtil.exceptionString()
  197.       try:
  198.         import cgi
  199.         response.clear()
  200.         response.write('<html><pre>\n')
  201.         response.write('Unexpected exception: (please report!)\n')
  202.         response.write(cgi.escape(errorString))
  203.         response.write('\n</pre></html>\n')
  204.         response.returncode = response.RETURN_OK
  205.       except:
  206.         sys.stderr.write(errorString+'\n')
  207.     return response.returncode
  208.  
  209. class spyceRequest:
  210.   """Underlying Spyce request object. All implementations (CGI, Apache...)
  211.   should subclass and implement the methods marked 'not implemented'."""
  212.   def __init__(self):
  213.     self._in = None
  214.   def read(self, limit=None):
  215.     if limit:
  216.       return self._in.read(limit)
  217.     else:
  218.       return self._in.read()
  219.   def readline(self, limit=None):
  220.     if limit:
  221.       return self._in.readline(limit)
  222.     else:
  223.       return self._in.readline()
  224.   def env(self, name=None):
  225.     raise 'not implemented'
  226.   def getHeader(self, type=None):
  227.     raise 'not implemented'
  228.   def getServerID(self):
  229.     raise 'not implemented'
  230.  
  231. class spyceResponse:
  232.   """Underlying Spyce response object. All implementations (CGI, Apache...)
  233.   should subclass and implement the methods marked 'not implemented', and
  234.   also properly define the RETURN codes."""
  235.   RETURN_CONTINUE = 100
  236.   RETURN_SWITCHING_PROTOCOLS = 101
  237.   RETURN_OK = 200
  238.   RETURN_CREATED = 201
  239.   RETURN_ACCEPTED = 202
  240.   RETURN_NON_AUTHORITATIVE_INFORMATION = 203
  241.   RETURN_NO_CONTENT = 204
  242.   RETURN_RESET_CONTENT = 205
  243.   RETURN_PARTIAL_CONTENT = 206
  244.   RETURN_MULTIPLE_CHOICES = 300
  245.   RETURN_MOVED_PERMANENTLY = 301
  246.   RETURN_MOVED_TEMPORARILY = 302
  247.   RETURN_SEE_OTHER = 303
  248.   RETURN_NOT_MODIFIED = 304
  249.   RETURN_USE_PROXY = 305
  250.   RETURN_TEMPORARY_REDIRECT = 307
  251.   RETURN_BAD_REQUEST = 400
  252.   RETURN_UNAUTHORIZED = 401
  253.   RETURN_PAYMENT_REQUIRED = 402
  254.   RETURN_FORBIDDEN = 403
  255.   RETURN_NOT_FOUND = 404
  256.   RETURN_METHOD_NOT_ALLOWED = 405
  257.   RETURN_NOT_ACCEPTABLE = 406
  258.   RETURN_PROXY_AUTHENTICATION_REQUIRED = 407
  259.   RETURN_REQUEST_TIMEOUT = 408
  260.   RETURN_CONFLICT = 409
  261.   RETURN_GONE = 410
  262.   RETURN_LENGTH_REQUIRED = 411
  263.   RETURN_PRECONDITION_FAILED = 412
  264.   RETURN_REQUEST_ENTITY_TOO_LARGE = 413
  265.   RETURN_REQUEST_URI_TOO_LONG = 414
  266.   RETURN_UNSUPPORTED_MEDIA_TYPE = 415
  267.   RETURN_REQUEST_RANGE_NOT_SATISFIABLE = 416
  268.   RETURN_EXPECTATION_FAILED = 417
  269.   RETURN_INTERNAL_SERVER_ERROR = 500
  270.   RETURN_NOT_IMPLEMENTED = 501
  271.   RETURN_BAD_GATEWAY = 502
  272.   RETURN_SERVICE_UNAVAILABLE = 503
  273.   RETURN_GATEWAY_TIMEOUT = 504
  274.   RETURN_HTTP_VERSION_NOT_SUPPORTED = 505
  275.   RETURN_CODE = {
  276.     RETURN_CONTINUE: 'CONTINUE',
  277.     RETURN_SWITCHING_PROTOCOLS: 'SWITCHING PROTOCOLS',
  278.     RETURN_OK: 'OK',
  279.     RETURN_CREATED: 'CREATED',
  280.     RETURN_ACCEPTED: 'ACCEPTED',
  281.     RETURN_NON_AUTHORITATIVE_INFORMATION: 'NON AUTHORITATIVE INFORMATION',
  282.     RETURN_NO_CONTENT: 'NO CONTENT',
  283.     RETURN_RESET_CONTENT: 'RESET CONTENT',
  284.     RETURN_PARTIAL_CONTENT: 'PARTIAL CONTENT',
  285.     RETURN_MULTIPLE_CHOICES: 'MULTIPLE CHOICES',
  286.     RETURN_MOVED_PERMANENTLY: 'MOVED PERMANENTLY',
  287.     RETURN_MOVED_TEMPORARILY: 'MOVED TEMPORARILY',
  288.     RETURN_SEE_OTHER: 'SEE OTHER',
  289.     RETURN_NOT_MODIFIED: 'NOT MODIFIED',
  290.     RETURN_USE_PROXY: 'USE PROXY',
  291.     RETURN_TEMPORARY_REDIRECT: 'TEMPORARY REDIRECT',
  292.     RETURN_BAD_REQUEST: 'BAD REQUEST',
  293.     RETURN_UNAUTHORIZED: 'UNAUTHORIZED',
  294.     RETURN_PAYMENT_REQUIRED: 'PAYMENT REQUIRED',
  295.     RETURN_FORBIDDEN: 'FORBIDDEN',
  296.     RETURN_NOT_FOUND: 'NOT FOUND',
  297.     RETURN_METHOD_NOT_ALLOWED: 'METHOD NOT ALLOWED',
  298.     RETURN_NOT_ACCEPTABLE: 'NOT ACCEPTABLE',
  299.     RETURN_PROXY_AUTHENTICATION_REQUIRED: 'PROXY AUTHENTICATION REQUIRED',
  300.     RETURN_REQUEST_TIMEOUT: 'REQUEST TIMEOUT',
  301.     RETURN_CONFLICT: 'CONFLICT',
  302.     RETURN_GONE: 'GONE',
  303.     RETURN_LENGTH_REQUIRED: 'LENGTH REQUIRED',
  304.     RETURN_PRECONDITION_FAILED: 'PRECONDITION FAILED',
  305.     RETURN_REQUEST_ENTITY_TOO_LARGE: 'REQUEST ENTITY TOO LARGE',
  306.     RETURN_REQUEST_URI_TOO_LONG: 'REQUEST URI TOO LONG',
  307.     RETURN_UNSUPPORTED_MEDIA_TYPE: 'UNSUPPORTED MEDIA TYPE',
  308.     RETURN_REQUEST_RANGE_NOT_SATISFIABLE: 'REQUEST RANGE NOT SATISFIABLE',
  309.     RETURN_EXPECTATION_FAILED: 'EXPECTATION FAILED',
  310.     RETURN_INTERNAL_SERVER_ERROR: 'INTERNAL SERVER ERROR',
  311.     RETURN_NOT_IMPLEMENTED: 'NOT IMPLEMENTED',
  312.     RETURN_BAD_GATEWAY: 'BAD GATEWAY',
  313.     RETURN_SERVICE_UNAVAILABLE: 'SERVICE UNAVAILABLE',
  314.     RETURN_GATEWAY_TIMEOUT: 'GATEWAY TIMEOUT',
  315.     RETURN_HTTP_VERSION_NOT_SUPPORTED: 'HTTP VERSION NOT SUPPORTED',
  316.   }
  317.   def __init__(self):
  318.     pass
  319.   def write(self, s):
  320.     raise 'not implemented'
  321.   def writeErr(self, s):
  322.     raise 'not implemented'
  323.   def close(self):
  324.     raise 'not implemented'
  325.   def clear(self):
  326.     raise 'not implemented'
  327.   def sendHeaders(self):
  328.     raise 'not implemented'
  329.   def clearHeaders(self):
  330.     raise 'not implemented'
  331.   def setContentType(self, content_type):
  332.     raise 'not implemented'
  333.   def setReturnCode(self, code):
  334.     raise 'not implemented'
  335.   def addHeader(self, type, data, replace=0):
  336.     raise 'not implemented'
  337.   def flush(self):
  338.     raise 'not implemented'
  339.   def unbuffer(self):
  340.     raise 'not implemented'
  341.  
  342. class spyceCode:
  343.   '''Takes care of compiling the Spyce file, and generating a wrapper'''
  344.   def __init__(self, code, filename=None, sig='', limit=3):
  345.     # store variables
  346.     self._filename = filename
  347.     self._limit = limit
  348.     # generate code
  349.     self._code, self._coderefs, self._modrefs = \
  350.       spyceCompile.spyceCompile(code, filename, sig, getServer())
  351.     self._wrapperQueue = []
  352.     self._wrappersMade = 0
  353.   # wrappers
  354.   def newWrapper(self):
  355.     "Get a wrapper for this code from queue, or make new one"
  356.     try: return self._wrapperQueue.pop()
  357.     except IndexError: pass
  358.     self._wrappersMade = self._wrappersMade + 1
  359.     return spyceWrapper(self)
  360.   def returnWrapper(self, w):
  361.     "Return wrapper back to queue after use"
  362.     if len(self._wrapperQueue)<self._limit:
  363.       self._wrapperQueue.append(w)
  364.   # serialization
  365.   def __getstate__(self):
  366.     return self._filename, self._code, self._coderefs, self._modrefs, self._limit
  367.   def __setstate__(self, state):
  368.     self._filename, self._code, self._coderefs, self._modrefs, self._limit = state
  369.     self._wrapperQueue = []
  370.     self._wrappersMade = 0
  371.   # accessors
  372.   def getCode(self):
  373.     "Return processed Spyce (i.e. Python) code"
  374.     return self._code
  375.   def getFilename(self):
  376.     "Return source filename, if it exists"
  377.     return self._filename
  378.   def getCodeRefs(self):
  379.     "Return python-to-Spyce code line references"
  380.     return self._coderefs
  381.   def getModRefs(self):
  382.     "Return list of import references in Spyce code"
  383.     return self._modrefs
  384.  
  385. class spyceWrapper:
  386.   """Wrapper object runs the entire show, bringing together the code, the
  387.   Spyce environment, the request and response objects and the modules. It is
  388.   NOT thread safe - new instances are generated as necessary by spyceCode!
  389.   This object is generated by a spyceCode object. The common Spyce handler
  390.   code calls the 'processing' functions. Module writers interact with this
  391.   object via the spyceModuleAPI calls. This is arguable the trickiest portion
  392.   of the Spyce so don't touch unless you know what you are doing."""
  393.   def __init__(self, spycecode):
  394.     # store variables
  395.     self._spycecode = spycecode
  396.     # api object
  397.     self._api = self
  398.     # module tracking
  399.     self._modCache = {}
  400.     self._modstarted = []
  401.     self._modules = {}
  402.     # compile python code
  403.     self._codeenv = { spyceCompile.SPYCE_WRAPPER: self._api }
  404.     try: exec self._spycecode.getCode() in self._codeenv
  405.     except SyntaxError: raise spyceException.pythonSyntaxError(self)
  406.     self._freshenv = self._codeenv.keys()
  407.     # spyce hooks
  408.     noop = lambda *args: None
  409.     self.process = self._codeenv[spyceCompile.SPYCE_PROCESS_FUNC]
  410.     try: self.init = self._codeenv[spyceCompile.SPYCE_INIT_FUNC]
  411.     except: self.init = noop
  412.     try: self.destroy = self._codeenv[spyceCompile.SPYCE_DESTROY_FUNC]
  413.     except: self.destroy = noop
  414.     # request, response
  415.     self._response = self._request = None
  416.     self._responseCallback = {}
  417.     self._moduleCallback = {}
  418.   def _startModule(self, name, file=None, as=None, force=0):
  419.     "Initialise module for current request."
  420.     if as==None: as=name
  421.     if force or not self._codeenv.has_key(as):
  422.       modclass = getServer().loadModule(name, file, self._spycecode.getFilename())
  423.       mod = modclass(self._api)
  424.       self.setModule(as, mod, 0)
  425.       if DEBUG_ERROR:
  426.         sys.stderr.write(as+'.start\n')
  427.       mod.start()
  428.       self._modstarted.append((as, mod))
  429.     else: mod = self._codeenv[as]
  430.     return mod
  431.   # spyce processing
  432.   def spyceInit(self, request, response):
  433.     "Initialise a Spyce for processing."
  434.     self._request = request
  435.     self._response = response
  436.     for mod in DEFAULT_MODULES:
  437.       self._startModule(mod)
  438.     self._modstarteddefault = self._modstarted
  439.     self._modstarted = []
  440.     for (modname, modfrom, modas) in self.getModRefs():
  441.       self._startModule(modname, modfrom, modas, 1)
  442.     exec '_spyce_start()' in { '_spyce_start': self.init }
  443.   def spyceProcess(self, *args, **kwargs):
  444.     "Process the current Spyce request."
  445.     path = sys.path
  446.     try:
  447.       if self._spycecode.getFilename():
  448.         path = copy.copy(sys.path)
  449.         sys.path.append(os.path.dirname(self._spycecode.getFilename()))
  450.       dict = { '_spyce_process': self.process,
  451.         '_spyce_args': args, '_spyce_kwargs': kwargs, }
  452.       exec '_spyce_result = apply(_spyce_process, _spyce_args, _spyce_kwargs)' in dict
  453.       return dict['_spyce_result']
  454.     finally:
  455.       sys.path = path
  456.   def spyceDestroy(self, theError=None):
  457.     "Cleanup after the request processing."
  458.     try:
  459.       exec '_spyce_finish()' in { '_spyce_finish': self.destroy }
  460.       # explicit modules
  461.       self._modstarted.reverse()
  462.       for as, mod in self._modstarted:
  463.         try: 
  464.           if DEBUG_ERROR:
  465.             sys.stderr.write(as+'.finish\n')
  466.           mod.finish(theError)
  467.         except spyceException.spyceDone: pass
  468.         except spyceException.spyceRedirect, e:
  469.           return spyceFileHandler(self._request, self._response, e.filename)
  470.         except KeyboardInterrupt: raise
  471.         except SystemExit: pass
  472.         except: theError = spyceException.spyceRuntimeException(self._api)
  473.       finishError = None
  474.       # default modules
  475.       self._modstarteddefault.reverse()
  476.       for as, mod in self._modstarteddefault:
  477.         try: 
  478.           if DEBUG_ERROR:
  479.             sys.stderr.write(as+'.finish\n')
  480.           mod.finish(theError)
  481.         except: finishError = 1
  482.       self._request = None
  483.       self._response = None
  484.       if finishError: raise
  485.     finally:
  486.       self.spyceCleanup()
  487.   def spyceCleanup(self):
  488.     "Sweep the Spyce environment."
  489.     self._modstarteddefault = []
  490.     self._modstarted = []
  491.     self._modules = {}
  492.     if self._freshenv!=None:
  493.       for e in self._codeenv.keys():
  494.         if e not in self._freshenv:
  495.           del self._codeenv[e]
  496.   # API methods
  497.   def getFilename(self):
  498.     "Return filename of current Spyce"
  499.     return self._spycecode.getFilename()
  500.   def getCode(self):
  501.     "Return processed Spyce (i.e. Python) code"
  502.     return self._spycecode.getCode()
  503.   def getCodeRefs(self):
  504.     "Return python-to-Spyce code line references"
  505.     return self._spycecode.getCodeRefs()
  506.   def getModRefs(self):
  507.     "Return list of import references in Spyce code"
  508.     return self._spycecode.getModRefs()
  509.   def getServerObject(self):
  510.     "Return unique (per engine instance) server object"
  511.     return getServer().serverobject
  512.   def getServerGlobals(self):
  513.     "Return server configuration globals"
  514.     return getServer().globals
  515.   def getServerID(self):
  516.     "Return unique server identifier"
  517.     return self._request.getServerID()
  518.   def getPageError(self):
  519.     "Return default page error value"
  520.     return getServer().pageerror
  521.   def getRequest(self):
  522.     "Return internal request object"
  523.     return self._request
  524.   def getResponse(self):
  525.     "Return internal response object"
  526.     return self._response
  527.   def setResponse(self, o):
  528.     "Set internal response object"
  529.     self._response = o
  530.     for f in self._responseCallback.keys(): f()
  531.   def registerResponseCallback(self, f):
  532.     "Register a callback for when internal response changes"
  533.     self._responseCallback[f] = 1
  534.   def unregisterResponseCallback(self, f):
  535.     "Unregister a callback for when internal response changes"
  536.     try: del self._responseCallback[f]
  537.     except KeyError: pass
  538.   def getModules(self):
  539.     "Return references to currently loaded modules"
  540.     return self._modules
  541.   def getModule(self, name):
  542.     """Get module reference. The module is dynamically loaded and initialised
  543.     if it does not exist (ie. if it was not explicitly imported, but requested
  544.     by another module during processing)"""
  545.     return self._startModule(name)
  546.   def setModule(self, name, mod, notify=1):
  547.     "Add existing module (by reference) to Spyce namespace (used for includes)"
  548.     self._codeenv[name] = mod
  549.     self._modules[name] = mod
  550.     if notify:
  551.       for f in self._moduleCallback.keys(): 
  552.         f()
  553.   def delModule(self, name, notify=1):
  554.     "Add existing module (by reference) to Spyce namespace (used for includes)"
  555.     del self._codeenv[name]
  556.     del self._modules[name]
  557.     if notify:
  558.       for f in self._moduleCallback.keys(): 
  559.         f()
  560.   def getGlobals(self):
  561.     "Return the Spyce global namespace dictionary"
  562.     return self._codeenv
  563.   def registerModuleCallback(self, f):
  564.     "Register a callback for modules change"
  565.     self._moduleCallback[f] = 1
  566.   def unregisterModuleCallback(self, f):
  567.     "Unregister a callback for modules change"
  568.     try: del self._moduleCallback[f]
  569.     except KeyError: pass
  570.   def spyceFile(self, file):
  571.     "Return a spyceCode object of a file"
  572.     return getServer().spyce_cache[('file', file)]
  573.   def spyceString(self, code):
  574.     "Return a spyceCode object of a string"
  575.     return getServer().spyce_cache[('string', code)]
  576.   def spyceModule(self, name, file=None, rel_file=None):
  577.     "Return Spyce module class"
  578.     return getServer().loadModule(name, file, rel_file)
  579.   def spyceTaglib(self, name, file=None, rel_file=None):
  580.     "Return Spyce taglib class"
  581.     return getServer().loadModule(name, file, rel_file)
  582.   def setStdout(self, out):
  583.     "Set the stdout stream (thread-safe)"
  584.     serverout = getServer().stdout
  585.     if serverout: serverout.setObject(out)
  586.     else: sys.stdout = out
  587.   def getStdout(self):
  588.     "Get the stdout stream (thread-safe)"
  589.     serverout = getServer().stdout
  590.     if serverout: return serverout.getObject()
  591.     else: return sys.stdout
  592.  
  593. ##################################################
  594. # Spyce cache
  595. #
  596.  
  597. def spyceFileCacheValid(key, validity):
  598.   "Determine whether compiled Spyce is valid"
  599.   try: 
  600.     filename, sig = key
  601.   except:
  602.     filename, sig = key, ''
  603.   try:
  604.     if not os.path.exists(filename):
  605.       return 0
  606.     if not os.access(filename, os.R_OK):
  607.       return 0
  608.     return os.path.getmtime(filename) == validity
  609.   except KeyboardInterrupt: raise
  610.   except:
  611.     return 0
  612.  
  613. def spyceFileCacheGenerate(key):
  614.   "Generate new Spyce wrapper (recompiles)."
  615.   try: 
  616.     filename, sig = key
  617.   except:
  618.     filename, sig = key, ''
  619.   # ensure file exists and we have permissions
  620.   if not os.path.exists(filename):
  621.     raise spyceException.spyceNotFound(filename)
  622.   if not os.access(filename, os.R_OK):
  623.     raise spyceException.spyceForbidden(filename)
  624.   # generate
  625.   mtime = os.path.getmtime(filename)
  626.   f = None
  627.   try:
  628.     f = open(filename)
  629.     code = f.read()
  630.   finally:
  631.     if f: f.close()
  632.   s = spyceCode(code, filename=filename, sig=sig)
  633.   return mtime, s
  634.  
  635. def spyceStringCacheValid(code, validity):
  636.   return 1
  637.  
  638. def spyceStringCacheGenerate(key):
  639.   try: 
  640.     code, sig = key
  641.   except:
  642.     code, sig = key, ''
  643.   s = spyceCode(code, sig=sig)
  644.   return None, s
  645.  
  646. def spyceCacheValid((type, key), validity):
  647.   return { 
  648.     'string': spyceStringCacheValid,
  649.     'file': spyceFileCacheValid,
  650.   }[type](key, validity)
  651.  
  652. def spyceCacheGenerate((type, key)):
  653.   return {
  654.     'string': spyceStringCacheGenerate,
  655.     'file': spyceFileCacheGenerate,
  656.   }[type](key)
  657.  
  658.  
  659. ##################################################
  660. # Spyce common entry points
  661. #
  662.  
  663. def spyceFileHandler(request, response, filename, sig='', args=None, kwargs=None, config_file=None):
  664.   return _spyceCommonHandler(request, response, ('file', (filename, sig)), args, kwargs, config_file)
  665.  
  666. def spyceStringHandler(request, response, code, sig='', args=None, kwargs=None, config_file=None):
  667.   return _spyceCommonHandler(request, response, ('string', (code, sig)), args, kwargs, config_file)
  668.  
  669. def _spyceCommonHandler(request, response, spyceInfo, args=None, kwargs=None, config_file=None):
  670.   return getServer(config_file).commonHandler(request, response, spyceInfo, args, kwargs)
  671.  
  672. if __name__ == '__main__':
  673.   execfile('run_spyceCmd.py')
  674.  
  675.