Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pygments/lexers/__init__.py: 43%

190 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 07:45 +0000

1""" 

2 pygments.lexers 

3 ~~~~~~~~~~~~~~~ 

4 

5 Pygments lexers. 

6 

7 :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS. 

8 :license: BSD, see LICENSE for details. 

9""" 

10 

11import re 

12import sys 

13import types 

14import fnmatch 

15from os.path import basename 

16 

17from pygments.lexers._mapping import LEXERS 

18from pygments.modeline import get_filetype_from_buffer 

19from pygments.plugin import find_plugin_lexers 

20from pygments.util import ClassNotFound, guess_decode 

21 

22COMPAT = { 

23 'Python3Lexer': 'PythonLexer', 

24 'Python3TracebackLexer': 'PythonTracebackLexer', 

25} 

26 

27__all__ = ['get_lexer_by_name', 'get_lexer_for_filename', 'find_lexer_class', 

28 'guess_lexer', 'load_lexer_from_file'] + list(LEXERS) + list(COMPAT) 

29 

30_lexer_cache = {} 

31_pattern_cache = {} 

32 

33 

34def _fn_matches(fn, glob): 

35 """Return whether the supplied file name fn matches pattern filename.""" 

36 if glob not in _pattern_cache: 

37 pattern = _pattern_cache[glob] = re.compile(fnmatch.translate(glob)) 

38 return pattern.match(fn) 

39 return _pattern_cache[glob].match(fn) 

40 

41 

42def _load_lexers(module_name): 

43 """Load a lexer (and all others in the module too).""" 

44 mod = __import__(module_name, None, None, ['__all__']) 

45 for lexer_name in mod.__all__: 

46 cls = getattr(mod, lexer_name) 

47 _lexer_cache[cls.name] = cls 

48 

49 

50def get_all_lexers(plugins=True): 

51 """Return a generator of tuples in the form ``(name, aliases, 

52 filenames, mimetypes)`` of all know lexers. 

53 

54 If *plugins* is true (the default), plugin lexers supplied by entrypoints 

55 are also returned. Otherwise, only builtin ones are considered. 

56 """ 

57 for item in LEXERS.values(): 

58 yield item[1:] 

59 if plugins: 

60 for lexer in find_plugin_lexers(): 

61 yield lexer.name, lexer.aliases, lexer.filenames, lexer.mimetypes 

62 

63 

64def find_lexer_class(name): 

65 """Lookup a lexer class by name. 

66 

67 Return None if not found. 

68 """ 

69 if name in _lexer_cache: 

70 return _lexer_cache[name] 

71 # lookup builtin lexers 

72 for module_name, lname, aliases, _, _ in LEXERS.values(): 

73 if name == lname: 

74 _load_lexers(module_name) 

75 return _lexer_cache[name] 

76 # continue with lexers from setuptools entrypoints 

77 for cls in find_plugin_lexers(): 

78 if cls.name == name: 

79 return cls 

80 

81 

82def find_lexer_class_by_name(_alias): 

83 """Lookup a lexer class by alias. 

84 

85 Like `get_lexer_by_name`, but does not instantiate the class. 

86 

87 .. versionadded:: 2.2 

88 """ 

89 if not _alias: 

90 raise ClassNotFound('no lexer for alias %r found' % _alias) 

91 # lookup builtin lexers 

92 for module_name, name, aliases, _, _ in LEXERS.values(): 

93 if _alias.lower() in aliases: 

94 if name not in _lexer_cache: 

95 _load_lexers(module_name) 

96 return _lexer_cache[name] 

97 # continue with lexers from setuptools entrypoints 

98 for cls in find_plugin_lexers(): 

99 if _alias.lower() in cls.aliases: 

100 return cls 

101 raise ClassNotFound('no lexer for alias %r found' % _alias) 

102 

103 

104def get_lexer_by_name(_alias, **options): 

105 """Get a lexer by an alias. 

106 

107 Raises ClassNotFound if not found. 

108 """ 

109 if not _alias: 

110 raise ClassNotFound('no lexer for alias %r found' % _alias) 

111 

112 # lookup builtin lexers 

113 for module_name, name, aliases, _, _ in LEXERS.values(): 

114 if _alias.lower() in aliases: 

115 if name not in _lexer_cache: 

116 _load_lexers(module_name) 

117 return _lexer_cache[name](**options) 

118 # continue with lexers from setuptools entrypoints 

