#!/usr/bin/env python
##################################################
# SPYCE - Python-based HTML Scripting
# Copyright (c) 2002 Rimon Barr.
#
# Refer to spyce.py
# CVS: $Id: spyceCmd.py 1159 2006-08-16 23:33:19Z ellisj $
##################################################
import imp, getopt, sys, traceback, os, os.path, string, glob, copy
import spyce, spyceException, spyceUtil, spyceCompile, spycePreload
__doc__ = '''Command-line and CGI-based Spyce entry point.'''
##################################################
# Output
#
# output version
def showVersion(out=sys.stdout):
"Emit version information."
out.write('Spyce %s\n' % spyce.__version__)
# output syntax
def showUsage(out=sys.stdout):
"Emit command-line usage information."
showVersion(out)
out.write('Command-line usage:\n')
out.write(' spyce -c [-o filename.html] <filename.spy>\n')
out.write(' spyce -w <filename.spy> <-- CGI\n')
out.write(' spyce -O filename(s).spy <-- batch process\n')
out.write(' spyce -l [-d file ] <-- proxy server\n')
out.write(' spyce -h | -v\n')
out.write(' -h, -?, --help display this help information\n')
out.write(' -v, --version display version\n')
out.write(' -o, --output send output to given file\n')
out.write(' -O send outputs of multiple files to *.html\n')
out.write(' -c, --compile compile only; do not execute\n')
out.write(' -w, --web cgi mode: emit headers (or use run_spyceCGI.py)\n')
out.write(' -q, --query set QUERY_STRING environment variable\n')
out.write(' -l, --listen run in HTTP server mode\n')
out.write(' -d, --daemon run as a daemon process with given pidfile\n')
out.write(' --conf [file] Spyce configuration file\n')
out.write('To configure Apache, please refer to: spyceApache.conf\n')
out.write('For more details, refer to the documentation.\n')
out.write(' http://spyce.sourceforge.net\n')
out.write('Send comments, suggestions and bug reports to <rimon-AT-acm.org>.\n')
##################################################
# Request / Response handlers
#
class spyceCmdlineRequest(spyce.spyceRequest):
'CGI/Command-line Spyce request object. (see spyce.spyceRequest)'
def __init__(self, input, env, filename):
spyce.spyceRequest.__init__(self)
self._in = input
self._env = dict(env)
if not self._env.has_key('SERVER_SOFTWARE'):
self._env['SERVER_SOFTWARE'] = 'spyce %s Command-line' % spyce.__version__
if not self._env.has_key('REQUEST_URI'):
self._env['REQUEST_URI']=filename
if not self._env.has_key('REQUEST_METHOD'):
self._env['REQUEST_METHOD']='spyce'
if not self._env.has_key('QUERY_STRING'):
self._env['QUERY_STRING']=''
self._headers = {
'Content-Length': self.env('CONTENT_LENGTH'),
'Content-Type': self.env('CONTENT_TYPE'),
'User-Agent': self.env('HTTP_USER_AGENT'),
'Accept': self.env('HTTP_ACCEPT'),
'Accept-Encoding': self.env('HTTP_ACCEPT_ENCODING'),
'Accept-Language': self.env('HTTP_ACCEPT_LANGUAGE'),
'Accept-Charset': self.env('HTTP_ACCEPT_CHARSET'),
'Cookie': self.env('HTTP_COOKIE'),
'Referer': self.env('HTTP_REFERER'),
'Host': self.env('HTTP_HOST'),
'Connection': self.env('HTTP_CONNECTION'),
'Keep-Alive': self.env('HTTP_KEEP_ALIVE'),
}
def env(self, name=None):
return spyceUtil.extractValue(self._env, name)
def getHeader(self, type=None):
return spyceUtil.extractValue(self._headers, type)
def getServerID(self):
return os.getpid()
class spyceCmdlineResponse(spyce.spyceResponse):
'CGI/Command-line Spyce response object. (see spyce.spyceResponse)'
def __init__(self, out, err, cgimode=0):
spyce.spyceResponse.__init__(self)
if not cgimode:
self.RETURN_OK = 0
self.RETURN_CODE[self.RETURN_OK] = 'OK'
self.origout = out
self.out, self.err = spyceUtil.BufferedOutput(out), err
self.cgimode = cgimode
self.headers, self.headersSent = [], 0
self.CT = None
self.returncode = self.RETURN_OK
# functions (for performance)
self.write = self.out.write
self.writeErr = self.err.write
self.clear = self.out.clear
def close(self):
self.flush()
self.out.close()
def sendHeaders(self):
if self.cgimode and not self.headersSent:
resultText = self.RETURN_CODE.get(self.returncode)
self.origout.write('Status: %3d "%s"\n' % (self.returncode, resultText))
if not self.CT: self.setContentType('text/html')
self.origout.write('Content-Type: %s\n' % self.CT)
for h in self.headers: self.origout.write('%s: %s\n'%h)
self.origout.write('\n')
self.headersSent = 1
def clearHeaders(self):
if self.headersSent: raise Exception, 'headers already sent'
self.headers = []
def setContentType(self, content_type):
if self.headersSent: raise Exception, 'headers already sent'
self.CT = content_type
def setReturnCode(self, code):
if self.headersSent: raise Exception, 'headers already sent'
self.returncode = code
def addHeader(self, type, data, replace=0):
if self.headersSent: raise Exception, 'headers already sent'
if type=='Content-Type': self.setContentType(data)
else:
if replace:
self.headers = filter(lambda (type, _), t2=type: type!=t2, self.headers)
self.headers.append((type, data))
def flush(self, stopFlag=0):
if stopFlag: return
self.sendHeaders()
self.out.flush()
def unbuffer(self):
self.sendHeaders()
self.out.unbuffer()
##################################################
# Daemonizing
#
def daemonize(stdin='/dev/null', stdout='/dev/null', stderr='/dev/null', pidfile=None):
'''Forks current process into a daemon. stdin, stdout, and stderr arguments
are file names that are opened and used in place of the standard file descriptors
in sys.stdin, sys.stdout, and sys.stderr, which default to /dev/null.
Note that stderr is unbuffered, so output may interleave with unexpected order
if shares destination with stdout.'''
def forkToChild():
try:
if os.fork()>0: sys.exit(0) # exit parent.
except OSError, e:
sys.stderr.write("fork failed: (%d) %s\n" % (e.errno, e.strerror))
sys.exit(1)
# First fork; decouple
forkToChild()
os.chdir("/")
os.umask(0)
os.setsid()
# Second fork; create pidfile; redirect descriptors
forkToChild()
pid = str(os.getpid())
if pidfile:
f = open(pidfile,'w+')
f.write("%s\n" % pid)
f.close()
si = open(stdin, 'r')
so = open(stdout, 'a+')
se = open(stderr, 'a+', 0)
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
# I am a daemon!
##################################################
# Command-line entry point
#
#for debugging/profiling only
#sys.stdout = spyceUtil.NoCloseOut(sys.stdout)
def spyceMain(cgimode=0, cgiscript=None,
stdout=sys.stdout, stdin=sys.stdin, stderr=sys.stderr, environ=os.environ):
"Command-line and CGI entry point."
# defaults
compileonlyMode = 0
outputFilename = None
defaultOutputFilename = 0
httpmode = 0
daemon = None
configFile = None
# parse options
if cgimode and cgiscript: args = [cgiscript]
else:
try:
opts, args = getopt.getopt(sys.argv[1:], 'h?vco:Owq:ld:p:',
['help', 'version', 'compile', 'output=', 'web', 'query=', 'listen', 'daemon=', 'conf=',])
except getopt.error:
if cgimode: stdout.write('Content-Type: text/plain\n\n')
stdout.write('syntax: unknown switch used\n')
stdout.write('Use -h option for help.\n')
return -1
for o, a in opts:
if o in ("-v", "--version"): showVersion(); return
if o in ("-h", "--help", "-?"): showUsage(); return
if o in ("-c", "--compileonly"): compileonlyMode = 1
if o in ("-o", "--output"): outputFilename = a
if o in ("-O", ): defaultOutputFilename = 1
if o in ("-w", "--web"): cgimode = 1
if o in ("-q", "--query"): environ['QUERY_STRING'] = a
if o in ("-l", "--listen"): httpmode = 1
if o in ("-d", "--daemon"): daemon = a
if o in ("--conf", ): configFile = a
if compileonlyMode and httpmode:
stdout.write('compile mode (-c) is incompatible with http listen mode (-l)\n')
return -1
if not configFile: configFile = spycePreload.defaultConfigFile()
# web server mode
if httpmode:
import spyceWWW
return spyceWWW.spyceHTTPserver(configFile, daemon=daemon)
configmod = spycePreload.getConfigModule(configFile)
# some checks
if not cgimode and not defaultOutputFilename and len(args)>1:
stdout.write('syntax: too many files to process\n')
stdout.write('Use -h option for help.\n')
return -1
# file globbing
if defaultOutputFilename:
globbed = map(glob.glob, args)
args = []
for g in globbed:
for f in g: args.append(f)
if not len(args):
if cgimode: stdout.write('Content-Type: text/plain\n\n')
stdout.write('syntax: please specify a spyce file to process\n')
stdout.write('Use -h option for help.\n')
return -1
# run spyce
result=0
try:
while len(args):
result, script = 0, args[0]
del args[0]
if cgimode:
dir = os.path.dirname(script)
if dir:
script = os.path.basename(script)
os.chdir(dir)
try:
output = stdout
if defaultOutputFilename:
outputFilename = os.path.splitext(script)[0]+'.html'
stdout.write('Processing: %s\n'%script)
stdout.flush()
if outputFilename:
output = open(outputFilename, 'w')
server = spyce.getServer(configmod)
if compileonlyMode:
f = file(script)
if spyceUtil.isTagCollection(f):
code, coderefs, modrefs = spyceCompile.spyceCompile(f.read(), script, '', server, True)
output.write(code)
else:
s = server.spyce_cache['file', script]
output.write(s.getCode())
f.close()
output.write('\n')
else:
request = spyceCmdlineRequest(stdin, environ, script)
response = spyceCmdlineResponse(output, stderr, cgimode)
result = spyce.spyceFileHandler(request, response, script, config=configmod)
response.close()
except (SystemExit, KeyboardInterrupt): raise
except (spyceException.spyceForbidden, spyceException.spyceNotFound), e:
if cgimode:
stdout.write('Content-Type: text/plain\n\n')
stdout.write(str(e)+'\n')
except:
if cgimode:
stdout.write('Content-Type: text/plain\n\n')
stdout.write(spyceUtil.exceptionString()+'\n')
if output:
output.close()
except KeyboardInterrupt: stdout.write('Break!\n')
return result
ABSPATH = 1
if ABSPATH:
for i in range(len(sys.path)):
sys.path[i] = os.path.abspath(sys.path[i])
ABSPATH = 0
# Command-line entry point
if __name__=='__main__':
os.environ[spyce.SPYCE_ENTRY] = 'cmd'
try: sys.exit(spyceMain(cgimode=0))
except KeyboardInterrupt: print 'Break!'
except SystemExit: pass
except:
traceback.print_exc()
sys.exit(1)
|