id summary reporter owner description type status priority milestone component version severity resolution keywords cc branch changelog apichanges internalchanges 5586 WSGI application entry point should be cleaned up. Graham.Dumpleton@… Christopher Lenz "One purpose of the WSGI environment passed to an application which supports the WSGI protocol is configuration. How Trac uses the WSGI environment is however quite poorly done, with it not actually being possible to use the WSGI environment to configure Trac. The result is that one is forced to set the configuration information using environment variables in os.environ rather than the more obvious method for WSGI applications of it being passed in the WSGI environment. The result is that for some WSGI web server adapters, one has to use fiddles outside of the normal configuration mechanisms for WSGI application entry points. Further the WSGI application entry point for Trac includes code which is specific to mod_python when it shouldn't do this. The mod_python specific code should be in the mod_python front end and the information passed from the mod_python front end to the WSGI entry point should be the same as for any other WSGI application. This would leave the WSGI application entry point as a pure WSGI application rather than it mixing in mod_python specific code. === Suggested Changes === 1. Move the mod_python specific code from trac.web.main.dispatch_request() into trac.web.modpython_frontend where it belongs. In particular, do not have the mod_python front end pass: * mod_python.options * mod_python.subprocess_env Instead, have the mod_python front end itself extract from those tables supplied by the mod_python request objects the values it needs and pass only: * trac.env_path * trac.env_parent_dir * trac.env_index_template * trac.template_vars * trac.locale This would see the following code eliminated from the WSGI entry point and replaced with equivalent code in the mod_python front end. {{{ if 'mod_python.options' in environ: options = environ['mod_python.options'] environ.setdefault('trac.env_path', options.get('TracEnv')) environ.setdefault('trac.env_parent_dir', options.get('TracEnvParentDir')) environ.setdefault('trac.env_index_template', options.get('TracEnvIndexTemplate')) environ.setdefault('trac.template_vars', options.get('TracTemplateVars')) environ.setdefault('trac.locale', options.get('TracLocale')) }}} Most importantly though, it shouldn't pass through values in the WSGI environment where there was no equivalent entry set in the mod_python options. For example, in the mod_python front end it would use: {{{ if options.has_key('TracEnv'): environ['trac.env_path'] = options['TracEnv'] }}} If the WSGI entry point has to add dummy entries for values which weren't defined then it should be doing it and the mod_python front end shouldn't be required to provide dummy entries. 2. Have the mod_python front end also do the mod_python specific fiddle of SCRIPT_NAME and PATH_INFO required when mod_python configuration is not hosted at the root of the URL namespace. This fiddle is only required because of shortcomings of how the WSGI adapter for mod_python is written, the need to fix up these values should therefore be in the mod_python front end. This would see the following code eliminated from the WSGI entry point and replaced with equivalent code in the mod_python front end. {{{ if 'TracUriRoot' in options: # Special handling of SCRIPT_NAME/PATH_INFO for mod_python, which # tends to get confused for whatever reason root_uri = options['TracUriRoot'].rstrip('/') request_uri = environ['REQUEST_URI'].split('?', 1)[0] if not request_uri.startswith(root_uri): raise ValueError('TracUriRoot set to %s but request URL ' 'is %s' % (root_uri, request_uri)) environ['SCRIPT_NAME'] = root_uri environ['PATH_INFO'] = urllib.unquote(request_uri[len(root_uri):]) }}} 3. Change the WSGI entry point so that if the values: * trac.env_path * trac.env_parent_dir * trac.env_index_template * trac.template_vars exist in the WSGI environment that they should be used in preference to the following environment variables defined in os.environ: * TRAC_ENV * TRAC_ENV_PARENT_DIR * TRAC_ENV_INDEX_TEMPLATE * TRAC_TEMPLATE_VARS Also don't wipe out the value for: * trac.locale This would thus see the code: {{{ environ.setdefault('trac.env_path', os.getenv('TRAC_ENV')) environ.setdefault('trac.env_parent_dir', os.getenv('TRAC_ENV_PARENT_DIR')) environ.setdefault('trac.env_index_template', os.getenv('TRAC_ENV_INDEX_TEMPLATE')) environ.setdefault('trac.template_vars', os.getenv('TRAC_TEMPLATE_VARS')) environ.setdefault('trac.locale', '') }}} needing to be changed. In other words for each entry it should say: {{{ if not environ.has_key('trac.env_path'): environ.setdefault('trac.env_path', os.getenv('TRAC_ENV')) }}} 4. Change how Python egg cache directory is set so that value is read from entry in WSGI environment directly. This would necessitate mod_python front end copying entry if it exists from mod_python subprocess_env table to WSGI environment. This sees the code: {{{ if 'mod_python.subprocess_env' in environ: egg_cache = environ['mod_python.subprocess_env'].get('PYTHON_EGG_CACHE') if egg_cache: os.environ['PYTHON_EGG_CACHE'] = egg_cache }}} changing. === Outcome Of Changes === The result of the changes is that one has a pure WSGI entry point for Trac that can be properly configured through the WSGI environment. The possible configuration values that could be supplied in this way would be: * trac.env_path * trac.env_parent_dir * trac.env_index_template * trac.template_vars * trac.locale * PYTHON_EGG_CACHE The mod_python front end would then be a WSGI adapter for Trac that yields a proper WSGI environment adhering to this interface. This includes SCRIPT_NAME and PATH_INFO having already been fixed up to work around not being able to have a WSGI adapter for mod_python that can work this out itself. For other WSGI hosting solutions such as mod_wsgi, its standard mechanism for configuring WSGI applications could then be used, namely whereby any Apache variables set using SetEnv will be passed through to the WSGI application in exactly the same way that such variables would be automatically passed through to CGI scripts. For example: {{{ SetEnv trac.env_path /usr/local/trac/mysite SetEnv PYTHON_EGG_CACHE /usr/local/trac/mysite/eggs }}} This would replace the current need to step outside of the normal configuration mechanisms for Apache modules to configure Trac by having to explicitly set variables in os.environ in the WSGI application script file itself. {{{ import os os.environ['TRAC_ENV'] = '/usr/local/trac/mysite' os.environ['PYTHON_EGG_CACHE'] = '/usr/local/trac/mysite/eggs' import trac.web.main application = trac.web.main.dispatch_request }}} Why this is beneficial for Apache at least is that one can then use a single WSGI application script for Trac and for the configuration to be dynamically generated by Apache configuration using rewrite rules, thereby making it somewhat easier to manage a mass hosting like situation for distinct Trac instances. The only way around this at present for mod_wsgi is to create a WSGI middleware component that wraps the Trac WSGI entry point which fakes up the mod_python.options and mod_python.subprocess_env entries in the WSGI environment so that Trac thinks it is actually mod_python and will use those dynamic entries. Making the Trac WSGI entry point a better behaved WSGI application, by cleaning up the WSGI entry point so that isn't also doing mod_python stuff and can accept configuration through the WSGI environment may also be of benefit to other WSGI deployment solutions such as provided by Paste. " enhancement closed low 0.11 general 0.10 minor fixed wsgi