Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

2apipkg: control the exported namespace of a Python package. 

3 

4see https://pypi.python.org/pypi/apipkg 

5 

6(c) holger krekel, 2009 - MIT license 

7""" 

8import os 

9import sys 

10from types import ModuleType 

11 

12from .version import version as __version__ 

13 

14 

15def _py_abspath(path): 

16 """ 

17 special version of abspath 

18 that will leave paths from jython jars alone 

19 """ 

20 if path.startswith('__pyclasspath__'): 

21 

22 return path 

23 else: 

24 return os.path.abspath(path) 

25 

26 

27def distribution_version(name): 

28 """try to get the version of the named distribution, 

29 returs None on failure""" 

30 from pkg_resources import get_distribution, DistributionNotFound 

31 try: 

32 dist = get_distribution(name) 

33 except DistributionNotFound: 

34 pass 

35 else: 

36 return dist.version 

37 

38 

39def initpkg(pkgname, exportdefs, attr=None, eager=False): 

40 """ initialize given package from the export definitions. """ 

41 attr = attr or {} 

42 oldmod = sys.modules.get(pkgname) 

43 d = {} 

44 f = getattr(oldmod, '__file__', None) 

45 if f: 

46 f = _py_abspath(f) 

47 d['__file__'] = f 

48 if hasattr(oldmod, '__version__'): 

49 d['__version__'] = oldmod.__version__ 

50 if hasattr(oldmod, '__loader__'): 

51 d['__loader__'] = oldmod.__loader__ 

52 if hasattr(oldmod, '__path__'): 

53 d['__path__'] = [_py_abspath(p) for p in oldmod.__path__] 

54 if hasattr(oldmod, '__package__'): 

55 d['__package__'] = oldmod.__package__ 

56 if '__doc__' not in exportdefs and getattr(oldmod, '__doc__', None): 

57 d['__doc__'] = oldmod.__doc__ 

58 d.update(attr) 

59 if hasattr(oldmod, "__dict__"): 

60 oldmod.__dict__.update(d) 

61 mod = ApiModule(pkgname, exportdefs, implprefix=pkgname, attr=d) 

62 sys.modules[pkgname] = mod 

63 # eagerload in bypthon to avoid their monkeypatching breaking packages 

64 if 'bpython' in sys.modules or eager: 

65 for module in list(sys.modules.values()): 

66 if isinstance(module, ApiModule): 

67 module.__dict__ 

68 

69 

70def importobj(modpath, attrname): 

71 """imports a module, then resolves the attrname on it""" 

72 module = __import__(modpath, None, None, ['__doc__']) 

73 if not attrname: 

74 return module 

75 

76 retval = module 

77 names = attrname.split(".") 

78 for x in names: 

79 retval = getattr(retval, x) 

80 return retval 

81 

82 

83class ApiModule(ModuleType): 

84 """the magical lazy-loading module standing""" 

85 def __docget(self): 

86 try: 

87 return self.__doc 

88 except AttributeError: 

89 if '__doc__' in self.__map__: 

90 return self.__makeattr('__doc__') 

91 

92 def __docset(self, value): 

93 self.__doc = value 

94 __doc__ = property(__docget, __docset) 

95 

96 def __init__(self, name, importspec, implprefix=None, attr=None): 

97 self.__name__ = name 

98 self.__all__ = [x for x in importspec if x != '__onfirstaccess__'] 

99 self.__map__ = {} 

100 self.__implprefix__ = implprefix or name 

101 if attr: 

102 for name, val in attr.items(): 

103 # print "setting", self.__name__, name, val 

104 setattr(self, name, val) 

105 for name, importspec in importspec.items(): 

106 if isinstance(importspec, dict): 

107 subname = '%s.%s' % (self.__name__, name) 

108 apimod = ApiModule(subname, importspec, implprefix) 

109 sys.modules[subname] = apimod 

110 setattr(self, name, apimod) 

111 else: 

112 parts = importspec.split(':') 

113 modpath = parts.pop(0) 

114 attrname = parts and parts[0] or "" 

115 if modpath[0] == '.': 

116 modpath = implprefix + modpath 

117 

118 if not attrname: 

119 subname = '%s.%s' % (self.__name__, name) 

120 apimod = AliasModule(subname, modpath) 

121 sys.modules[subname] = apimod 

122 if '.' not in name: 

123 setattr(self, name, apimod) 

124 else: 

125 self.__map__[name] = (modpath, attrname) 

126 

127 def __repr__(self): 

128 repr_list = [] 

129 if hasattr(self, '__version__'): 

130 repr_list.append("version=" + repr(self.__version__)) 

131 if hasattr(self, '__file__'): 

132 repr_list.append('from ' + repr(self.__file__)) 

133 if repr_list: 

134 return '<ApiModule %r %s>' % (self.__name__, " ".join(repr_list)) 

135 return '<ApiModule %r>' % (self.__name__,) 

136 

137 def __makeattr(self, name): 

138 """lazily compute value for name or raise AttributeError if unknown.""" 

139 # print "makeattr", self.__name__, name 

140 target = None 

141 if '__onfirstaccess__' in self.__map__: 

142 target = self.__map__.pop('__onfirstaccess__') 

143 importobj(*target)() 

144 try: 

145 modpath, attrname = self.__map__[name] 

146 except KeyError: 

147 if target is not None and name != '__onfirstaccess__': 

148 # retry, onfirstaccess might have set attrs 

149 return getattr(self, name) 

150 raise AttributeError(name) 

151 else: 

152 result = importobj(modpath, attrname) 

153 setattr(self, name, result) 

154 try: 

155 del self.__map__[name] 

156 except KeyError: 

157 pass # in a recursive-import situation a double-del can happen 

158 return result 

159 

160 __getattr__ = __makeattr 

161 

162 @property 

163 def __dict__(self): 

164 # force all the content of the module 

165 # to be loaded when __dict__ is read 

166 dictdescr = ModuleType.__dict__['__dict__'] 

167 dict = dictdescr.__get__(self) 

168 if dict is not None: 

169 hasattr(self, 'some') 

170 for name in self.__all__: 

171 try: 

172 self.__makeattr(name) 

173 except AttributeError: 

174 pass 

175 return dict 

176 

177 

178def AliasModule(modname, modpath, attrname=None): 

179 mod = [] 

180 

181 def getmod(): 

182 if not mod: 

183 x = importobj(modpath, None) 

184 if attrname is not None: 

185 x = getattr(x, attrname) 

186 mod.append(x) 

187 return mod[0] 

188 

189 class AliasModule(ModuleType): 

190 

191 def __repr__(self): 

192 x = modpath 

193 if attrname: 

194 x += "." + attrname 

195 return '<AliasModule %r for %r>' % (modname, x) 

196 

197 def __getattribute__(self, name): 

198 try: 

199 return getattr(getmod(), name) 

200 except ImportError: 

201 return None 

202 

203 def __setattr__(self, name, value): 

204 setattr(getmod(), name, value) 

205 

206 def __delattr__(self, name): 

207 delattr(getmod(), name) 

208 

209 return AliasModule(str(modname))