Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pip/_vendor/pygments/lexers/__init__.py: 19%
183 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:48 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:48 +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 sys
12import types
13from fnmatch import fnmatch
14from os.path import basename
16from pip._vendor.pygments.lexers._mapping import LEXERS
17from pip._vendor.pygments.modeline import get_filetype_from_buffer
18from pip._vendor.pygments.plugin import find_plugin_lexers
19from pip._vendor.pygments.util import ClassNotFound, guess_decode
21COMPAT = {
22 'Python3Lexer': 'PythonLexer',
23 'Python3TracebackLexer': 'PythonTracebackLexer',
24}
26__all__ = ['get_lexer_by_name', 'get_lexer_for_filename', 'find_lexer_class',
27 'guess_lexer', 'load_lexer_from_file'] + list(LEXERS) + list(COMPAT)
29_lexer_cache = {}
31def _load_lexers(module_name):
32 """Load a lexer (and all others in the module too)."""
33 mod = __import__(module_name, None, None, ['__all__'])
34 for lexer_name in mod.__all__:
35 cls = getattr(mod, lexer_name)
36 _lexer_cache[cls.name] = cls
39def get_all_lexers(plugins=True):
40 """Return a generator of tuples in the form ``(name, aliases,
41 filenames, mimetypes)`` of all know lexers.
43 If *plugins* is true (the default), plugin lexers supplied by entrypoints
44 are also returned. Otherwise, only builtin ones are considered.
45 """
46 for item in LEXERS.values():
47 yield item[1:]
48 if plugins:
49 for lexer in find_plugin_lexers():
50 yield lexer.name, lexer.aliases, lexer.filenames, lexer.mimetypes
53def find_lexer_class(name):
54 """Lookup a lexer class by name.
56 Return None if not found.
57 """
58 if name in _lexer_cache:
59 return _lexer_cache[name]
60 # lookup builtin lexers
61 for module_name, lname, aliases, _, _ in LEXERS.values():
62 if name == lname:
63 _load_lexers(module_name)
64 return _lexer_cache[name]
65 # continue with lexers from setuptools entrypoints
66 for cls in find_plugin_lexers():
67 if cls.name == name:
68 return cls
71def find_lexer_class_by_name(_alias):
72 """Lookup a lexer class by alias.
74 Like `get_lexer_by_name`, but does not instantiate the class.
76 .. versionadded:: 2.2
77 """
78 if not _alias:
79 raise ClassNotFound('no lexer for alias %r found' % _alias)
80 # lookup builtin lexers
81 for module_name, name, aliases, _, _ in LEXERS.values():
82 if _alias.lower() in aliases:
83 if name not in _lexer_cache:
84 _load_lexers(module_name)
85 return _lexer_cache[name]
86 # continue with lexers from setuptools entrypoints
87 for cls in find_plugin_lexers():
88 if _alias.lower() in cls.aliases:
89 return cls
90 raise ClassNotFound('no lexer for alias %r found' % _alias)
93def get_lexer_by_name(_alias, **options):
94 """Get a lexer by an alias.
96 Raises ClassNotFound if not found.
97 """
98 if not _alias:
99 raise ClassNotFound('no lexer for alias %r found' % _alias)
101 # lookup builtin lexers
102 for module_name, name, aliases, _, _ in LEXERS.values():
103 if _alias.lower() in aliases:
104 if name not in _lexer_cache:
105 _load_lexers(module_name)
106 return _lexer_cache[name](**options)
107 # continue with lexers from setuptools entrypoints
108 for cls in find_plugin_lexers():
109 if _alias.lower() in cls.aliases:
110 return cls(**options)
111 raise ClassNotFound('no lexer for alias %r found' % _alias)
114def load_lexer_from_file(filename, lexername="CustomLexer", **options):
115 """Load a lexer from a file.
117 This method expects a file located relative to the current working
118 directory, which contains a Lexer class. By default, it expects the
119 Lexer to be name CustomLexer; you can specify your own class name
120 as the second argument to this function.
122 Users should be very careful with the input, because this method
123 is equivalent to running eval on the input file.
125 Raises ClassNotFound if there are any problems importing the Lexer.
127 .. versionadded:: 2.2
128 """
129 try:
130 # This empty dict will contain the namespace for the exec'd file
131 custom_namespace = {}
132 with open(filename, 'rb') as f:
133 exec(f.read(), custom_namespace)
134 # Retrieve the class `lexername` from that namespace
135 if lexername not in custom_namespace:
136 raise ClassNotFound('no valid %s class found in %s' %
137 (lexername, filename))
138 lexer_class = custom_namespace[lexername]
139 # And finally instantiate it with the options
140 return lexer_class(**options)
141 except OSError as err:
142 raise ClassNotFound('cannot read %s: %s' % (filename, err))
143 except ClassNotFound:
144 raise
145 except Exception as err:
146 raise ClassNotFound('error when loading custom lexer: %s' % err)
149def find_lexer_class_for_filename(_fn, code=None):
150 """Get a lexer for a filename.
152 If multiple lexers match the filename pattern, use ``analyse_text()`` to
153 figure out which one is more appropriate.
155 Returns None if not found.
156 """
157 matches = []
158 fn = basename(_fn)
159 for modname, name, _, filenames, _ in LEXERS.values():
160 for filename in filenames:
161 if fnmatch(fn, filename):
162 if name not in _lexer_cache:
163 _load_lexers(modname)
164 matches.append((_lexer_cache[name], filename))
165 for cls in find_plugin_lexers():
166 for filename in cls.filenames:
167 if fnmatch(fn, filename):
168 matches.append((cls, filename))
170 if isinstance(code, bytes):
171 # decode it, since all analyse_text functions expect unicode
172 code = guess_decode(code)
174 def get_rating(info):
175 cls, filename = info
176 # explicit patterns get a bonus
177 bonus = '*' not in filename and 0.5 or 0
178 # The class _always_ defines analyse_text because it's included in
179 # the Lexer class. The default implementation returns None which
180 # gets turned into 0.0. Run scripts/detect_missing_analyse_text.py
181 # to find lexers which need it overridden.
182 if code:
183 return cls.analyse_text(code) + bonus, cls.__name__
184 return cls.priority + bonus, cls.__name__
186 if matches:
187 matches.sort(key=get_rating)
188 # print "Possible lexers, after sort:", matches
189 return matches[-1][0]
192def get_lexer_for_filename(_fn, code=None, **options):
193 """Get a lexer for a filename.
195 If multiple lexers match the filename pattern, use ``analyse_text()`` to
196 figure out which one is more appropriate.
198 Raises ClassNotFound if not found.
199 """
200 res = find_lexer_class_for_filename(_fn, code)
201 if not res:
202 raise ClassNotFound('no lexer for filename %r found' % _fn)
203 return res(**options)
206def get_lexer_for_mimetype(_mime, **options):
207 """Get a lexer for a mimetype.
209 Raises ClassNotFound if not found.
210 """
211 for modname, name, _, _, mimetypes in LEXERS.values():
212 if _mime in mimetypes:
213 if name not in _lexer_cache:
214 _load_lexers(modname)
215 return _lexer_cache[name](**options)
216 for cls in find_plugin_lexers():
217 if _mime in cls.mimetypes:
218 return cls(**options)
219 raise ClassNotFound('no lexer for mimetype %r found' % _mime)
222def _iter_lexerclasses(plugins=True):
223 """Return an iterator over all lexer classes."""
224 for key in sorted(LEXERS):
225 module_name, name = LEXERS[key][:2]
226 if name not in _lexer_cache:
227 _load_lexers(module_name)
228 yield _lexer_cache[name]
229 if plugins:
230 yield from find_plugin_lexers()
233def guess_lexer_for_filename(_fn, _text, **options):
234 """
235 Lookup all lexers that handle those filenames primary (``filenames``)
236 or secondary (``alias_filenames``). Then run a text analysis for those
237 lexers and choose the best result.
239 usage::
241 >>> from pygments.lexers import guess_lexer_for_filename
242 >>> guess_lexer_for_filename('hello.html', '<%= @foo %>')
243 <pygments.lexers.templates.RhtmlLexer object at 0xb7d2f32c>
244 >>> guess_lexer_for_filename('hello.html', '<h1>{{ title|e }}</h1>')
245 <pygments.lexers.templates.HtmlDjangoLexer object at 0xb7d2f2ac>
246 >>> guess_lexer_for_filename('style.css', 'a { color: <?= $link ?> }')
247 <pygments.lexers.templates.CssPhpLexer object at 0xb7ba518c>
248 """
249 fn = basename(_fn)
250 primary = {}
251 matching_lexers = set()
252 for lexer in _iter_lexerclasses():
253 for filename in lexer.filenames:
254 if fnmatch(fn, filename):
255 matching_lexers.add(lexer)
256 primary[lexer] = True
257 for filename in lexer.alias_filenames:
258 if fnmatch(fn, filename):
259 matching_lexers.add(lexer)
260 primary[lexer] = False
261 if not matching_lexers:
262 raise ClassNotFound('no lexer for filename %r found' % fn)
263 if len(matching_lexers) == 1:
264 return matching_lexers.pop()(**options)
265 result = []
266 for lexer in matching_lexers:
267 rv = lexer.analyse_text(_text)
268 if rv == 1.0:
269 return lexer(**options)
270 result.append((rv, lexer))
272 def type_sort(t):
273 # sort by:
274 # - analyse score
275 # - is primary filename pattern?
276 # - priority
277 # - last resort: class name
278 return (t[0], primary[t[1]], t[1].priority, t[1].__name__)
279 result.sort(key=type_sort)
281 return result[-1][1](**options)
284def guess_lexer(_text, **options):
285 """Guess a lexer by strong distinctions in the text (eg, shebang)."""
287 if not isinstance(_text, str):
288 inencoding = options.get('inencoding', options.get('encoding'))
289 if inencoding:
290 _text = _text.decode(inencoding or 'utf8')
291 else:
292 _text, _ = guess_decode(_text)
294 # try to get a vim modeline first
295 ft = get_filetype_from_buffer(_text)
297 if ft is not None:
298 try:
299 return get_lexer_by_name(ft, **options)
300 except ClassNotFound:
301 pass
303 best_lexer = [0.0, None]
304 for lexer in _iter_lexerclasses():
305 rv = lexer.analyse_text(_text)
306 if rv == 1.0:
307 return lexer(**options)
308 if rv > best_lexer[0]:
309 best_lexer[:] = (rv, lexer)
310 if not best_lexer[0] or best_lexer[1] is None:
311 raise ClassNotFound('no lexer matching the text found')
312 return best_lexer[1](**options)
315class _automodule(types.ModuleType):
316 """Automatically import lexers."""
318 def __getattr__(self, name):
319 info = LEXERS.get(name)
320 if info:
321 _load_lexers(info[0])
322 cls = _lexer_cache[info[1]]
323 setattr(self, name, cls)
324 return cls
325 if name in COMPAT:
326 return getattr(self, COMPAT[name])
327 raise AttributeError(name)
330oldmod = sys.modules[__name__]
331newmod = _automodule(__name__)
332newmod.__dict__.update(oldmod.__dict__)
333sys.modules[__name__] = newmod
334del newmod.newmod, newmod.oldmod, newmod.sys, newmod.types