pylons.util
Covered: 106 lines
Missed: 60 lines
Skipped 43 lines
Percent: 63 %
  1
"""Paste Template and Pylons utility functions
  3
PylonsTemplate is a Paste Template sub-class that configures the source
  4
directory and default plug-ins for a new Pylons project. The minimal
  5
template a more minimal template with less additional directories and
  6
layout.
  8
"""
  9
import logging
 10
import sys
 12
import pkg_resources
 13
from paste.deploy.converters import asbool
 14
from paste.script.appinstall import Installer
 15
from paste.script.templates import Template, var
 16
from tempita import paste_script_template_renderer
 18
import pylons
 19
import pylons.configuration
 20
import pylons.i18n
 22
__all__ = ['AttribSafeContextObj', 'ContextObj', 'PylonsContext',
 23
           'class_name_from_module_name', 'call_wsgi_application']
 25
log = logging.getLogger(__name__)
 27
def call_wsgi_application(application, environ, catch_exc_info=False):
 28
    """
 29
    Call the given WSGI application, returning ``(status_string,
 30
    headerlist, app_iter)``
 32
    Be sure to call ``app_iter.close()`` if it's there.
 34
    If catch_exc_info is true, then returns ``(status_string,
 35
    headerlist, app_iter, exc_info)``, where the fourth item may
 36
    be None, but won't be if there was an exception.  If you don't
 37
    do this and there was an exception, the exception will be
 38
    raised directly.
 40
    """
 41
    captured = []
 42
    output = []
 43
    def start_response(status, headers, exc_info=None):
 44
        if exc_info is not None and not catch_exc_info:
 45
            raise exc_info[0], exc_info[1], exc_info[2]
 46
        captured[:] = [status, headers, exc_info]
 47
        return output.append
 48
    app_iter = application(environ, start_response)
 49
    if not captured or output:
 50
        try:
 51
            output.extend(app_iter)
 52
        finally:
 53
            if hasattr(app_iter, 'close'):
 54
                app_iter.close()
 55
        app_iter = output
 56
    if catch_exc_info:
 57
        return (captured[0], captured[1], app_iter, captured[2])
 58
    else:
 59
        return (captured[0], captured[1], app_iter)
 62
def class_name_from_module_name(module_name):
 63
    """Takes a module name and returns the name of the class it
 64
    defines.
 66
    If the module name contains dashes, they are replaced with
 67
    underscores.
 69
    Example::
 71
        >>> class_name_from_module_name('with-dashes')
 72
        'WithDashes'
 73
        >>> class_name_from_module_name('with_underscores')
 74
        'WithUnderscores'
 75
        >>> class_name_from_module_name('oneword')
 76
        'Oneword'
 78
    """
 79
    words = module_name.replace('-', '_').split('_')
 80
    return ''.join(w.title() for w in words)
 83
class PylonsContext(object):
 84
    """Pylons context object
 86
    All the Pylons Stacked Object Proxies are also stored here, for use
 87
    in generators and async based operation where the globals can't be
 88
    used.
 90
    This object is attached in
 91
    :class:`~pylons.controllers.core.WSGIController` instances as
 92
    :attr:`~WSGIController._py_object`. For example::
 94
        class MyController(WSGIController):
 95
            def index(self):
 96
                pyobj = self._py_object
 97
                return "Environ is %s" % pyobj.request.environ
 99
    """
100
    pass
103
class ContextObj(object):
104
    """The :term:`tmpl_context` object, with strict attribute access
105
    (raises an Exception when the attribute does not exist)"""
106
    def __repr__(self):
107
        attrs = sorted((name, value)
108
                       for name, value in self.__dict__.iteritems()
109
                       if not name.startswith('_'))
110
        parts = []
111
        for name, value in attrs:
112
            value_repr = repr(value)
113
            if len(value_repr) > 70:
114
                value_repr = value_repr[:60] + '...' + value_repr[-5:]
115
            parts.append(' %s=%s' % (name, value_repr))
116
        return '<%s.%s at %s%s>' % (
117
            self.__class__.__module__,
118
            self.__class__.__name__,
119
            hex(id(self)),
120
            ','.join(parts))
123
class AttribSafeContextObj(ContextObj):
124
    """The :term:`tmpl_context` object, with lax attribute access (
125
    returns '' when the attribute does not exist)"""
126
    def __getattr__(self, name):
127
        try:
128
            return object.__getattribute__(self, name)
129
        except AttributeError:
130
            log.debug("No attribute called %s found on c object, returning "
131
                      "empty string", name)
132
            return ''
135
class PylonsTemplate(Template):
136
    _template_dir = ('pylons', 'templates/default_project')
137
    template_renderer = staticmethod(paste_script_template_renderer)
138
    summary = 'Pylons application template'
139
    egg_plugins = ['PasteScript', 'Pylons']
140
    vars = [
141
        var('template_engine', 'mako/genshi/jinja2/etc: Template language', 
142
            default='mako'),
143
        var('sqlalchemy', 'True/False: Include SQLAlchemy 0.5 configuration',
144
            default=False),
145
    ]
146
    ensure_names = ['description', 'author', 'author_email', 'url']
148
    def pre(self, command, output_dir, vars):
149
        """Called before template is applied."""
150
        package_logger = vars['package']
151
        if package_logger == 'root':
153
            package_logger = 'app'
154
        vars['package_logger'] = package_logger
156
        template_engine = \
157
            vars.setdefault('template_engine',
158
                            pylons.configuration.default_template_engine)
160
        if template_engine == 'mako':
162
            vars['babel_templates_extractor'] = \
163
                ("('templates/**.mako', 'mako', {'input_encoding': 'utf-8'})"
164
                 ",\n%s#%s" % (' ' * 4, ' ' * 8))
165
        else:
166
            vars['babel_templates_extractor'] = ''
169
        for name in self.ensure_names:
170
            vars.setdefault(name, '')
172
        vars['version'] = vars.get('version', '0.1')
173
        vars['zip_safe'] = asbool(vars.get('zip_safe', 'false'))
174
        vars['sqlalchemy'] = asbool(vars.get('sqlalchemy', 'false'))
177
class MinimalPylonsTemplate(PylonsTemplate):
178
    _template_dir = ('pylons', 'templates/minimal_project')
179
    summary = 'Pylons minimal application template'
180
    vars = [
181
        var('template_engine', 'mako/genshi/jinja2/etc: Template language', 
182
            default='mako'),
183
    ]
186
class PylonsInstaller(Installer):
187
    use_cheetah = False
188
    config_file = 'config/deployment.ini_tmpl'
190
    def config_content(self, command, vars):
191
        """
192
        Called by ``self.write_config``, this returns the text content
193
        for the config file, given the provided variables.
194
        """
195
        modules = [line.strip()
196
                    for line in self.dist.get_metadata_lines('top_level.txt')
197
                    if line.strip() and not line.strip().startswith('#')]
198
        if not modules:
199
            print >> sys.stderr, 'No modules are listed in top_level.txt'
200
            print >> sys.stderr, \
201
                'Try running python setup.py egg_info to regenerate that file'
202
        for module in modules:
203
            if pkg_resources.resource_exists(module, self.config_file):
204
                return self.template_renderer(
205
                    pkg_resources.resource_string(module, self.config_file),
206
                    vars, filename=self.config_file)
208
        return super(PylonsInstaller, self).config_content(command, vars)