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
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 07:45 +0000
1"""
2 pygments.lexers
3 ~~~~~~~~~~~~~~~
5 Pygments lexers.
7 :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
8 :license: BSD, see LICENSE for details.
9"""
11import re
12import sys
13import types
14import fnmatch
15from os.path import basename
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
22COMPAT = {
23 'Python3Lexer': 'PythonLexer',
24 'Python3TracebackLexer': 'PythonTracebackLexer',
25}
27__all__ = ['get_lexer_by_name', 'get_lexer_for_filename', 'find_lexer_class',
28 'guess_lexer', 'load_lexer_from_file'] + list(LEXERS) + list(COMPAT)
30_lexer_cache = {}
31_pattern_cache = {}
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)
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
50def get_all_lexers(plugins=True):
51 """Return a generator of tuples in the form ``(name, aliases,
52 filenames, mimetypes)`` of all know lexers.
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
64def find_lexer_class(name):
65 """Lookup a lexer class by name.
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
82def find_lexer_class_by_name(_alias):
83 """Lookup a lexer class by alias.
85 Like `get_lexer_by_name`, but does not instantiate the class.
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)
104def get_lexer_by_name(_alias, **options):
105 """Get a lexer by an alias.
107 Raises ClassNotFound if not found.
108 """
109 if not _alias:
110 raise ClassNotFound('no lexer for alias %r found' % _alias)
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)
125def load_lexer_from_file(filename, lexername="CustomLexer", **options):
126 """Load a lexer from a file.
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.
133 Users should be very careful with the input, because this method
134 is equivalent to running eval on the input file.
136 Raises ClassNotFound if there are any problems importing the Lexer.
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)
160def find_lexer_class_for_filename(_fn, code=None):
161 """Get a lexer for a filename.
163 If multiple lexers match the filename pattern, use ``analyse_text()`` to
164 figure out which one is more appropriate.
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))
181 if isinstance(code, bytes):
182 # decode it, since all analyse_text functions expect unicode
183 code = guess_decode(code)
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__
197 if matches:
198 matches.sort(key=get_rating)
199 # print "Possible lexers, after sort:", matches
200 return matches[-1][0]
203def get_lexer_for_filename(_fn, code=None, **options):
204 """Get a lexer for a filename.
206 If multiple lexers match the filename pattern, use ``analyse_text()`` to
207 figure out which one is more appropriate.
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)
217def get_lexer_for_mimetype(_mime, **options):
218 """Get a lexer for a mimetype.
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)
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()
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.
250 usage::
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))
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)
292 return result[-1][1](**options)
295def guess_lexer(_text, **options):
296 """Guess a lexer by strong distinctions in the text (eg, shebang)."""
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)
305 # try to get a vim modeline first
306 ft = get_filetype_from_buffer(_text)
308 if ft is not None:
309 try:
310 return get_lexer_by_name(ft, **options)
311 except ClassNotFound:
312 pass
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)
326class _automodule(types.ModuleType):
327 """Automatically import lexers."""
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)
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