home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / spyce / *.* / spyce.py < prev    next >
Encoding:
Python Source  |  2004-05-18  |  22.3 KB  |  629 lines

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