|
1 """ |
|
2 apipkg: control the exported namespace of a python package. |
|
3 |
|
4 see http://pypi.python.org/pypi/apipkg |
|
5 |
|
6 (c) holger krekel, 2009 - MIT license |
|
7 """ |
|
8 import os |
|
9 import sys |
|
10 from types import ModuleType |
|
11 |
|
12 __version__ = '1.2.dev5' |
|
13 |
|
14 def initpkg(pkgname, exportdefs, attr=dict()): |
|
15 """ initialize given package from the export definitions. """ |
|
16 oldmod = sys.modules.get(pkgname) |
|
17 d = {} |
|
18 f = getattr(oldmod, '__file__', None) |
|
19 if f: |
|
20 f = os.path.abspath(f) |
|
21 d['__file__'] = f |
|
22 if hasattr(oldmod, '__version__'): |
|
23 d['__version__'] = oldmod.__version__ |
|
24 if hasattr(oldmod, '__loader__'): |
|
25 d['__loader__'] = oldmod.__loader__ |
|
26 if hasattr(oldmod, '__path__'): |
|
27 d['__path__'] = [os.path.abspath(p) for p in oldmod.__path__] |
|
28 if '__doc__' not in exportdefs and getattr(oldmod, '__doc__', None): |
|
29 d['__doc__'] = oldmod.__doc__ |
|
30 d.update(attr) |
|
31 if hasattr(oldmod, "__dict__"): |
|
32 oldmod.__dict__.update(d) |
|
33 mod = ApiModule(pkgname, exportdefs, implprefix=pkgname, attr=d) |
|
34 sys.modules[pkgname] = mod |
|
35 |
|
36 def importobj(modpath, attrname): |
|
37 module = __import__(modpath, None, None, ['__doc__']) |
|
38 if not attrname: |
|
39 return module |
|
40 |
|
41 retval = module |
|
42 names = attrname.split(".") |
|
43 for x in names: |
|
44 retval = getattr(retval, x) |
|
45 return retval |
|
46 |
|
47 class ApiModule(ModuleType): |
|
48 def __docget(self): |
|
49 try: |
|
50 return self.__doc |
|
51 except AttributeError: |
|
52 if '__doc__' in self.__map__: |
|
53 return self.__makeattr('__doc__') |
|
54 def __docset(self, value): |
|
55 self.__doc = value |
|
56 __doc__ = property(__docget, __docset) |
|
57 |
|
58 def __init__(self, name, importspec, implprefix=None, attr=None): |
|
59 self.__name__ = name |
|
60 self.__all__ = [x for x in importspec if x != '__onfirstaccess__'] |
|
61 self.__map__ = {} |
|
62 self.__implprefix__ = implprefix or name |
|
63 if attr: |
|
64 for name, val in attr.items(): |
|
65 #print "setting", self.__name__, name, val |
|
66 setattr(self, name, val) |
|
67 for name, importspec in importspec.items(): |
|
68 if isinstance(importspec, dict): |
|
69 subname = '%s.%s'%(self.__name__, name) |
|
70 apimod = ApiModule(subname, importspec, implprefix) |
|
71 sys.modules[subname] = apimod |
|
72 setattr(self, name, apimod) |
|
73 else: |
|
74 parts = importspec.split(':') |
|
75 modpath = parts.pop(0) |
|
76 attrname = parts and parts[0] or "" |
|
77 if modpath[0] == '.': |
|
78 modpath = implprefix + modpath |
|
79 |
|
80 if not attrname: |
|
81 subname = '%s.%s'%(self.__name__, name) |
|
82 apimod = AliasModule(subname, modpath) |
|
83 sys.modules[subname] = apimod |
|
84 if '.' not in name: |
|
85 setattr(self, name, apimod) |
|
86 else: |
|
87 self.__map__[name] = (modpath, attrname) |
|
88 |
|
89 def __repr__(self): |
|
90 l = [] |
|
91 if hasattr(self, '__version__'): |
|
92 l.append("version=" + repr(self.__version__)) |
|
93 if hasattr(self, '__file__'): |
|
94 l.append('from ' + repr(self.__file__)) |
|
95 if l: |
|
96 return '<ApiModule %r %s>' % (self.__name__, " ".join(l)) |
|
97 return '<ApiModule %r>' % (self.__name__,) |
|
98 |
|
99 def __makeattr(self, name): |
|
100 """lazily compute value for name or raise AttributeError if unknown.""" |
|
101 #print "makeattr", self.__name__, name |
|
102 target = None |
|
103 if '__onfirstaccess__' in self.__map__: |
|
104 target = self.__map__.pop('__onfirstaccess__') |
|
105 importobj(*target)() |
|
106 try: |
|
107 modpath, attrname = self.__map__[name] |
|
108 except KeyError: |
|
109 if target is not None and name != '__onfirstaccess__': |
|
110 # retry, onfirstaccess might have set attrs |
|
111 return getattr(self, name) |
|
112 raise AttributeError(name) |
|
113 else: |
|
114 result = importobj(modpath, attrname) |
|
115 setattr(self, name, result) |
|
116 try: |
|
117 del self.__map__[name] |
|
118 except KeyError: |
|
119 pass # in a recursive-import situation a double-del can happen |
|
120 return result |
|
121 |
|
122 __getattr__ = __makeattr |
|
123 |
|
124 def __dict__(self): |
|
125 # force all the content of the module to be loaded when __dict__ is read |
|
126 dictdescr = ModuleType.__dict__['__dict__'] |
|
127 dict = dictdescr.__get__(self) |
|
128 if dict is not None: |
|
129 hasattr(self, 'some') |
|
130 for name in self.__all__: |
|
131 try: |
|
132 self.__makeattr(name) |
|
133 except AttributeError: |
|
134 pass |
|
135 return dict |
|
136 __dict__ = property(__dict__) |
|
137 |
|
138 |
|
139 def AliasModule(modname, modpath): |
|
140 mod = [] |
|
141 |
|
142 def getmod(): |
|
143 if not mod: |
|
144 mod.append(importobj(modpath, None)) |
|
145 return mod[0] |
|
146 |
|
147 class AliasModule(ModuleType): |
|
148 |
|
149 def __repr__(self): |
|
150 return '<AliasModule %r for %r>' % (modname, modpath) |
|
151 |
|
152 def __getattribute__(self, name): |
|
153 return getattr(getmod(), name) |
|
154 |
|
155 def __setattr__(self, name, value): |
|
156 setattr(getmod(), name, value) |
|
157 |
|
158 def __delattr__(self, name): |
|
159 delattr(getmod(), name) |
|
160 |
|
161 return AliasModule(modname) |