119 for cls in find_plugin_lexers(): 

120 if _alias.lower() in cls.aliases: 

121 return cls(**options) 

122 raise ClassNotFound('no lexer for alias %r found' % _alias) 

123 

124 

125def load_lexer_from_file(filename, lexername="CustomLexer", **options): 

126 """Load a lexer from a file. 

127 

128 This method expects a file located relative to the current working 

129 directory, which contains a Lexer class. By default, it expects the 

130 Lexer to be name CustomLexer; you can specify your own class name 

131 as the second argument to this function. 

132 

133 Users should be very careful with the input, because this method 

134 is equivalent to running eval on the input file. 

135 

136 Raises ClassNotFound if there are any problems importing the Lexer. 

137 

138 .. versionadded:: 2.2 

139 """ 

140 try: 

141 # This empty dict will contain the namespace for the exec'd file 

142 custom_namespace = {} 

143 with open(filename, 'rb') as f: 

144 exec(f.read(), custom_namespace) 

145 # Retrieve the class `lexername` from that namespace 

146 if lexername not in custom_namespace: 

147 raise ClassNotFound('no valid %s class found in %s' % 

148 (lexername, filename)) 

149 lexer_class = custom_namespace[lexername] 

150 # And finally instantiate it with the options 

151 return lexer_class(**options) 

152 except OSError as err: 

153 raise ClassNotFound('cannot read %s: %s' % (filename, err)) 

154 except ClassNotFound: 

155 raise 

156 except Exception as err: 

157 raise ClassNotFound('error when loading custom lexer: %s' % err) 

158 

159 

160def find_lexer_class_for_filename(_fn, code=None): 

161 """Get a lexer for a filename. 

162 

163 If multiple lexers match the filename pattern, use ``analyse_text()`` to 

164 figure out which one is more appropriate. 

165 

166 Returns None if not found. 

167 """ 

168 matches = [] 

169 fn = basename(_fn) 

170 for modname, name, _, filenames, _ in LEXERS.values(): 

171 for filename in filenames: 

172 if _fn_matches(fn, filename): 

173 if name not in _lexer_cache: 

174 _load_lexers(modname) 

175 matches.append((_lexer_cache[name], filename)) 

176 for cls in find_plugin_lexers(): 

177 for filename in cls.filenames: 

178 if _fn_matches(fn, filename): 

179 matches.append((cls, filename)) 

180 

181 if isinstance(code, bytes): 

182 # decode it, since all analyse_text functions expect unicode 

183 code = guess_decode(code) 

184 

185 def get_rating(info): 

186 cls, filename = info 

187 # explicit patterns get a bonus 

188 bonus = '*' not in filename and 0.5 or 0 

189 # The class _always_ defines analyse_text because it's included in 

190 # the Lexer class. The default implementation returns None which 

191 # gets turned into 0.0. Run scripts/detect_missing_analyse_text.py 

192 # to find lexers which need it overridden. 

193 if code: 

194 return cls.analyse_text(code) + bonus, cls.__name__ 

195 return cls.priority + bonus, cls.__name__ 

196 

197 if matches: 

198 matches.sort(key=get_rating) 

199 # print "Possible lexers, after sort:", matches 

200 return matches[-1][0] 

201 

202 

203def get_lexer_for_filename(_fn, code=None, **options): 

204 """Get a lexer for a filename. 

205 

206 If multiple lexers match the filename pattern, use ``analyse_text()`` to 

207 figure out which one is more appropriate. 

208 

209 Raises ClassNotFound if not found. 

210 """ 

211 res = find_lexer_class_for_filename(_fn, code) 

212 if not res: 

213 raise ClassNotFound('no lexer for filename %r found' % _fn) 

214 return res(**options) 

215 

216 

217def get_lexer_for_mimetype(_mime, **options): 

218 """Get a lexer for a mimetype. 

219 

220 Raises ClassNotFound if not found. 

221 """ 

222 for modname, name, _, _, mimetypes in LEXERS.values(): 

223 if _mime in mimetypes: 

224 if name not in _lexer_cache: 

225 _load_lexers(modname) 

226 return _lexer_cache[name](**options) 

227 for cls in find_plugin_lexers(): 

228 if _mime in cls.mimetypes: 

229 return cls(**options) 

230 raise ClassNotFound('no lexer for mimetype %r found' % _mime) 

231 

232 

233def _iter_lexerclasses(plugins=True): 

