micropython

Annotated micropython/__init__.py

3:3699ff87d748
2007-10-20 Paul Boddie Added "vacuum" methods for removing speculatively created modules. Separated the __builtins__ module namespace from each module's own namespace. Added function parameter details to Function objects. Added convenience methods for dictionary access to objects. Added more missing node visitor methods.
paul@0 1
#!/usr/bin/env python
paul@0 2
paul@0 3
"""
paul@0 4
The micropython package for processing Python source code. The code originates
paul@0 5
from the simplify package but has had various details related to that package
paul@0 6
removed.
paul@0 7
paul@0 8
Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
paul@0 9
paul@0 10
This program is free software; you can redistribute it and/or modify it under
paul@0 11
the terms of the GNU General Public License as published by the Free Software
paul@0 12
Foundation; either version 3 of the License, or (at your option) any later
paul@0 13
version.
paul@0 14
paul@0 15
This program is distributed in the hope that it will be useful, but WITHOUT
paul@0 16
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@0 17
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@0 18
details.
paul@0 19
paul@0 20
You should have received a copy of the GNU General Public License along with
paul@0 21
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@0 22
paul@0 23
--------
paul@0 24
paul@0 25
To use this module, an importer should be constructed and the load_from_file
paul@0 26
method used. Here, the standard path for module searching is employed:
paul@0 27
paul@0 28
importer = Importer(sys.path)
paul@0 29
importer.load_from_file(filename)
paul@0 30
paul@0 31
Such importer objects are the most convenient mechanism through which the
paul@0 32
functionality of the micropython package may be accessed.
paul@0 33
"""
paul@0 34
paul@0 35
import micropython.inspect
paul@0 36
import os
paul@0 37
try:
paul@0 38
    set
paul@0 39
except NameError:
paul@0 40
    from sets import Set as set
paul@0 41
paul@0 42
class Importer:
paul@0 43
paul@0 44
    "An import machine, searching for and loading modules."
paul@0 45
paul@0 46
    def __init__(self, path=None):
paul@0 47
paul@0 48
        """
paul@0 49
        Initialise the importer with the given search 'path' - a list of
paul@0 50
        directories to search for Python modules.
paul@0 51
        """
paul@0 52
paul@0 53
        self.path = path or [os.getcwd()]
paul@0 54
        self.modules = {}
paul@2 55
        self.loading = set()
paul@0 56
paul@3 57
    def vacuum(self):
paul@3 58
paul@3 59
        "Tidy up the modules."
paul@3 60
paul@3 61
        for name, module in self.modules.items():
paul@3 62
            if module.loaded:
paul@3 63
                module.vacuum()
paul@3 64
            else:
paul@3 65
                del self.modules[name]
paul@3 66
paul@0 67
    def get_modules(self):
paul@0 68
paul@0 69
        "Return all modules known to the importer."
paul@0 70
paul@0 71
        return self.modules.values()
paul@0 72
paul@0 73
    def find_in_path(self, name):
paul@0 74
paul@0 75
        """
paul@0 76
        Find the given module 'name' in the search path, returning None where no
paul@0 77
        such module could be found, or a 2-tuple from the 'find' method
paul@0 78
        otherwise.
paul@0 79
        """
paul@0 80
paul@0 81
        for d in self.path:
paul@0 82
            m = self.find(d, name)
paul@0 83
            if m: return m
paul@0 84
        return None
paul@0 85
paul@0 86
    def find(self, d, name):
paul@0 87
paul@0 88
        """
paul@0 89
        In the directory 'd', find the given module 'name', where 'name' can
paul@0 90
        either refer to a single file module or to a package. Return None if the
paul@0 91
        'name' cannot be associated with either a file or a package directory,
paul@0 92
        or a 2-tuple from '_find_package' or '_find_module' otherwise.
paul@0 93
        """
paul@0 94
paul@0 95
        m = self._find_package(d, name)
paul@0 96
        if m: return m
paul@0 97
        m = self._find_module(d, name)
paul@0 98
        if m: return m
paul@0 99
        return None
paul@0 100
paul@0 101
    def _find_module(self, d, name):
paul@0 102
paul@0 103
        """
paul@0 104
        In the directory 'd', find the given module 'name', returning None where
paul@0 105
        no suitable file exists in the directory, or a 2-tuple consisting of
paul@0 106
        None (indicating that no package directory is involved) and a filename
paul@0 107
        indicating the location of the module.
paul@0 108
        """
paul@0 109
paul@0 110
        name_py = name + os.extsep + "py"
paul@0 111
        filename = self._find_file(d, name_py)
paul@0 112
        if filename:
paul@0 113
            return None, filename
paul@0 114
        return None
paul@0 115
paul@0 116
    def _find_package(self, d, name):
paul@0 117
paul@0 118
        """
paul@0 119
        In the directory 'd', find the given package 'name', returning None
paul@0 120
        where no suitable package directory exists, or a 2-tuple consisting of
paul@0 121
        a directory (indicating the location of the package directory itself)
paul@0 122
        and a filename indicating the location of the __init__.py module which
paul@0 123
        declares the package's top-level contents.
paul@0 124
        """
paul@0 125
paul@0 126
        filename = self._find_file(d, name)
paul@0 127
        if filename:
paul@0 128
            init_py = "__init__" + os.path.extsep + "py"
paul@0 129
            init_py_filename = self._find_file(filename, init_py)
