sources for initpkg.py [rev. 38799]
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
"""
package initialization.
You use the functionality of this package by putting
    from py.initpkg import initpkg
    initpkg(__name__, exportdefs={
        'name1.name2' : ('./path/to/file.py', 'name')
        ...
    })
into your package's __init__.py file.  This will
lead your package to only expose the names of all
your implementation files that you explicitely
specify.  In the above example 'name1' will
become a Module instance where 'name2' is
bound in its namespace to the 'name' object
in the relative './path/to/file.py' python module.
Note that you can also use a '.c' file in which
case it will be compiled via distutils-facilities
on the fly.
"""
from __future__ import generators
import sys
import os
assert sys.version_info >= (2,2,0), "py lib requires python 2.2 or higher"
from types import ModuleType
# ---------------------------------------------------
# Package Object
# ---------------------------------------------------
class Package(object):
    def __init__(self, name, exportdefs):
        pkgmodule = sys.modules[name]
        assert pkgmodule.__name__ == name
        self.name = name
        self.exportdefs = exportdefs
        self.module = pkgmodule
        assert not hasattr(pkgmodule, '__package__'), \
                   "unsupported reinitialization of %r" % pkgmodule
        pkgmodule.__package__ = self
        # make available pkgname.__
        implname = name + '.' + '__'
        self.implmodule = ModuleType(implname)
        self.implmodule.__name__ = implname
        self.implmodule.__file__ = os.path.abspath(pkgmodule.__file__)
        self.implmodule.__path__ = [os.path.abspath(p)
                                    for p in pkgmodule.__path__]
        pkgmodule.__ = self.implmodule
        setmodule(implname, self.implmodule)
        # inhibit further direct filesystem imports through the package module
        del pkgmodule.__path__
    def _resolve(self, extpyish):
        """ resolve a combined filesystem/python extpy-ish path. """
        fspath, modpath = extpyish
        if not fspath.endswith('.py'):
            import py
            e = py.path.local(self.implmodule.__file__)
            e = e.dirpath(fspath, abs=True)
            e = py.path.extpy(e, modpath)
            return e.resolve()
        assert fspath.startswith('./'), \
               "%r is not an implementation path (XXX)" % (extpyish,)
        implmodule = self._loadimpl(fspath[:-3])
        if not isinstance(modpath, str): # export the entire module
            return implmodule
        current = implmodule
        for x in modpath.split('.'):
            try: 
                current = getattr(current, x)
            except AttributeError: 
                raise AttributeError("resolving %r failed: %s" %(
                                     extpyish, x))
        return current
    def getimportname(self, path): 
        if not path.ext.startswith('.py'): 
            return None 
        import py
        base = py.path.local(self.implmodule.__file__).dirpath()
        if not path.relto(base): 
            return None 
        names = path.new(ext='').relto(base).split(path.sep) 
        dottedname = ".".join([self.implmodule.__name__] + names)
        return dottedname 
    def _loadimpl(self, relfile):
        """ load implementation for the given relfile. """
        parts = [x.strip() for x in relfile.split('/') if x and x!= '.']
        modpath = ".".join([self.implmodule.__name__] + parts)
        #print "trying import", modpath
        return __import__(modpath, None, None, ['__doc__'])
    def exportitems(self):
        return self.exportdefs.items()
    def getpath(self):
        from py.path import local
        base = local(self.implmodule.__file__).dirpath()
        assert base.check()
        return base
    def _iterfiles(self):
        from py.__.path.common import checker
        base = self.getpath()
        for x in base.visit(checker(file=1, notext='.pyc'),
                            rec=checker(dotfile=0)):
            yield x
    def shahexdigest(self, cache=[]):
        """ return sha hexdigest for files contained in package. """
        if cache:
            return cache[0]
        from sha import sha
        sum = sha()
        # XXX the checksum depends on the order in which visit() enumerates
        # the files, and it doesn't depend on the file names and paths
        for x in self._iterfiles():
            sum.update(x.read())
        cache.append(sum.hexdigest())
        return cache[0]
    def getzipdata(self):
        """ return string representing a zipfile containing the package. """
        import zipfile
        import py
        try:
            from cStringIO import StringIO
        except ImportError:
            from StringIO import StringIO
        base = py.__package__.getpath().dirpath()
        outf = StringIO()
        f = zipfile.ZipFile(outf, 'w', compression=zipfile.ZIP_DEFLATED)
        try:
            for x in self._iterfiles():
                f.write(str(x), x.relto(base))
        finally:
            f.close()
        return outf.getvalue()
    def getrev(self):
        import py
        p = py.path.svnwc(self.module.__file__).dirpath()
        try:
            return p.info().rev
        except (KeyboardInterrupt, MemoryError, SystemExit):
            raise
        except:
            return 'unknown'
