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

1import functools 

2import logging 

3import os 

4import py_compile 

5import shutil 

6import sys 

7import tempfile 

8import warnings 

9import pkg_resources 

10 

11try: 

12 from importlib.machinery import SourceFileLoader 

13 from threading import RLock 

14 lock = RLock() 

15 acquire_lock = lock.acquire 

16 release_lock = lock.release 

17 del lock 

18except ImportError: 

19 from imp import acquire_lock, release_lock, load_source 

20 

21 class SourceFileLoader: 

22 def __init__(self, base, filename): 

23 self.base = base 

24 self.filename = filename 

25 

26 def load_module(self): 

27 try: 

28 acquire_lock() 

29 assert self.base not in sys.modules 

30 with open(self.filename, 'rb') as f: 

31 return load_source(self.base, self.filename, f) 

32 finally: 

33 release_lock() 

34 

35log = logging.getLogger('chameleon.loader') 

36 

37from .utils import string_type 

38from .utils import encode_string 

39 

40 

41def cache(func): 

42 def load(self, *args, **kwargs): 

43 template = self.registry.get(args) 

44 if template is None: 

45 self.registry[args] = template = func(self, *args, **kwargs) 

46 return template 

47 return load 

48 

49 

50def abspath_from_asset_spec(spec): 

51 pname, filename = spec.split(':', 1) 

52 return pkg_resources.resource_filename(pname, filename) 

53 

54if os.name == "nt": 

55 def abspath_from_asset_spec(spec, f=abspath_from_asset_spec): 

56 if spec[1] == ":": 

57 return spec 

58 return f(spec) 

59 

60 

61class TemplateLoader(object): 

62 """Template loader class. 

63 

64 To load templates using relative filenames, pass a sequence of 

65 paths (or a single path) as ``search_path``. 

66 

67 To apply a default filename extension to inputs which do not have 

68 an extension already (i.e. no dot), provide this as 

69 ``default_extension`` (e.g. ``'.pt'``). 

70 

71 Additional keyword-arguments will be passed on to the template 

72 constructor. 

73 """ 

74 

75 default_extension = None 

76 

77 def __init__(self, search_path=None, default_extension=None, **kwargs): 

78 if search_path is None: 

79 search_path = [] 

80 if isinstance(search_path, string_type): 

81 search_path = [search_path] 

82 if default_extension is not None: 

83 self.default_extension = ".%s" % default_extension.lstrip('.') 

84 self.search_path = search_path 

85 self.registry = {} 

86 self.kwargs = kwargs 

87 

88 @cache 

89 def load(self, spec, cls=None): 

90 if cls is None: 

91 raise ValueError("Unbound template loader.") 

92 

93 spec = spec.strip() 

94 

95 if self.default_extension is not None and '.' not in spec: 

96 spec += self.default_extension 

97 

98 if ':' in spec: 

99 spec = abspath_from_asset_spec(spec) 

100 

101 if not os.path.isabs(spec): 

102 for path in self.search_path: 

103 path = os.path.join(path, spec) 

104 if os.path.exists(path): 

105 spec = path 

106 break 

107 else: 

108 raise ValueError("Template not found: %s." % spec) 

109 

110 return cls(spec, search_path=self.search_path, **self.kwargs) 

111 

112 def bind(self, cls): 

113 return functools.partial(self.load, cls=cls) 

114 

115 

116class MemoryLoader(object): 

117 def build(self, source, filename): 

118 code = compile(source, filename, 'exec') 

119 env = {} 

120 exec(code, env) 

121 return env 

122 

123 def get(self, name): 

124 return None 

125 

126 

127class ModuleLoader(object): 

128 def __init__(self, path, remove=False): 

129 self.path = path 

130 self.remove = remove 

131 

132 def __del__(self, shutil=shutil): 

133 if not self.remove: 

134 return 

135 try: 

136 shutil.rmtree(self.path) 

137 except: 

138 warnings.warn("Could not clean up temporary file path: %s" % (self.path,)) 

139 

140 def get(self, filename): 

141 path = os.path.join(self.path, filename) 

142 if os.path.exists(path): 

143 log.debug("loading module from cache: %s." % filename) 

144 base, ext = os.path.splitext(filename) 

145 return self._load(base, path) 

146 else: 

147 log.debug('cache miss: %s' % filename) 

148 

149 def build(self, source, filename): 

150 acquire_lock() 

151 try: 

152 d = self.get(filename) 

153 if d is not None: 

154 return d 

155 

156 base, ext = os.path.splitext(filename) 

157 name = os.path.join(self.path, base + ".py") 

158 

159 log.debug("writing source to disk (%d bytes)." % len(source)) 

160 fd, fn = tempfile.mkstemp(prefix=base, suffix='.tmp', dir=self.path) 

161 temp = os.fdopen(fd, 'wb') 

162 encoded = source.encode('utf-8') 

163 header = encode_string("# -*- coding: utf-8 -*-" + "\n") 

164 

165 try: 

166 try: 

167 temp.write(header) 

168 temp.write(encoded) 

169 finally: 

170 temp.close() 

171 except: 

172 os.remove(fn) 

173 raise 

174 

175 os.rename(fn, name) 

176 log.debug("compiling %s into byte-code..." % filename) 

177 py_compile.compile(name) 

178 

179 return self._load(base, name) 

180 finally: 

181 release_lock() 

182 

183 def _load(self, base, filename): 

184 acquire_lock() 

185 try: 

186 module = sys.modules.get(base) 

187 if module is None: 

188 module = SourceFileLoader(base, filename).load_module() 

189 finally: 

190 release_lock() 

191 

192 return module.__dict__