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

190 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +0000

1""" 

2 pygments.lexers 

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

4 

5 Pygments lexers. 

6 

7 :copyright: Copyright 2006-2023 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 'LeanLexer': 'Lean3Lexer', 

26} 

27 

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

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

30 

31_lexer_cache = {} 

32_pattern_cache = {} 

33 

34 

35def _fn_matches(fn, glob): 

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

37 if glob not in _pattern_cache: 

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

39 return pattern.match(fn) 

40 return _pattern_cache[glob].match(fn) 

41 

42 

43def _load_lexers(module_name): 

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

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

46 for lexer_name in mod.__all__: 

47 cls = getattr(mod, lexer_name) 

48 _lexer_cache[cls.name] = cls 

49 

50 

51def get_all_lexers(plugins=True): 

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

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

54 

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

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

57 """ 

58 for item in LEXERS.values(): 

59 yield item[1:] 

60 if plugins: 

61 for lexer in find_plugin_lexers(): 

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

63 

64 

65def find_lexer_class(name): 

66 """ 

67 Return the `Lexer` subclass that with the *name* attribute as given by 

68 the *name* argument. 

69 """ 

70 if name in _lexer_cache: 

71 return _lexer_cache[name] 

72 # lookup builtin lexers 

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

74 if name == lname: 

75 _load_lexers(module_name) 

76 return _lexer_cache[name] 

77 # continue with lexers from setuptools entrypoints 

78 for cls in find_plugin_lexers(): 

79 if cls.name == name: 

80 return cls 

81 

82 

83def find_lexer_class_by_name(_alias): 

84 """ 

85 Return the `Lexer` subclass that has `alias` in its aliases list, without 

86 instantiating it. 

87 

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

89 

90 Will raise :exc:`pygments.util.ClassNotFound` if no lexer with that alias is 

91 found. 

92 

93 .. versionadded:: 2.2 

94 """ 

95 if not _alias: 

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

97 # lookup builtin lexers 

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

99 if _alias.lower() in aliases: 

100 if name not in _lexer_cache: 

101 _load_lexers(module_name) 

102 return _lexer_cache[name] 

103 # continue with lexers from setuptools entrypoints 

104 for cls in find_plugin_lexers(): 

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

106 return cls 

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

108 

109 

110def get_lexer_by_name(_alias, **options): 

111 """ 

112 Return an instance of a `Lexer` subclass that has `alias` in its 

113 aliases list. The lexer is given the `options` at its 

114 instantiation. 

115 

116 Will raise :exc:`pygments.util.ClassNotFound` if no lexer with that alias is 

117 found. 

118 """ 

119 if not _alias: 

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

121 

122 # lookup builtin lexers 

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

124 if _alias.lower() in aliases: 

125 if name not in _lexer_cache: 

126 _load_lexers(module_name) 

127 return _lexer_cache[name](**options) 

128 # continue with lexers from setuptools entrypoints 

129 for cls in find_plugin_lexers(): 

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

131 return cls(**options) 

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

133 

134 

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

136 """Load a lexer from a file. 

137 

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

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

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

141 as the second argument to this function. 

142 

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

144 is equivalent to running eval on the input file. 

145 

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

147 

148 .. versionadded:: 2.2 

149 """ 

150 try: 

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

152 custom_namespace = {} 

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

154 exec(f.read(), custom_namespace) 

155 # Retrieve the class `lexername` from that namespace 

156 if lexername not in custom_namespace: 

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

158 (lexername, filename)) 

159 lexer_class = custom_namespace[lexername] 

160 # And finally instantiate it with the options 

161 return lexer_class(**options) 

162 except OSError as err: 

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

164 except ClassNotFound: 

165 raise 

166 except Exception as err: 

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

168 

169 

170def find_lexer_class_for_filename(_fn, code=None): 

171 """Get a lexer for a filename. 

172 

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

174 figure out which one is more appropriate. 

175 

176 Returns None if not found. 

177 """ 

178 matches = [] 

179 fn = basename(_fn) 

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

181 for filename in filenames: 

182 if _fn_matches(fn, filename): 

183 if name not in _lexer_cache: 

184 _load_lexers(modname) 

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

186 for cls in find_plugin_lexers(): 

187 for filename in cls.filenames: 

188 if _fn_matches(fn, filename): 

189 matches.append((cls, filename)) 

190 

191 if isinstance(code, bytes): 

192 # decode it, since all analyse_text functions expect unicode 

193 code = guess_decode(code) 

194 

195 def get_rating(info): 

196 cls, filename = info 

197 # explicit patterns get a bonus 

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

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

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

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

202 # to find lexers which need it overridden. 

203 if code: 

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

205 return cls.priority + bonus, cls.__name__ 

206 

207 if matches: 

208 matches.sort(key=get_rating) 

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

210 return matches[-1][0] 

211 

