/lib/galaxy/webapps/community/buildapp.py

https://bitbucket.org/cistrome/cistrome-harvard/ · Python · 200 lines · 145 code · 11 blank · 44 comment · 26 complexity · 13880a16e1a90771fb2e772cfca652d7 MD5 · raw file

  1. """
  2. Provides factory methods to assemble the Galaxy web application
  3. """
  4. import logging, atexit, os, os.path, sys, config
  5. from inspect import isclass
  6. from paste.request import parse_formvars
  7. from paste.util import import_string
  8. from paste import httpexceptions
  9. from paste.deploy.converters import asbool
  10. import pkg_resources
  11. import galaxy.webapps.community.model
  12. import galaxy.webapps.community.model.mapping
  13. import galaxy.web.framework
  14. from galaxy.webapps.community.framework.middleware import hg
  15. log = logging.getLogger( __name__ )
  16. def add_ui_controllers( webapp, app ):
  17. """
  18. Search for controllers in the 'galaxy.webapps.controllers' module and add
  19. them to the webapp.
  20. """
  21. from galaxy.web.base.controller import BaseUIController
  22. from galaxy.web.base.controller import ControllerUnavailable
  23. import galaxy.webapps.community.controllers
  24. controller_dir = galaxy.webapps.community.controllers.__path__[0]
  25. for fname in os.listdir( controller_dir ):
  26. if not fname.startswith( "_" ) and fname.endswith( ".py" ):
  27. name = fname[:-3]
  28. module_name = "galaxy.webapps.community.controllers." + name
  29. module = __import__( module_name )
  30. for comp in module_name.split( "." )[1:]:
  31. module = getattr( module, comp )
  32. # Look for a controller inside the modules
  33. for key in dir( module ):
  34. T = getattr( module, key )
  35. if isclass( T ) and T is not BaseUIController and issubclass( T, BaseUIController ):
  36. webapp.add_ui_controller( name, T( app ) )
  37. import galaxy.web.controllers
  38. controller_dir = galaxy.web.controllers.__path__[0]
  39. for fname in os.listdir( controller_dir ):
  40. # TODO: fix this if we decide to use, we don't need to inspect all controllers...
  41. if fname.startswith( 'user' ) and fname.endswith( ".py" ):
  42. name = fname[:-3]
  43. module_name = "galaxy.web.controllers." + name
  44. module = __import__( module_name )
  45. for comp in module_name.split( "." )[1:]:
  46. module = getattr( module, comp )
  47. # Look for a controller inside the modules
  48. for key in dir( module ):
  49. T = getattr( module, key )
  50. if isclass( T ) and T is not BaseUIController and issubclass( T, BaseUIController ):
  51. webapp.add_ui_controller( name, T( app ) )
  52. def app_factory( global_conf, **kwargs ):
  53. """Return a wsgi application serving the root object"""
  54. # Create the Galaxy tool shed application unless passed in
  55. if 'app' in kwargs:
  56. app = kwargs.pop( 'app' )
  57. else:
  58. try:
  59. from galaxy.webapps.community.app import UniverseApplication
  60. app = UniverseApplication( global_conf=global_conf, **kwargs )
  61. except:
  62. import traceback, sys
  63. traceback.print_exc()
  64. sys.exit( 1 )
  65. atexit.register( app.shutdown )
  66. # Create the universe WSGI application
  67. webapp = galaxy.web.framework.WebApplication( app, session_cookie='galaxycommunitysession' )
  68. add_ui_controllers( webapp, app )
  69. webapp.add_route( '/:controller/:action', action='index' )
  70. webapp.add_route( '/:action', controller='repository', action='index' )
  71. webapp.add_route( '/repos/*path_info', controller='hg', action='handle_request', path_info='/' )
  72. webapp.finalize_config()
  73. # Wrap the webapp in some useful middleware
  74. if kwargs.get( 'middleware', True ):
  75. webapp = wrap_in_middleware( webapp, global_conf, **kwargs )
  76. if kwargs.get( 'static_enabled', True ):
  77. webapp = wrap_in_static( webapp, global_conf, **kwargs )
  78. # Close any pooled database connections before forking
  79. try:
  80. galaxy.webapps.community.model.mapping.metadata.engine.connection_provider._pool.dispose()
  81. except:
  82. pass
  83. # Return
  84. return webapp
  85. def wrap_in_middleware( app, global_conf, **local_conf ):
  86. """Based on the configuration wrap `app` in a set of common and useful middleware."""
  87. # Merge the global and local configurations
  88. conf = global_conf.copy()
  89. conf.update( local_conf )
  90. debug = asbool( conf.get( 'debug', False ) )
  91. # First put into place httpexceptions, which must be most closely
  92. # wrapped around the application (it can interact poorly with
  93. # other middleware):
  94. app = httpexceptions.make_middleware( app, conf )
  95. log.debug( "Enabling 'httpexceptions' middleware" )
  96. # The recursive middleware allows for including requests in other
  97. # requests or forwarding of requests, all on the server side.
  98. if asbool(conf.get('use_recursive', True)):
  99. from paste import recursive
  100. app = recursive.RecursiveMiddleware( app, conf )
  101. log.debug( "Enabling 'recursive' middleware" )
  102. # Various debug middleware that can only be turned on if the debug
  103. # flag is set, either because they are insecure or greatly hurt
  104. # performance
  105. if debug:
  106. # Middleware to check for WSGI compliance
  107. if asbool( conf.get( 'use_lint', True ) ):
  108. from paste import lint
  109. app = lint.make_middleware( app, conf )
  110. log.debug( "Enabling 'lint' middleware" )
  111. # Middleware to run the python profiler on each request
  112. if asbool( conf.get( 'use_profile', False ) ):
  113. import profile
  114. app = profile.ProfileMiddleware( app, conf )
  115. log.debug( "Enabling 'profile' middleware" )
  116. # Middleware that intercepts print statements and shows them on the
  117. # returned page
  118. if asbool( conf.get( 'use_printdebug', True ) ):
  119. from paste.debug import prints
  120. app = prints.PrintDebugMiddleware( app, conf )
  121. log.debug( "Enabling 'print debug' middleware" )
  122. if debug and asbool( conf.get( 'use_interactive', False ) ):
  123. # Interactive exception debugging, scary dangerous if publicly
  124. # accessible, if not enabled we'll use the regular error printing
  125. # middleware.
  126. pkg_resources.require( "WebError" )
  127. from weberror import evalexception
  128. app = evalexception.EvalException( app, conf,
  129. templating_formatters=build_template_error_formatters() )
  130. log.debug( "Enabling 'eval exceptions' middleware" )
  131. else:
  132. # Not in interactive debug mode, just use the regular error middleware
  133. from paste.exceptions import errormiddleware
  134. app = errormiddleware.ErrorMiddleware( app, conf )
  135. log.debug( "Enabling 'error' middleware" )
  136. # Transaction logging (apache access.log style)
  137. if asbool( conf.get( 'use_translogger', True ) ):
  138. from paste.translogger import TransLogger
  139. app = TransLogger( app )
  140. log.debug( "Enabling 'trans logger' middleware" )
  141. # Config middleware just stores the paste config along with the request,
  142. # not sure we need this but useful
  143. from paste.deploy.config import ConfigMiddleware
  144. app = ConfigMiddleware( app, conf )
  145. log.debug( "Enabling 'config' middleware" )
  146. # X-Forwarded-Host handling
  147. from galaxy.web.framework.middleware.xforwardedhost import XForwardedHostMiddleware
  148. app = XForwardedHostMiddleware( app )
  149. log.debug( "Enabling 'x-forwarded-host' middleware" )
  150. app = hg.Hg( app, conf )
  151. log.debug( "Enabling hg middleware" )
  152. return app
  153. def wrap_in_static( app, global_conf, **local_conf ):
  154. from paste.urlmap import URLMap
  155. from galaxy.web.framework.middleware.static import CacheableStaticURLParser as Static
  156. urlmap = URLMap()
  157. # Merge the global and local configurations
  158. conf = global_conf.copy()
  159. conf.update(local_conf)
  160. # Get cache time in seconds
  161. cache_time = conf.get( "static_cache_time", None )
  162. if cache_time is not None:
  163. cache_time = int( cache_time )
  164. # Send to dynamic app by default
  165. urlmap["/"] = app
  166. # Define static mappings from config
  167. urlmap["/static"] = Static( conf.get( "static_dir" ), cache_time )
  168. urlmap["/images"] = Static( conf.get( "static_images_dir" ), cache_time )
  169. urlmap["/static/scripts"] = Static( conf.get( "static_scripts_dir" ), cache_time )
  170. urlmap["/static/style"] = Static( conf.get( "static_style_dir" ), cache_time )
  171. urlmap["/favicon.ico"] = Static( conf.get( "static_favicon_dir" ), cache_time )
  172. # URL mapper becomes the root webapp
  173. return urlmap
  174. def build_template_error_formatters():
  175. """
  176. Build a list of template error formatters for WebError. When an error
  177. occurs, WebError pass the exception to each function in this list until
  178. one returns a value, which will be displayed on the error page.
  179. """
  180. formatters = []
  181. # Formatter for mako
  182. import mako.exceptions
  183. def mako_html_data( exc_value ):
  184. if isinstance( exc_value, ( mako.exceptions.CompileException, mako.exceptions.SyntaxException ) ):
  185. return mako.exceptions.html_error_template().render( full=False, css=False )
  186. if isinstance( exc_value, AttributeError ) and exc_value.args[0].startswith( "'Undefined' object has no attribute" ):
  187. return mako.exceptions.html_error_template().render( full=False, css=False )
  188. formatters.append( mako_html_data )
  189. return formatters