You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

script.py 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. # -*- test-case-name: twisted.web.test.test_script -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. I contain PythonScript, which is a very simple python script resource.
  6. """
  7. from __future__ import division, absolute_import
  8. import os, traceback
  9. from twisted import copyright
  10. from twisted.python.filepath import _coerceToFilesystemEncoding
  11. from twisted.python.compat import execfile, networkString, NativeStringIO, _PY3
  12. from twisted.web import http, server, static, resource, util
  13. rpyNoResource = """<p>You forgot to assign to the variable "resource" in your script. For example:</p>
  14. <pre>
  15. # MyCoolWebApp.rpy
  16. import mygreatresource
  17. resource = mygreatresource.MyGreatResource()
  18. </pre>
  19. """
  20. class AlreadyCached(Exception):
  21. """
  22. This exception is raised when a path has already been cached.
  23. """
  24. class CacheScanner:
  25. def __init__(self, path, registry):
  26. self.path = path
  27. self.registry = registry
  28. self.doCache = 0
  29. def cache(self):
  30. c = self.registry.getCachedPath(self.path)
  31. if c is not None:
  32. raise AlreadyCached(c)
  33. self.recache()
  34. def recache(self):
  35. self.doCache = 1
  36. noRsrc = resource.ErrorPage(500, "Whoops! Internal Error", rpyNoResource)
  37. def ResourceScript(path, registry):
  38. """
  39. I am a normal py file which must define a 'resource' global, which should
  40. be an instance of (a subclass of) web.resource.Resource; it will be
  41. renderred.
  42. """
  43. cs = CacheScanner(path, registry)
  44. glob = {'__file__': _coerceToFilesystemEncoding("", path),
  45. 'resource': noRsrc,
  46. 'registry': registry,
  47. 'cache': cs.cache,
  48. 'recache': cs.recache}
  49. try:
  50. execfile(path, glob, glob)
  51. except AlreadyCached as ac:
  52. return ac.args[0]
  53. rsrc = glob['resource']
  54. if cs.doCache and rsrc is not noRsrc:
  55. registry.cachePath(path, rsrc)
  56. return rsrc
  57. def ResourceTemplate(path, registry):
  58. from quixote import ptl_compile
  59. glob = {'__file__': _coerceToFilesystemEncoding("", path),
  60. 'resource': resource.ErrorPage(500, "Whoops! Internal Error",
  61. rpyNoResource),
  62. 'registry': registry}
  63. with open(path) as f: # Not closed by quixote as of 2.9.1
  64. e = ptl_compile.compile_template(f, path)
  65. code = compile(e, "<source>", "exec")
  66. eval(code, glob, glob)
  67. return glob['resource']
  68. class ResourceScriptWrapper(resource.Resource):
  69. def __init__(self, path, registry=None):
  70. resource.Resource.__init__(self)
  71. self.path = path
  72. self.registry = registry or static.Registry()
  73. def render(self, request):
  74. res = ResourceScript(self.path, self.registry)
  75. return res.render(request)
  76. def getChildWithDefault(self, path, request):
  77. res = ResourceScript(self.path, self.registry)
  78. return res.getChildWithDefault(path, request)
  79. class ResourceScriptDirectory(resource.Resource):
  80. """
  81. L{ResourceScriptDirectory} is a resource which serves scripts from a
  82. filesystem directory. File children of a L{ResourceScriptDirectory} will
  83. be served using L{ResourceScript}. Directory children will be served using
  84. another L{ResourceScriptDirectory}.
  85. @ivar path: A C{str} giving the filesystem path in which children will be
  86. looked up.
  87. @ivar registry: A L{static.Registry} instance which will be used to decide
  88. how to interpret scripts found as children of this resource.
  89. """
  90. def __init__(self, pathname, registry=None):
  91. resource.Resource.__init__(self)
  92. self.path = pathname
  93. self.registry = registry or static.Registry()
  94. def getChild(self, path, request):
  95. fn = os.path.join(self.path, path)
  96. if os.path.isdir(fn):
  97. return ResourceScriptDirectory(fn, self.registry)
  98. if os.path.exists(fn):
  99. return ResourceScript(fn, self.registry)
  100. return resource.NoResource()
  101. def render(self, request):
  102. return resource.NoResource().render(request)
  103. class PythonScript(resource.Resource):
  104. """
  105. I am an extremely simple dynamic resource; an embedded python script.
  106. This will execute a file (usually of the extension '.epy') as Python code,
  107. internal to the webserver.
  108. """
  109. isLeaf = True
  110. def __init__(self, filename, registry):
  111. """
  112. Initialize me with a script name.
  113. """
  114. self.filename = filename
  115. self.registry = registry
  116. def render(self, request):
  117. """
  118. Render me to a web client.
  119. Load my file, execute it in a special namespace (with 'request' and
  120. '__file__' global vars) and finish the request. Output to the web-page
  121. will NOT be handled with print - standard output goes to the log - but
  122. with request.write.
  123. """
  124. request.setHeader(b"x-powered-by", networkString("Twisted/%s" % copyright.version))
  125. namespace = {'request': request,
  126. '__file__': _coerceToFilesystemEncoding("", self.filename),
  127. 'registry': self.registry}
  128. try:
  129. execfile(self.filename, namespace, namespace)
  130. except IOError as e:
  131. if e.errno == 2: #file not found
  132. request.setResponseCode(http.NOT_FOUND)
  133. request.write(resource.NoResource("File not found.").render(request))
  134. except:
  135. io = NativeStringIO()
  136. traceback.print_exc(file=io)
  137. output = util._PRE(io.getvalue())
  138. if _PY3:
  139. output = output.encode("utf8")
  140. request.write(output)
  141. request.finish()
  142. return server.NOT_DONE_YET