212 

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

214 """Get a lexer for a filename. 

215 

216 Return a `Lexer` subclass instance that has a filename pattern 

217 matching `fn`. The lexer is given the `options` at its 

218 instantiation. 

219 

220 Raise :exc:`pygments.util.ClassNotFound` if no lexer for that filename 

221 is found. 

222 

223 If multiple lexers match the filename pattern, use their ``analyse_text()`` 

224 methods to figure out which one is more appropriate. 

225 """ 

226 res = find_lexer_class_for_filename(_fn, code) 

227 if not res: 

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

229 return res(**options) 

230 

231 

232def get_lexer_for_mimetype(_mime, **options): 

233 """ 

234 Return a `Lexer` subclass instance that has `mime` in its mimetype 

235 list. The lexer is given the `options` at its instantiation. 

236 

237 Will raise :exc:`pygments.util.ClassNotFound` if not lexer for that mimetype 

238 is found. 

239 """ 

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

241 if _mime in mimetypes: 

242 if name not in _lexer_cache: 

243 _load_lexers(modname) 

244 return _lexer_cache[name](**options) 

245 for cls in find_plugin_lexers(): 

246 if _mime in cls.mimetypes: 

247 return cls(**options) 

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

249 

250 

251def _iter_lexerclasses(plugins=True): 

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

253 for key in sorted(LEXERS): 

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

255 if name not in _lexer_cache: 

256 _load_lexers(module_name) 

257 yield _lexer_cache[name] 

258 if plugins: 

259 yield from find_plugin_lexers() 

260 

261 

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

263 """ 

264 As :func:`guess_lexer()`, but only lexers which have a pattern in `filenames` 

265 or `alias_filenames` that matches `filename` are taken into consideration. 

266 

267 :exc:`pygments.util.ClassNotFound` is raised if no lexer thinks it can 

268 handle the content. 

269 """ 

270 fn = basename(_fn) 

271 primary = {} 

272 matching_lexers = set() 

273 for lexer in _iter_lexerclasses(): 

274 for filename in lexer.filenames: 

275 if _fn_matches(fn, filename): 

276 matching_lexers.add(lexer) 

277 primary[lexer] = True 

278 for filename in lexer.alias_filenames: 

279 if _fn_matches(fn, filename): 

280 matching_lexers.add(lexer) 

281 primary[lexer] = False 

282 if not matching_lexers: 

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

284 if len(matching_lexers) == 1: 

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

286 result = [] 

287 for lexer in matching_lexers: 

288 rv = lexer.analyse_text(_text) 

289 if rv == 1.0: 

290 return lexer(**options) 

291 result.append((rv, lexer)) 

292 

293 def type_sort(t): 

294 # sort by: 

295 # - analyse score 

296 # - is primary filename pattern? 

297 # - priority 

298 # - last resort: class name 

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

300 result.sort(key=type_sort) 

301 

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

303 

304 

305def guess_lexer(_text, **options): 

306 """ 

307 Return a `Lexer` subclass instance that's guessed from the text in 

308 `text`. For that, the :meth:`.analyse_text()` method of every known lexer 

309 class is called with the text as argument, and the lexer which returned the 

310 highest value will be instantiated and returned. 

311 

312 :exc:`pygments.util.ClassNotFound` is raised if no lexer thinks it can 

313 handle the content. 

314 """ 

315 

316 if not isinstance(_text, str): 

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

318 if inencoding: 

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

320 else: 

321 _text, _ = guess_decode(_text) 

322 

323 # try to get a vim modeline first 

324 ft = get_filetype_from_buffer(_text) 

325 

326 if ft is not None: 

327 try: 

328 return get_lexer_by_name(ft, **options) 

329 except ClassNotFound: 

330 pass 

331 

332 best_lexer = [0.0, None] 

333 for lexer in _iter_lexerclasses(): 

334 rv = lexer.analyse_text(_text) 

335 if rv == 1.0: 

336 return lexer(**options) 

337 if rv > best_lexer[0]: 

338 best_lexer[:] = (rv, lexer) 

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

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

341 return best_lexer[1](**options) 

342 

343 

344class _automodule(types.ModuleType): 

345 """Automatically import lexers.""" 

346 

347 def __getattr__(self, name): 

348 info = LEXERS.get(name) 

349 if info: 

350 _load_lexers(info[0]) 

351 cls = _lexer_cache[info[1]] 

352 setattr(self, name, cls) 

353 return cls 

354 if name in COMPAT: 

355 return getattr(self, COMPAT[name]) 

356 raise AttributeError(name) 

357 

358 

359oldmod = sys.modules[__name__] 

360newmod = _automodule(__name__) 

361newmod.__dict__.update(oldmod.__dict__) 

362sys.modules[__name__] = newmod 

363del newmod.newmod, newmod.oldmod, newmod.sys, newmod.types