def setmodule(modpath, module):
    #print "sys.modules[%r] = %r" % (modpath, module)
    sys.modules[modpath] = module
# ---------------------------------------------------
# Virtual Module Object
# ---------------------------------------------------
class Module(ModuleType):
    def __init__(self, pkg, name):
        self.__package__ = pkg
        self.__name__ = name
        self.__map__ = {}
    def __getattr__(self, name):
        if '*' in self.__map__: 
            extpy = self.__map__['*'][0], name 
            result = self.__package__._resolve(extpy) 
        else: 
            try:
                extpy = self.__map__[name]
            except KeyError:
                __tracebackhide__ = True
                raise AttributeError(name)
            else: 
                result = self.__package__._resolve(extpy) 
                del self.__map__[name]
        setattr(self, name, result)
        #self._fixinspection(result, name) 
        return result
    def _deprecated_fixinspection(self, result, name): 
        # modify some attrs to make a class appear at export level 
        if hasattr(result, '__module__'):
            if not result.__module__.startswith('py.__'):
                return   # don't change __module__ nor __name__ for classes
                         # that the py lib re-exports from somewhere else,
                         # e.g. py.builtin.BaseException
            try:
                setattr(result, '__module__', self.__name__)
            except (AttributeError, TypeError):
                pass
        if hasattr(result, '__bases__'):
            try:
                setattr(result, '__name__', name)
            except (AttributeError, TypeError):
                pass
    def __repr__(self):
        return '<Module %r>' % (self.__name__, )
    def getdict(self):
        # force all the content of the module to be loaded when __dict__ is read
        dictdescr = ModuleType.__dict__['__dict__']
        dict = dictdescr.__get__(self)
        if dict is not None:
            if '*' not in self.__map__: 
                for name in self.__map__.keys():
                    hasattr(self, name)   # force attribute to be loaded, ignore errors
                assert not self.__map__, "%r not empty" % self.__map__
            else: 
                fsname = self.__map__['*'][0] 
                dict.update(self.__package__._loadimpl(fsname[:-3]).__dict__)
        return dict
    __dict__ = property(getdict)
    del getdict
# ---------------------------------------------------
# Bootstrap Virtual Module Hierarchy
# ---------------------------------------------------
def initpkg(pkgname, exportdefs, **kw):
    #print "initializing package", pkgname
    # bootstrap Package object
    pkg = Package(pkgname, exportdefs)
    for name, value in kw.items(): 
        setattr(pkg, name, value) 
    seen = { pkgname : pkg.module }
    deferred_imports = []
    for pypath, extpy in pkg.exportitems():
        pyparts = pypath.split('.')
        modparts = pyparts[:]
        if extpy[1] != '*': 
            lastmodpart = modparts.pop()
        else: 
            lastmodpart = '*'
        current = pkgname
        # ensure modules
        for name in modparts:
            previous = current
            current += '.' + name
            if current not in seen:
                seen[current] = mod = Module(pkg, current)
                setattr(seen[previous], name, mod)
                setmodule(current, mod)
        mod = seen[current]
        if not hasattr(mod, '__map__'):
            assert mod is pkg.module, \
                   "only root modules are allowed to be non-lazy. "
            deferred_imports.append((mod, pyparts[-1], extpy))
        else:
            if extpy[1] == '__doc__':
                mod.__doc__ = pkg._resolve(extpy)
            else:
                mod.__map__[lastmodpart] = extpy 
    for mod, pypart, extpy in deferred_imports: 
        setattr(mod, pypart, pkg._resolve(extpy))