summaryrefslogtreecommitdiff
path: root/flup/resolver/importingmodule.py
blob: b60d0d0fe7665cadc5efb38cc57bdccf65f72e81 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# Copyright (c) 2005 Allan Saddi <allan@saddi.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $Id$

__author__ = 'Allan Saddi <allan@saddi.com>'
__version__ = '$Revision$'

import sys
import os
import imp

from resolver import *

__all__ = ['ImportingModuleResolver']

class NoDefault(object):
    pass

class ImportingModuleResolver(Resolver):
    """
    Constructor takes a directory name or a list of directories. Interprets
    the first two components of PATH_INFO as 'module/function'. Modules
    are imported as needed and must reside in the directories specified.
    Module and function names beginning with underscore are ignored.

    If the 'module' part of PATH_INFO is missing, it is assumed to be
    self.default_module.

    If the 'function' part of PATH_INFO is missing, it is assumed to be
    self.index_page.

    Upon successful resolution, appends the module and function names to
    SCRIPT_NAME and updates PATH_INFO as the remaining components of the path.

    NB: I would recommend explicitly setting all modules' __all__ list.
    Otherwise, be sure all the names of module-level callables that you
    don't want exported begin with underscore.
    """
    # No default module by default.
    default_module = None
    index_page = 'index'

    def __init__(self, path, defaultModule=NoDefault, index=NoDefault):
        self.path = path
        if defaultModule is not NoDefault:
            self.default_module = defaultModule
        if index is not NoDefault:
            self.index_page = index

    def resolve(self, request, redirect=False):
        path_info = request.pathInfo.split(';')[0]
        path_info = path_info.split('/')

        assert len(path_info) > 0
        assert not path_info[0]

        while len(path_info) < 3:
            path_info.append('')

        module_name, func_name = path_info[1:3]

        if not module_name:
            module_name = self.default_module

        if not func_name:
            func_name = self.index_page

        module = None
        if module_name and (module_name[0] != '_' or redirect) and \
           not module_name.count('.'):
            try:
                module = _import_module(module_name, path=self.path)
            except:
                pass

        if module is not None:
            if func_name and (func_name[0] != '_' or redirect):
                module_all = getattr(module, '__all__', None)
                if module_all is None or func_name in module_all or redirect:
                    func = getattr(module, func_name, None)
                    if callable(func):
                        self._updatePath(request, 2)
                        return func

        return None

def _import_module(name, path=None):
    """
    Imports a module. If path is None, module will be searched for in
    sys.path. If path is given (which may be a single string or a list),
    the module will only be searched for in those directories.
    """
    if path is not None and type(path) is not list:
        path = [path]

    module = sys.modules.get(name)
    if module is not None:
        module_file = getattr(module, '__file__')
        if module_file is None or \
               (path is not None and os.path.dirname(module_file) not in path):
            return None

        return module

    fp, pathname, description = imp.find_module(name, path)
    try:
        return imp.load_module(name, fp, pathname, description)
    finally:
        if fp:
            fp.close()