paul@0 130
            if init_py_filename:
paul@0 131
                return filename, init_py_filename
paul@0 132
        return None
paul@0 133
paul@0 134
    def _find_file(self, d, filename):
paul@0 135
paul@0 136
        """
paul@0 137
        Return the filename obtained when searching the directory 'd' for the
paul@0 138
        given 'filename', or None if no actual file exists for the filename.
paul@0 139
        """
paul@0 140
paul@0 141
        filename = os.path.join(d, filename)
paul@0 142
        if os.path.exists(filename):
paul@0 143
            return filename
paul@0 144
        else:
paul@0 145
            return None
paul@0 146
paul@0 147
    def load(self, name, return_leaf=0):
paul@0 148
paul@0 149
        """
paul@0 150
        Load the module or package with the given 'name'. Return an object
paul@0 151
        referencing the loaded module or package, or None if no such module or
paul@0 152
        package exists.
paul@0 153
        """
paul@0 154
paul@0 155
        print "Loading", name
paul@2 156
        if self.modules.has_key(name) and self.modules[name].loaded:
paul@2 157
            print "Cached", name
paul@0 158
            return self.modules[name]
paul@0 159
paul@0 160
        # Split the name into path components, and try to find the uppermost in
paul@0 161
        # the search path.
paul@0 162
paul@0 163
        path = name.split(".")
paul@0 164
        m = self.find_in_path(path[0])
paul@0 165
        if not m:
paul@2 166
            print "Not found", path[0]
paul@0 167
            return None # NOTE: Import error.
paul@0 168
        d, filename = m
paul@0 169
paul@0 170
        # Either acquire a reference to an already-imported module, or load the
paul@0 171
        # module from a file.
paul@0 172
paul@2 173
        top = module = self.load_from_file(filename, path[0])
paul@0 174
paul@0 175
        # For hierarchical names, traverse each path component...
paul@0 176
paul@0 177
        if len(path) > 1:
paul@0 178
            if not d:
paul@2 179
                print "No package", filename
paul@0 180
                return None # NOTE: Import error (package not found).
paul@1 181
            else:
paul@1 182
                self.add_submodules(d, module)
paul@0 183
paul@0 184
            path_so_far = path[:1]
paul@0 185
            for p in path[1:]:
paul@0 186
                path_so_far.append(p)
paul@0 187
paul@0 188
                # Find the package or module concerned.
paul@0 189
paul@0 190
                m = self.find(d, p)
paul@0 191
                if not m:
paul@2 192
                    print "Not found", p
paul@0 193
                    return None # NOTE: Import error.
paul@0 194
                d, filename = m
paul@0 195
                module_name = ".".join(path_so_far)
paul@0 196
paul@0 197
                # Either reference an imported module or load one from a file.
paul@0 198
paul@2 199
                submodule = self.load_from_file(filename, module_name)
paul@0 200
paul@1 201
                if d:
paul@1 202
                    self.add_submodules(d, module)
paul@1 203
paul@0 204
                # Store the submodule within its parent module.
paul@0 205
paul@0 206
                module.namespace[p] = submodule
paul@0 207
                module = submodule
paul@0 208
paul@0 209
        # Return either the deepest or the uppermost module.
paul@0 210
paul@0 211
        if return_leaf:
paul@0 212
            return module
paul@0 213
        else:
paul@0 214
            return top
paul@0 215
paul@0 216
    def load_from_file(self, name, module_name=None):
paul@0 217
paul@0 218
        """
paul@0 219
        Load the module with the given 'name' (which may be a full module path).
paul@0 220
        """
paul@0 221
paul@0 222
        if module_name is None:
paul@0 223
            module_name = "__main__"
paul@0 224
paul@1 225
        module = self.add_module(module_name)
paul@2 226
        if not module.loaded and module not in self.loading:
paul@2 227
            self.loading.add(module)
paul@2 228
            print "Parsing", name
paul@2 229
            module.parse(name)
paul@2 230
            print "Done", name
paul@2 231
            self.loading.remove(module)
paul@2 232
            module.loaded = 1
paul@0 233
paul@0 234
        # Record the module.
paul@0 235
paul@2 236
        print "Loaded", module_name #, "with namespace", module.namespace.keys()
paul@0 237
        return module
paul@0 238
paul@1 239
    def add_module(self, module_name):
paul@1 240
paul@1 241
        """
paul@1 242
        Return the module with the given 'module_name', adding a new module
paul@1 243
        object if one does not already exist.
paul@1 244
        """
paul@1 245
paul@1 246
        if not self.modules.has_key(module_name):
paul@1 247
            self.modules[module_name] = module = micropython.inspect.Module(module_name, self)
paul@1 248
        else:
paul@1 249
            module = self.modules[module_name]
paul@1 250
        return module
paul@1 251
paul@1 252
    def add_submodules(self, pathname, module):
paul@1 253
paul@1 254
        """
paul@1 255
        Work around insufficient __all__ declarations and examine the directory
paul@1 256
        with the given 'pathname', adding submodules to the given 'module'.
paul@1 257
        """
paul@1 258
paul@1 259
        for filename in os.listdir(pathname):
paul@1 260
            submodule = os.path.splitext(filename)[0]
paul@1 261
            module.namespace[submodule] = self.add_module(module.name + "." + submodule)
paul@1 262
paul@0 263
# vim: tabstop=4 expandtab shiftwidth=4