pylons.util
Covered: 106 lines
Missed: 60 lines
Skipped 43 lines
Percent: 63 %

      1

    """Paste Template and Pylons utility functions

      2

      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.

      7

      8

    """

      9

    import logging

     10

    import sys

     11

     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

     17

     18

    import pylons

     19

    import pylons.configuration

     20

    import pylons.i18n

     21

     22

    __all__ = ['AttribSafeContextObj', 'ContextObj', 'PylonsContext',

     23

               'class_name_from_module_name', 'call_wsgi_application']

     24

     25

    log = logging.getLogger(__name__)

     26

     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)``

     31

     32

        Be sure to call ``app_iter.close()`` if it's there.

     33

     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.

     39

     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)

     60

     61

     62

    def class_name_from_module_name(module_name):

     63

        """Takes a module name and returns the name of the class it

     64

        defines.

     65

     66

        If the module name contains dashes, they are replaced with

     67

        underscores.

     68

     69

        Example::

     70

     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'

     77

     78

        """

     79

        words = module_name.replace('-', '_').split('_')

     80

        return ''.join(w.title() for w in words)

     81

     82

     83

    class PylonsContext(object):

     84

        """Pylons context object

     85

     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.

     89

     90

        This object is attached in

     91

        :class:`~pylons.controllers.core.WSGIController` instances as

     92

        :attr:`~WSGIController._py_object`. For example::

     93

     94

            class MyController(WSGIController):

     95

                def index(self):

     96

                    pyobj = self._py_object

     97

                    return "Environ is %s" % pyobj.request.environ

     98

     99

        """

    100

        pass

    101

    102

    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))

    121

    122

    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 ''

    133

    134

    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']

    147

    148

        def pre(self, command, output_dir, vars):

    149

            """Called before template is applied."""

    150

            package_logger = vars['package']

    151

            if package_logger == 'root':

    152

                # Rename the app logger in the rare case a project is named 'root'

    153

                package_logger = 'app'

    154

            vars['package_logger'] = package_logger

    155

    156

            template_engine = \

    157

                vars.setdefault('template_engine',

    158

                                pylons.configuration.default_template_engine)

    159

    160

            if template_engine == 'mako':

    161

                # Support a Babel extractor default for 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'] = ''

    167

    168

            # Ensure these exist in the namespace

    169

            for name in self.ensure_names:

    170

                vars.setdefault(name, '')

    171

    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'))

    175

    176

    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

        ]

    184

    185

    186

    class PylonsInstaller(Installer):

    187

        use_cheetah = False

    188

        config_file = 'config/deployment.ini_tmpl'

    189

    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)

    207

            # Legacy support for the old location in egg-info

    208

            return super(PylonsInstaller, self).config_content(command, vars)

    209