234 """Return an iterator over all lexer classes.""" 

235 for key in sorted(LEXERS): 

236 module_name, name = LEXERS[key][:2] 

237 if name not in _lexer_cache: 

238 _load_lexers(module_name) 

239 yield _lexer_cache[name] 

240 if plugins: 

241 yield from find_plugin_lexers() 

242 

243 

244def guess_lexer_for_filename(_fn, _text, **options): 

245 """ 

246 Lookup all lexers that handle those filenames primary (``filenames``) 

247 or secondary (``alias_filenames``). Then run a text analysis for those 

248 lexers and choose the best result. 

249 

250 usage:: 

251 

252 >>> from pygments.lexers import guess_lexer_for_filename 

253 >>> guess_lexer_for_filename('hello.html', '<%= @foo %>') 

254 <pygments.lexers.templates.RhtmlLexer object at 0xb7d2f32c> 

255 >>> guess_lexer_for_filename('hello.html', '<h1>{{ title|e }}</h1>') 

256 <pygments.lexers.templates.HtmlDjangoLexer object at 0xb7d2f2ac> 

257 >>> guess_lexer_for_filename('style.css', 'a { color: <?= $link ?> }') 

258 <pygments.lexers.templates.CssPhpLexer object at 0xb7ba518c> 

259 """ 

260 fn = basename(_fn) 

261 primary = {} 

262 matching_lexers = set() 

263 for lexer in _iter_lexerclasses(): 

264 for filename in lexer.filenames: 

265 if _fn_matches(fn, filename): 

266 matching_lexers.add(lexer) 

267 primary[lexer] = True 

268 for filename in lexer.alias_filenames: 

269 if _fn_matches(fn, filename): 

270 matching_lexers.add(lexer) 

271 primary[lexer] = False 

272 if not matching_lexers: 

273 raise ClassNotFound('no lexer for filename %r found' % fn) 

274 if len(matching_lexers) == 1: 

275 return matching_lexers.pop()(**options) 

276 result = [] 

277 for lexer in matching_lexers: 

278 rv = lexer.analyse_text(_text) 

279 if rv == 1.0: 

280 return lexer(**options) 

281 result.append((rv, lexer)) 

282 

283 def type_sort(t): 

284 # sort by: 

285 # - analyse score 

286 # - is primary filename pattern? 

287 # - priority 

288 # - last resort: class name 

289 return (t[0], primary[t[1]], t[1].priority, t[1].__name__) 

290 result.sort(key=type_sort) 

291 

292 return result[-1][1](**options) 

293 

294 

295def guess_lexer(_text, **options): 

296 """Guess a lexer by strong distinctions in the text (eg, shebang).""" 

297 

298 if not isinstance(_text, str): 

299 inencoding = options.get('inencoding', options.get('encoding')) 

300 if inencoding: 

301 _text = _text.decode(inencoding or 'utf8') 

302 else: 

303 _text, _ = guess_decode(_text) 

304 

305 # try to get a vim modeline first 

306 ft = get_filetype_from_buffer(_text) 

307 

308 if ft is not None: 

309 try: 

310 return get_lexer_by_name(ft, **options) 

311 except ClassNotFound: 

312 pass 

313 

314 best_lexer = [0.0, None] 

315 for lexer in _iter_lexerclasses(): 

316 rv = lexer.analyse_text(_text) 

317 if rv == 1.0: 

318 return lexer(**options) 

319 if rv > best_lexer[0]: 

320 best_lexer[:] = (rv, lexer) 

321 if not best_lexer[0] or best_lexer[1] is None: 

322 raise ClassNotFound('no lexer matching the text found') 

323 return best_lexer[1](**options) 

324 

325 

326class _automodule(types.ModuleType): 

327 """Automatically import lexers.""" 

328 

329 def __getattr__(self, name): 

330 info = LEXERS.get(name) 

331 if info: 

332 _load_lexers(info[0]) 

333 cls = _lexer_cache[info[1]] 

334 setattr(self, name, cls) 

335 return cls 

336 if name in COMPAT: 

337 return getattr(self, COMPAT[name]) 

338 raise AttributeError(name) 

339 

340 

341oldmod = sys.modules[__name__] 

342newmod = _automodule(__name__) 

343newmod.__dict__.update(oldmod.__dict__) 

344sys.modules[__name__] = newmod 

345del newmod.newmod, newmod.oldmod, newmod.sys, newmod.types