source: publico/il.spdo/trunk/PasteDeploy-1.5.0-py2.6.egg/paste/deploy/loadwsgi.py @ 5327

Última Alteração nesse arquivo desde 5327 foi 5327, incluída por fabianosantos, 8 anos atrás

Import inicial.

File size: 25.0 KB
RevLinha 
[5327]1# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
2# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
3from __future__ import with_statement
4import os
5import sys
6import re
7
8import pkg_resources
9
10from paste.deploy.compat import ConfigParser, unquote, iteritems
11from paste.deploy.util import fix_call, lookup_object
12
13__all__ = ['loadapp', 'loadserver', 'loadfilter', 'appconfig']
14
15
16############################################################
17## Utility functions
18############################################################
19
20
21def import_string(s):
22    return pkg_resources.EntryPoint.parse("x=" + s).load(False)
23
24
25def _aslist(obj):
26    """
27    Turn object into a list; lists and tuples are left as-is, None
28    becomes [], and everything else turns into a one-element list.
29    """
30    if obj is None:
31        return []
32    elif isinstance(obj, (list, tuple)):
33        return obj
34    else:
35        return [obj]
36
37
38def _flatten(lst):
39    """
40    Flatten a nested list.
41    """
42    if not isinstance(lst, (list, tuple)):
43        return [lst]
44    result = []
45    for item in lst:
46        result.extend(_flatten(item))
47    return result
48
49
50class NicerConfigParser(ConfigParser):
51
52    def __init__(self, filename, *args, **kw):
53        ConfigParser.__init__(self, *args, **kw)
54        self.filename = filename
55        if hasattr(self, '_interpolation'):
56            self._interpolation = self.InterpolateWrapper(self._interpolation)
57
58    read_file = getattr(ConfigParser, 'read_file', ConfigParser.readfp)
59
60    def defaults(self):
61        """Return the defaults, with their values interpolated (with the
62        defaults dict itself)
63
64        Mainly to support defaults using values such as %(here)s
65        """
66        defaults = ConfigParser.defaults(self).copy()
67        for key, val in iteritems(defaults):
68            defaults[key] = self.get('DEFAULT', key) or val
69        return defaults
70
71    def _interpolate(self, section, option, rawval, vars):
72        # Python < 3.2
73        try:
74            return ConfigParser._interpolate(
75                self, section, option, rawval, vars)
76        except Exception:
77            e = sys.exc_info()[1]
78            args = list(e.args)
79            args[0] = 'Error in file %s: %s' % (self.filename, e)
80            e.args = tuple(args)
81            e.message = args[0]
82            raise
83
84    class InterpolateWrapper(object):
85        # Python >= 3.2
86        def __init__(self, original):
87            self._original = original
88
89        def __getattr__(self, name):
90            return getattr(self._original, name)
91
92        def before_get(self, parser, section, option, value, defaults):
93            try:
94                return self._original.before_get(parser, section, option,
95                                                 value, defaults)
96            except Exception:
97                e = sys.exc_info()[1]
98                args = list(e.args)
99                args[0] = 'Error in file %s: %s' % (parser.filename, e)
100                e.args = tuple(args)
101                e.message = args[0]
102                raise
103
104
105############################################################
106## Object types
107############################################################
108
109
110class _ObjectType(object):
111
112    name = None
113    egg_protocols = None
114    config_prefixes = None
115
116    def __init__(self):
117        # Normalize these variables:
118        self.egg_protocols = [_aslist(p) for p in _aslist(self.egg_protocols)]
119        self.config_prefixes = [_aslist(p) for p in _aslist(self.config_prefixes)]
120
121    def __repr__(self):
122        return '<%s protocols=%r prefixes=%r>' % (
123            self.name, self.egg_protocols, self.config_prefixes)
124
125    def invoke(self, context):
126        assert context.protocol in _flatten(self.egg_protocols)
127        return fix_call(context.object,
128                        context.global_conf, **context.local_conf)
129
130
131class _App(_ObjectType):
132
133    name = 'application'
134    egg_protocols = ['paste.app_factory', 'paste.composite_factory',
135                     'paste.composit_factory']
136    config_prefixes = [['app', 'application'], ['composite', 'composit'],
137                       'pipeline', 'filter-app']
138
139    def invoke(self, context):
140        if context.protocol in ('paste.composit_factory',
141                                'paste.composite_factory'):
142            return fix_call(context.object,
143                            context.loader, context.global_conf,
144                            **context.local_conf)
145        elif context.protocol == 'paste.app_factory':
146            return fix_call(context.object, context.global_conf, **context.local_conf)
147        else:
148            assert 0, "Protocol %r unknown" % context.protocol
149
150APP = _App()
151
152
153class _Filter(_ObjectType):
154    name = 'filter'
155    egg_protocols = [['paste.filter_factory', 'paste.filter_app_factory']]
156    config_prefixes = ['filter']
157
158    def invoke(self, context):
159        if context.protocol == 'paste.filter_factory':
160            return fix_call(context.object,
161                            context.global_conf, **context.local_conf)
162        elif context.protocol == 'paste.filter_app_factory':
163            def filter_wrapper(wsgi_app):
164                # This should be an object, so it has a nicer __repr__
165                return fix_call(context.object,
166                                wsgi_app, context.global_conf,
167                                **context.local_conf)
168            return filter_wrapper
169        else:
170            assert 0, "Protocol %r unknown" % context.protocol
171
172FILTER = _Filter()
173
174
175class _Server(_ObjectType):
176    name = 'server'
177    egg_protocols = [['paste.server_factory', 'paste.server_runner']]
178    config_prefixes = ['server']
179
180    def invoke(self, context):
181        if context.protocol == 'paste.server_factory':
182            return fix_call(context.object,
183                            context.global_conf, **context.local_conf)
184        elif context.protocol == 'paste.server_runner':
185            def server_wrapper(wsgi_app):
186                # This should be an object, so it has a nicer __repr__
187                return fix_call(context.object,
188                                wsgi_app, context.global_conf,
189                                **context.local_conf)
190            return server_wrapper
191        else:
192            assert 0, "Protocol %r unknown" % context.protocol
193
194SERVER = _Server()
195
196
197# Virtual type: (@@: There's clearly something crufty here;
198# this probably could be more elegant)
199class _PipeLine(_ObjectType):
200    name = 'pipeline'
201
202    def invoke(self, context):
203        app = context.app_context.create()
204        filters = [c.create() for c in context.filter_contexts]
205        filters.reverse()
206        for filter in filters:
207            app = filter(app)
208        return app
209
210PIPELINE = _PipeLine()
211
212
213class _FilterApp(_ObjectType):
214    name = 'filter_app'
215
216    def invoke(self, context):
217        next_app = context.next_context.create()
218        filter = context.filter_context.create()
219        return filter(next_app)
220
221FILTER_APP = _FilterApp()
222
223
224class _FilterWith(_App):
225    name = 'filtered_with'
226
227    def invoke(self, context):
228        filter = context.filter_context.create()
229        filtered = context.next_context.create()
230        if context.next_context.object_type is APP:
231            return filter(filtered)
232        else:
233            # filtering a filter
234            def composed(app):
235                return filter(filtered(app))
236            return composed
237
238FILTER_WITH = _FilterWith()
239
240
241############################################################
242## Loaders
243############################################################
244
245
246def loadapp(uri, name=None, **kw):
247    return loadobj(APP, uri, name=name, **kw)
248
249
250def loadfilter(uri, name=None, **kw):
251    return loadobj(FILTER, uri, name=name, **kw)
252
253
254def loadserver(uri, name=None, **kw):
255    return loadobj(SERVER, uri, name=name, **kw)
256
257
258def appconfig(uri, name=None, relative_to=None, global_conf=None):
259    context = loadcontext(APP, uri, name=name,
260                          relative_to=relative_to,
261                          global_conf=global_conf)
262    return context.config()
263
264_loaders = {}
265
266
267def loadobj(object_type, uri, name=None, relative_to=None,
268            global_conf=None):
269    context = loadcontext(
270        object_type, uri, name=name, relative_to=relative_to,
271        global_conf=global_conf)
272    return context.create()
273
274
275def loadcontext(object_type, uri, name=None, relative_to=None,
276                global_conf=None):
277    if '#' in uri:
278        if name is None:
279            uri, name = uri.split('#', 1)
280        else:
281            # @@: Ignore fragment or error?
282            uri = uri.split('#', 1)[0]
283    if name is None:
284        name = 'main'
285    if ':' not in uri:
286        raise LookupError("URI has no scheme: %r" % uri)
287    scheme, path = uri.split(':', 1)
288    scheme = scheme.lower()
289    if scheme not in _loaders:
290        raise LookupError(
291            "URI scheme not known: %r (from %s)"
292            % (scheme, ', '.join(_loaders.keys())))
293    return _loaders[scheme](
294        object_type,
295        uri, path, name=name, relative_to=relative_to,
296        global_conf=global_conf)
297
298
299def _loadconfig(object_type, uri, path, name, relative_to,
300                global_conf):
301    isabs = os.path.isabs(path)
302    # De-Windowsify the paths:
303    path = path.replace('\\', '/')
304    if not isabs:
305        if not relative_to:
306            raise ValueError(
307                "Cannot resolve relative uri %r; no relative_to keyword "
308                "argument given" % uri)
309        relative_to = relative_to.replace('\\', '/')
310        if relative_to.endswith('/'):
311            path = relative_to + path
312        else:
313            path = relative_to + '/' + path
314    if path.startswith('///'):
315        path = path[2:]
316    path = unquote(path)
317    loader = ConfigLoader(path)
318    if global_conf:
319        loader.update_defaults(global_conf, overwrite=False)
320    return loader.get_context(object_type, name, global_conf)
321
322_loaders['config'] = _loadconfig
323
324
325def _loadegg(object_type, uri, spec, name, relative_to,
326             global_conf):
327    loader = EggLoader(spec)
328    return loader.get_context(object_type, name, global_conf)
329
330_loaders['egg'] = _loadegg
331
332
333def _loadfunc(object_type, uri, spec, name, relative_to,
334             global_conf):
335
336    loader = FuncLoader(spec)
337    return loader.get_context(object_type, name, global_conf)
338
339_loaders['call'] = _loadfunc
340
341############################################################
342## Loaders
343############################################################
344
345
346class _Loader(object):
347
348    def get_app(self, name=None, global_conf=None):
349        return self.app_context(
350            name=name, global_conf=global_conf).create()
351
352    def get_filter(self, name=None, global_conf=None):
353        return self.filter_context(
354            name=name, global_conf=global_conf).create()
355
356    def get_server(self, name=None, global_conf=None):
357        return self.server_context(
358            name=name, global_conf=global_conf).create()
359
360    def app_context(self, name=None, global_conf=None):
361        return self.get_context(
362            APP, name=name, global_conf=global_conf)
363
364    def filter_context(self, name=None, global_conf=None):
365        return self.get_context(
366            FILTER, name=name, global_conf=global_conf)
367
368    def server_context(self, name=None, global_conf=None):
369        return self.get_context(
370            SERVER, name=name, global_conf=global_conf)
371
372    _absolute_re = re.compile(r'^[a-zA-Z]+:')
373
374    def absolute_name(self, name):
375        """
376        Returns true if the name includes a scheme
377        """
378        if name is None:
379            return False
380        return self._absolute_re.search(name)
381
382
383class ConfigLoader(_Loader):
384
385    def __init__(self, filename):
386        self.filename = filename = filename.strip()
387        defaults = {
388            'here': os.path.dirname(os.path.abspath(filename)),
389            '__file__': os.path.abspath(filename)
390            }
391        self.parser = NicerConfigParser(filename, defaults=defaults)
392        self.parser.optionxform = str  # Don't lower-case keys
393        with open(filename) as f:
394            self.parser.read_file(f)
395
396    def update_defaults(self, new_defaults, overwrite=True):
397        for key, value in iteritems(new_defaults):
398            if not overwrite and key in self.parser._defaults:
399                continue
400            self.parser._defaults[key] = value
401
402    def get_context(self, object_type, name=None, global_conf=None):
403        if self.absolute_name(name):
404            return loadcontext(object_type, name,
405                               relative_to=os.path.dirname(self.filename),
406                               global_conf=global_conf)
407        section = self.find_config_section(
408            object_type, name=name)
409        if global_conf is None:
410            global_conf = {}
411        else:
412            global_conf = global_conf.copy()
413        defaults = self.parser.defaults()
414        global_conf.update(defaults)
415        local_conf = {}
416        global_additions = {}
417        get_from_globals = {}
418        for option in self.parser.options(section):
419            if option.startswith('set '):
420                name = option[4:].strip()
421                global_additions[name] = global_conf[name] = (
422                    self.parser.get(section, option))
423            elif option.startswith('get '):
424                name = option[4:].strip()
425                get_from_globals[name] = self.parser.get(section, option)
426            else:
427                if option in defaults:
428                    # @@: It's a global option (?), so skip it
429                    continue
430                local_conf[option] = self.parser.get(section, option)
431        for local_var, glob_var in get_from_globals.items():
432            local_conf[local_var] = global_conf[glob_var]
433        if object_type in (APP, FILTER) and 'filter-with' in local_conf:
434            filter_with = local_conf.pop('filter-with')
435        else:
436            filter_with = None
437        if 'require' in local_conf:
438            for spec in local_conf['require'].split():
439                pkg_resources.require(spec)
440            del local_conf['require']
441        if section.startswith('filter-app:'):
442            context = self._filter_app_context(
443                object_type, section, name=name,
444                global_conf=global_conf, local_conf=local_conf,
445                global_additions=global_additions)
446        elif section.startswith('pipeline:'):
447            context = self._pipeline_app_context(
448                object_type, section, name=name,
449                global_conf=global_conf, local_conf=local_conf,
450                global_additions=global_additions)
451        elif 'use' in local_conf:
452            context = self._context_from_use(
453                object_type, local_conf, global_conf, global_additions,
454                section)
455        else:
456            context = self._context_from_explicit(
457                object_type, local_conf, global_conf, global_additions,
458                section)
459        if filter_with is not None:
460            filter_with_context = LoaderContext(
461                obj=None,
462                object_type=FILTER_WITH,
463                protocol=None,
464                global_conf=global_conf, local_conf=local_conf,
465                loader=self)
466            filter_with_context.filter_context = self.filter_context(
467                name=filter_with, global_conf=global_conf)
468            filter_with_context.next_context = context
469            return filter_with_context
470        return context
471
472    def _context_from_use(self, object_type, local_conf, global_conf,
473                          global_additions, section):
474        use = local_conf.pop('use')
475        context = self.get_context(
476            object_type, name=use, global_conf=global_conf)
477        context.global_conf.update(global_additions)
478        context.local_conf.update(local_conf)
479        if '__file__' in global_conf:
480            # use sections shouldn't overwrite the original __file__
481            context.global_conf['__file__'] = global_conf['__file__']
482        # @@: Should loader be overwritten?
483        context.loader = self
484
485        if context.protocol is None:
486            # Determine protocol from section type
487            section_protocol = section.split(':', 1)[0]
488            if section_protocol in ('application', 'app'):
489                context.protocol = 'paste.app_factory'
490            elif section_protocol in ('composit', 'composite'):
491                context.protocol = 'paste.composit_factory'
492            else:
493                # This will work with 'server' and 'filter', otherwise it
494                # could fail but there is an error message already for
495                # bad protocols
496                context.protocol = 'paste.%s_factory' % context_protocol
497
498        return context
499
500    def _context_from_explicit(self, object_type, local_conf, global_conf,
501                               global_addition, section):
502        possible = []
503        for protocol_options in object_type.egg_protocols:
504            for protocol in protocol_options:
505                if protocol in local_conf:
506                    possible.append((protocol, local_conf[protocol]))
507                    break
508        if len(possible) > 1:
509            raise LookupError(
510                "Multiple protocols given in section %r: %s"
511                % (section, possible))
512        if not possible:
513            raise LookupError(
514                "No loader given in section %r" % section)
515        found_protocol, found_expr = possible[0]
516        del local_conf[found_protocol]
517        value = import_string(found_expr)
518        context = LoaderContext(
519            value, object_type, found_protocol,
520            global_conf, local_conf, self)
521        return context
522
523    def _filter_app_context(self, object_type, section, name,
524                            global_conf, local_conf, global_additions):
525        if 'next' not in local_conf:
526            raise LookupError(
527                "The [%s] section in %s is missing a 'next' setting"
528                % (section, self.filename))
529        next_name = local_conf.pop('next')
530        context = LoaderContext(None, FILTER_APP, None, global_conf,
531                                local_conf, self)
532        context.next_context = self.get_context(
533            APP, next_name, global_conf)
534        if 'use' in local_conf:
535            context.filter_context = self._context_from_use(
536                FILTER, local_conf, global_conf, global_additions,
537                section)
538        else:
539            context.filter_context = self._context_from_explicit(
540                FILTER, local_conf, global_conf, global_additions,
541                section)
542        return context
543
544    def _pipeline_app_context(self, object_type, section, name,
545                              global_conf, local_conf, global_additions):
546        if 'pipeline' not in local_conf:
547            raise LookupError(
548                "The [%s] section in %s is missing a 'pipeline' setting"
549                % (section, self.filename))
550        pipeline = local_conf.pop('pipeline').split()
551        if local_conf:
552            raise LookupError(
553                "The [%s] pipeline section in %s has extra "
554                "(disallowed) settings: %s"
555                % (', '.join(local_conf.keys())))
556        context = LoaderContext(None, PIPELINE, None, global_conf,
557                                local_conf, self)
558        context.app_context = self.get_context(
559            APP, pipeline[-1], global_conf)
560        context.filter_contexts = [
561            self.get_context(FILTER, name, global_conf)
562            for name in pipeline[:-1]]
563        return context
564
565    def find_config_section(self, object_type, name=None):
566        """
567        Return the section name with the given name prefix (following the
568        same pattern as ``protocol_desc`` in ``config``.  It must have the
569        given name, or for ``'main'`` an empty name is allowed.  The
570        prefix must be followed by a ``:``.
571
572        Case is *not* ignored.
573        """
574        possible = []
575        for name_options in object_type.config_prefixes:
576            for name_prefix in name_options:
577                found = self._find_sections(
578                    self.parser.sections(), name_prefix, name)
579                if found:
580                    possible.extend(found)
581                    break
582        if not possible:
583            raise LookupError(
584                "No section %r (prefixed by %s) found in config %s"
585                % (name,
586                   ' or '.join(map(repr, _flatten(object_type.config_prefixes))),
587                   self.filename))
588        if len(possible) > 1:
589            raise LookupError(
590                "Ambiguous section names %r for section %r (prefixed by %s) "
591                "found in config %s"
592                % (possible, name,
593                   ' or '.join(map(repr, _flatten(object_type.config_prefixes))),
594                   self.filename))
595        return possible[0]
596
597    def _find_sections(self, sections, name_prefix, name):
598        found = []
599        if name is None:
600            if name_prefix in sections:
601                found.append(name_prefix)
602            name = 'main'
603        for section in sections:
604            if section.startswith(name_prefix + ':'):
605                if section[len(name_prefix) + 1:].strip() == name:
606                    found.append(section)
607        return found
608
609
610class EggLoader(_Loader):
611
612    def __init__(self, spec):
613        self.spec = spec
614
615    def get_context(self, object_type, name=None, global_conf=None):
616        if self.absolute_name(name):
617            return loadcontext(object_type, name,
618                               global_conf=global_conf)
619        entry_point, protocol, ep_name = self.find_egg_entry_point(
620            object_type, name=name)
621        return LoaderContext(
622            entry_point,
623            object_type,
624            protocol,
625            global_conf or {}, {},
626            self,
627            distribution=pkg_resources.get_distribution(self.spec),
628            entry_point_name=ep_name)
629
630    def find_egg_entry_point(self, object_type, name=None):
631        """
632        Returns the (entry_point, protocol) for the with the given
633        ``name``.
634        """
635        if name is None:
636            name = 'main'
637        possible = []
638        for protocol_options in object_type.egg_protocols:
639            for protocol in protocol_options:
640                pkg_resources.require(self.spec)
641                entry = pkg_resources.get_entry_info(
642                    self.spec,
643                    protocol,
644                    name)
645                if entry is not None:
646                    possible.append((entry.load(), protocol, entry.name))
647                    break
648        if not possible:
649            # Better exception
650            dist = pkg_resources.get_distribution(self.spec)
651            raise LookupError(
652                "Entry point %r not found in egg %r (dir: %s; protocols: %s; "
653                "entry_points: %s)"
654                % (name, self.spec,
655                   dist.location,
656                   ', '.join(_flatten(object_type.egg_protocols)),
657                   ', '.join(_flatten([
658                (pkg_resources.get_entry_info(self.spec, prot, name) or {}).keys()
659                for prot in protocol_options] or '(no entry points)'))))
660        if len(possible) > 1:
661            raise LookupError(
662                "Ambiguous entry points for %r in egg %r (protocols: %s)"
663                % (name, self.spec, ', '.join(_flatten(protocol_options))))
664        return possible[0]
665
666
667class FuncLoader(_Loader):
668    """ Loader that supports specifying functions inside modules, without
669    using eggs at all. Configuration should be in the format:
670        use = call:my.module.path:function_name
671       
672    Dot notation is supported in both the module and function name, e.g.:
673        use = call:my.module.path:object.method
674    """
675    def __init__(self, spec):
676        self.spec = spec
677        if not ':' in spec:
678            raise LookupError("Configuration not in format module:function")
679
680    def get_context(self, object_type, name=None, global_conf=None):
681        obj = lookup_object(self.spec)
682        return LoaderContext(
683            obj,
684            object_type,
685            None, # determine protocol from section type
686            global_conf or {},
687            {},
688            self,
689            )
690
691
692class LoaderContext(object):
693
694    def __init__(self, obj, object_type, protocol,
695                 global_conf, local_conf, loader,
696                 distribution=None, entry_point_name=None):
697        self.object = obj
698        self.object_type = object_type
699        self.protocol = protocol
700        #assert protocol in _flatten(object_type.egg_protocols), (
701        #    "Bad protocol %r; should be one of %s"
702        #    % (protocol, ', '.join(map(repr, _flatten(object_type.egg_protocols)))))
703        self.global_conf = global_conf
704        self.local_conf = local_conf
705        self.loader = loader
706        self.distribution = distribution
707        self.entry_point_name = entry_point_name
708
709    def create(self):
710        return self.object_type.invoke(self)
711
712    def config(self):
713        conf = AttrDict(self.global_conf)
714        conf.update(self.local_conf)
715        conf.local_conf = self.local_conf
716        conf.global_conf = self.global_conf
717        conf.context = self
718        return conf
719
720
721class AttrDict(dict):
722    """
723    A dictionary that can be assigned to.
724    """
725    pass
Note: Veja TracBrowser para ajuda no uso do navegador do trac.
 

The contents and data of this website are published under license:
Creative Commons 4.0 Brasil - Atribuir Fonte - Compartilhar Igual.