root/galaxy-central/eggs/Routes-1.12.3-py2.6.egg/routes/util.py @ 3

リビジョン 3, 18.8 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1"""Utility functions for use in templates / controllers
2
3*PLEASE NOTE*: Many of these functions expect an initialized RequestConfig
4object. This is expected to have been initialized for EACH REQUEST by the web
5framework.
6
7"""
8import os
9import re
10import urllib
11from routes import request_config
12
13
14class RoutesException(Exception):
15    """Tossed during Route exceptions"""
16
17
18class MatchException(RoutesException):
19    """Tossed during URL matching exceptions"""
20
21
22class GenerationException(RoutesException):
23    """Tossed during URL generation exceptions"""
24
25
26def _screenargs(kargs, mapper, environ, force_explicit=False):
27    """
28    Private function that takes a dict, and screens it against the current
29    request dict to determine what the dict should look like that is used.
30    This is responsible for the requests "memory" of the current.
31    """
32    # Coerce any unicode args with the encoding
33    encoding = mapper.encoding
34    for key, val in kargs.iteritems():
35        if isinstance(val, unicode):
36            kargs[key] = val.encode(encoding)
37   
38    if mapper.explicit and mapper.sub_domains and not force_explicit:
39        return _subdomain_check(kargs, mapper, environ)
40    elif mapper.explicit and not force_explicit:
41        return kargs
42   
43    controller_name = kargs.get('controller')
44   
45    if controller_name and controller_name.startswith('/'):
46        # If the controller name starts with '/', ignore route memory
47        kargs['controller'] = kargs['controller'][1:]
48        return kargs
49    elif controller_name and not kargs.has_key('action'):
50        # Fill in an action if we don't have one, but have a controller
51        kargs['action'] = 'index'
52   
53    route_args = environ.get('wsgiorg.routing_args')
54    if route_args:
55        memory_kargs = route_args[1].copy()
56    else:
57        memory_kargs = {}
58     
59    # Remove keys from memory and kargs if kargs has them as None
60    for key in [key for key in kargs.keys() if kargs[key] is None]:
61        del kargs[key]
62        if memory_kargs.has_key(key):
63            del memory_kargs[key]
64   
65    # Merge the new args on top of the memory args
66    memory_kargs.update(kargs)
67   
68    # Setup a sub-domain if applicable
69    if mapper.sub_domains:
70        memory_kargs = _subdomain_check(memory_kargs, mapper, environ)
71    return memory_kargs
72
73
74def _subdomain_check(kargs, mapper, environ):
75    """Screen the kargs for a subdomain and alter it appropriately depending
76    on the current subdomain or lack therof."""
77    if mapper.sub_domains:
78        subdomain = kargs.pop('sub_domain', None)
79        if isinstance(subdomain, unicode):
80            subdomain = str(subdomain)
81       
82        fullhost = environ.get('HTTP_HOST') or environ.get('SERVER_NAME')
83       
84        # In case environ defaulted to {}
85        if not fullhost:
86            return kargs
87       
88        hostmatch = fullhost.split(':')
89        host = hostmatch[0]
90        port = ''
91        if len(hostmatch) > 1:
92            port += ':' + hostmatch[1]
93        sub_match = re.compile('^.+?\.(%s)$' % mapper.domain_match)
94        domain = re.sub(sub_match, r'\1', host)
95        if subdomain and not host.startswith(subdomain) and \
96            subdomain not in mapper.sub_domains_ignore:
97            kargs['_host'] = subdomain + '.' + domain + port
98        elif (subdomain in mapper.sub_domains_ignore or \
99            subdomain is None) and domain != host:
100            kargs['_host'] = domain + port
101        return kargs
102    else:
103        return kargs
104
105
106def _url_quote(string, encoding):
107    """A Unicode handling version of urllib.quote."""
108    if encoding:
109        if isinstance(string, unicode):
110            s = string.encode(encoding)
111        elif isinstance(string, str):
112            # assume the encoding is already correct
113            s = string
114        else:
115            s = unicode(string).encode(encoding)
116    else:
117        s = str(string)
118    return urllib.quote(s, '/')
119
120
121def _str_encode(string, encoding):
122    if encoding:
123        if isinstance(string, unicode):
124            s = string.encode(encoding)
125        elif isinstance(string, str):
126            # assume the encoding is already correct
127            s = string
128        else:
129            s = unicode(string).encode(encoding)
130    return s
131
132
133def url_for(*args, **kargs):
134    """Generates a URL
135   
136    All keys given to url_for are sent to the Routes Mapper instance for
137    generation except for::
138       
139        anchor          specified the anchor name to be appened to the path
140        host            overrides the default (current) host if provided
141        protocol        overrides the default (current) protocol if provided
142        qualified       creates the URL with the host/port information as
143                        needed
144       
145    The URL is generated based on the rest of the keys. When generating a new
146    URL, values will be used from the current request's parameters (if
147    present). The following rules are used to determine when and how to keep
148    the current requests parameters:
149   
150    * If the controller is present and begins with '/', no defaults are used
151    * If the controller is changed, action is set to 'index' unless otherwise
152      specified
153   
154    For example, if the current request yielded a dict of
155    {'controller': 'blog', 'action': 'view', 'id': 2}, with the standard
156    ':controller/:action/:id' route, you'd get the following results::
157   
158        url_for(id=4)                    =>  '/blog/view/4',
159        url_for(controller='/admin')     =>  '/admin',
160        url_for(controller='admin')      =>  '/admin/view/2'
161        url_for(action='edit')           =>  '/blog/edit/2',
162        url_for(action='list', id=None)  =>  '/blog/list'
163   
164    **Static and Named Routes**
165   
166    If there is a string present as the first argument, a lookup is done
167    against the named routes table to see if there's any matching routes. The
168    keyword defaults used with static routes will be sent in as GET query
169    arg's if a route matches.
170   
171    If no route by that name is found, the string is assumed to be a raw URL.
172    Should the raw URL begin with ``/`` then appropriate SCRIPT_NAME data will
173    be added if present, otherwise the string will be used as the url with
174    keyword args becoming GET query args.
175   
176    """
177    anchor = kargs.get('anchor')
178    host = kargs.get('host')
179    protocol = kargs.get('protocol')
180    qualified = kargs.pop('qualified', None)
181   
182    # Remove special words from kargs, convert placeholders
183    for key in ['anchor', 'host', 'protocol']:
184        if kargs.get(key):
185            del kargs[key]
186    config = request_config()
187    route = None
188    static = False
189    encoding = config.mapper.encoding
190    url = ''
191    if len(args) > 0:
192        route = config.mapper._routenames.get(args[0])
193       
194        # No named route found, assume the argument is a relative path
195        if not route:
196            static = True
197            url = args[0]
198       
199        if url.startswith('/') and hasattr(config, 'environ') \
200                and config.environ.get('SCRIPT_NAME'):
201            url = config.environ.get('SCRIPT_NAME') + url
202       
203        if static:
204            if kargs:
205                url += '?'
206                query_args = []
207                for key, val in kargs.iteritems():
208                    if isinstance(val, (list, tuple)):
209                        for value in val:
210                            query_args.append("%s=%s" % (
211                                urllib.quote(unicode(key).encode(encoding)),
212                                urllib.quote(unicode(value).encode(encoding))))
213                    else:
214                        query_args.append("%s=%s" % (
215                            urllib.quote(unicode(key).encode(encoding)),
216                            urllib.quote(unicode(val).encode(encoding))))
217                url += '&'.join(query_args)
218    environ = getattr(config, 'environ', {})
219    if 'wsgiorg.routing_args' not in environ:
220        environ = environ.copy()
221        mapper_dict = getattr(config, 'mapper_dict', None)
222        if mapper_dict is not None:
223            match_dict = mapper_dict.copy()
224        else:
225            match_dict = {}
226        environ['wsgiorg.routing_args'] = ((), match_dict)
227   
228    if not static:
229        route_args = []
230        if route:
231            if config.mapper.hardcode_names:
232                route_args.append(route)
233            newargs = route.defaults.copy()
234            newargs.update(kargs)
235           
236            # If this route has a filter, apply it
237            if route.filter:
238                newargs = route.filter(newargs)
239           
240            if not route.static:
241                # Handle sub-domains
242                newargs = _subdomain_check(newargs, config.mapper, environ)
243        else:
244            newargs = _screenargs(kargs, config.mapper, environ)
245        anchor = newargs.pop('_anchor', None) or anchor
246        host = newargs.pop('_host', None) or host
247        protocol = newargs.pop('_protocol', None) or protocol
248        url = config.mapper.generate(*route_args, **newargs)
249    if anchor is not None:
250        url += '#' + _url_quote(anchor, encoding)
251    if host or protocol or qualified:
252        if not host and not qualified:
253            # Ensure we don't use a specific port, as changing the protocol
254            # means that we most likely need a new port
255            host = config.host.split(':')[0]
256        elif not host:
257            host = config.host
258        if not protocol:
259            protocol = config.protocol
260        if url is not None:
261            url = protocol + '://' + host + url
262   
263    if not isinstance(url, str) and url is not None:
264        raise GenerationException("url_for can only return a string, got "
265                        "unicode instead: %s" % url)
266    if url is None:
267        raise GenerationException(
268            "url_for could not generate URL. Called with args: %s %s" % \
269            (args, kargs))
270    return url
271
272
273class URLGenerator(object):
274    """The URL Generator generates URL's
275   
276    It is automatically instantiated by the RoutesMiddleware and put
277    into the ``wsgiorg.routing_args`` tuple accessible as::
278   
279        url = environ['wsgiorg.routing_args'][0][0]
280   
281    Or via the ``routes.url`` key::
282   
283        url = environ['routes.url']
284   
285    The url object may be instantiated outside of a web context for use
286    in testing, however sub_domain support and fully qualified URL's
287    cannot be generated without supplying a dict that must contain the
288    key ``HTTP_HOST``.
289   
290    """
291    def __init__(self, mapper, environ):
292        """Instantiate the URLGenerator
293       
294        ``mapper``
295            The mapper object to use when generating routes.
296        ``environ``
297            The environment dict used in WSGI, alternately, any dict
298            that contains at least an ``HTTP_HOST`` value.
299       
300        """
301        self.mapper = mapper
302        if 'SCRIPT_NAME' not in environ:
303            environ['SCRIPT_NAME'] = ''
304        self.environ = environ
305   
306    def __call__(self, *args, **kargs):
307        """Generates a URL
308
309        All keys given to url_for are sent to the Routes Mapper instance for
310        generation except for::
311
312            anchor          specified the anchor name to be appened to the path
313            host            overrides the default (current) host if provided
314            protocol        overrides the default (current) protocol if provided
315            qualified       creates the URL with the host/port information as
316                            needed
317
318        """
319        anchor = kargs.get('anchor')
320        host = kargs.get('host')
321        protocol = kargs.get('protocol')
322        qualified = kargs.pop('qualified', None)
323
324        # Remove special words from kargs, convert placeholders
325        for key in ['anchor', 'host', 'protocol']:
326            if kargs.get(key):
327                del kargs[key]
328       
329        route = None
330        use_current = '_use_current' in kargs and kargs.pop('_use_current')
331       
332        static = False
333        encoding = self.mapper.encoding
334        url = ''
335               
336        more_args = len(args) > 0
337        if more_args:
338            route = self.mapper._routenames.get(args[0])
339       
340        if not route and more_args:
341            static = True
342            url = args[0]
343            if url.startswith('/') and self.environ.get('SCRIPT_NAME'):
344                url = self.environ.get('SCRIPT_NAME') + url
345
346            if static:
347                if kargs:
348                    url += '?'
349                    query_args = []
350                    for key, val in kargs.iteritems():
351                        if isinstance(val, (list, tuple)):
352                            for value in val:
353                                query_args.append("%s=%s" % (
354                                    urllib.quote(unicode(key).encode(encoding)),
355                                    urllib.quote(unicode(value).encode(encoding))))
356                        else:
357                            query_args.append("%s=%s" % (
358                                urllib.quote(unicode(key).encode(encoding)),
359                                urllib.quote(unicode(val).encode(encoding))))
360                    url += '&'.join(query_args)
361        if not static:
362            route_args = []
363            if route:
364                if self.mapper.hardcode_names:
365                    route_args.append(route)
366                newargs = route.defaults.copy()
367                newargs.update(kargs)
368               
369                # If this route has a filter, apply it
370                if route.filter:
371                    newargs = route.filter(newargs)
372                if not route.static or (route.static and not route.external):
373                    # Handle sub-domains, retain sub_domain if there is one
374                    sub = newargs.get('sub_domain', None)
375                    newargs = _subdomain_check(newargs, self.mapper,
376                                               self.environ)
377                    # If the route requires a sub-domain, and we have it, restore
378                    # it
379                    if 'sub_domain' in route.defaults:
380                        newargs['sub_domain'] = sub
381                   
382            elif use_current:
383                newargs = _screenargs(kargs, self.mapper, self.environ, force_explicit=True)
384            elif 'sub_domain' in kargs:
385                newargs = _subdomain_check(kargs, self.mapper, self.environ)
386            else:
387                newargs = kargs
388           
389            anchor = anchor or newargs.pop('_anchor', None)
390            host = host or newargs.pop('_host', None)
391            protocol = protocol or newargs.pop('_protocol', None)
392            newargs['_environ'] = self.environ
393            url = self.mapper.generate(*route_args, **newargs)
394        if anchor is not None:
395            url += '#' + _url_quote(anchor, encoding)
396        if host or protocol or qualified:
397            if 'routes.cached_hostinfo' not in self.environ:
398                cache_hostinfo(self.environ)
399            hostinfo = self.environ['routes.cached_hostinfo']
400           
401            if not host and not qualified:
402                # Ensure we don't use a specific port, as changing the protocol
403                # means that we most likely need a new port
404                host = hostinfo['host'].split(':')[0]
405            elif not host:
406                host = hostinfo['host']
407            if not protocol:
408                protocol = hostinfo['protocol']
409            if url is not None:
410                if host[-1] != '/':
411                    host += '/'
412                url = protocol + '://' + host + url.lstrip('/')
413
414        if not isinstance(url, str) and url is not None:
415            raise GenerationException("Can only return a string, got "
416                            "unicode instead: %s" % url)
417        if url is None:
418            raise GenerationException(
419                "Could not generate URL. Called with args: %s %s" % \
420                (args, kargs))
421        return url
422   
423    def current(self, *args, **kwargs):
424        """Generate a route that includes params used on the current
425        request
426       
427        The arguments for this method are identical to ``__call__``
428        except that arguments set to None will remove existing route
429        matches of the same name from the set of arguments used to
430        construct a URL.
431        """
432        return self(_use_current=True, *args, **kwargs)
433
434
435def redirect_to(*args, **kargs):
436    """Issues a redirect based on the arguments.
437   
438    Redirect's *should* occur as a "302 Moved" header, however the web
439    framework may utilize a different method.
440   
441    All arguments are passed to url_for to retrieve the appropriate URL, then
442    the resulting URL it sent to the redirect function as the URL.
443    """
444    target = url_for(*args, **kargs)
445    config = request_config()
446    return config.redirect(target)
447
448
449def cache_hostinfo(environ):
450    """Processes the host information and stores a copy
451   
452    This work was previously done but wasn't stored in environ, nor is
453    it guaranteed to be setup in the future (Routes 2 and beyond).
454   
455    cache_hostinfo processes environ keys that may be present to
456    determine the proper host, protocol, and port information to use
457    when generating routes.
458   
459    """
460    hostinfo = {}
461    if environ.get('HTTPS') or environ.get('wsgi.url_scheme') == 'https' \
462       or environ.get('HTTP_X_FORWARDED_PROTO') == 'https':
463        hostinfo['protocol'] = 'https'
464    else:
465        hostinfo['protocol'] = 'http'
466    if environ.get('HTTP_X_FORWARDED_HOST'):
467        hostinfo['host'] = environ['HTTP_X_FORWARDED_HOST']
468    elif environ.get('HTTP_HOST'):
469        hostinfo['host'] = environ['HTTP_HOST']
470    else:
471        hostinfo['host'] = environ['SERVER_NAME']
472        if environ.get('wsgi.url_scheme') == 'https':
473            if environ['SERVER_PORT'] != '443':
474                hostinfo['host'] += ':' + environ['SERVER_PORT']
475        else:
476            if environ['SERVER_PORT'] != '80':
477                hostinfo['host'] += ':' + environ['SERVER_PORT']
478    environ['routes.cached_hostinfo'] = hostinfo
479    return hostinfo
480
481
482def controller_scan(directory=None):
483    """Scan a directory for python files and use them as controllers"""
484    if directory is None:
485        return []
486   
487    def find_controllers(dirname, prefix=''):
488        """Locate controllers in a directory"""
489        controllers = []
490        for fname in os.listdir(dirname):
491            filename = os.path.join(dirname, fname)
492            if os.path.isfile(filename) and \
493                re.match('^[^_]{1,1}.*\.py$', fname):
494                controllers.append(prefix + fname[:-3])
495            elif os.path.isdir(filename):
496                controllers.extend(find_controllers(filename,
497                                                    prefix=prefix+fname+'/'))
498        return controllers
499    def longest_first(fst, lst):
500        """Compare the length of one string to another, shortest goes first"""
501        return cmp(len(lst), len(fst))
502    controllers = find_controllers(directory)
503    controllers.sort(longest_first)
504    return controllers
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。