__init__.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. """
  2. pygments.formatters
  3. ~~~~~~~~~~~~~~~~~~~
  4. Pygments formatters.
  5. :copyright: Copyright 2006-2024 by the Pygments team, see AUTHORS.
  6. :license: BSD, see LICENSE for details.
  7. """
  8. import re
  9. import sys
  10. import types
  11. import fnmatch
  12. from os.path import basename
  13. from pygments.formatters._mapping import FORMATTERS
  14. from pygments.plugin import find_plugin_formatters
  15. from pygments.util import ClassNotFound
  16. __all__ = ['get_formatter_by_name', 'get_formatter_for_filename',
  17. 'get_all_formatters', 'load_formatter_from_file'] + list(FORMATTERS)
  18. _formatter_cache = {} # classes by name
  19. _pattern_cache = {}
  20. def _fn_matches(fn, glob):
  21. """Return whether the supplied file name fn matches pattern filename."""
  22. if glob not in _pattern_cache:
  23. pattern = _pattern_cache[glob] = re.compile(fnmatch.translate(glob))
  24. return pattern.match(fn)
  25. return _pattern_cache[glob].match(fn)
  26. def _load_formatters(module_name):
  27. """Load a formatter (and all others in the module too)."""
  28. mod = __import__(module_name, None, None, ['__all__'])
  29. for formatter_name in mod.__all__:
  30. cls = getattr(mod, formatter_name)
  31. _formatter_cache[cls.name] = cls
  32. def get_all_formatters():
  33. """Return a generator for all formatter classes."""
  34. # NB: this returns formatter classes, not info like get_all_lexers().
  35. for info in FORMATTERS.values():
  36. if info[1] not in _formatter_cache:
  37. _load_formatters(info[0])
  38. yield _formatter_cache[info[1]]
  39. for _, formatter in find_plugin_formatters():
  40. yield formatter
  41. def find_formatter_class(alias):
  42. """Lookup a formatter by alias.
  43. Returns None if not found.
  44. """
  45. for module_name, name, aliases, _, _ in FORMATTERS.values():
  46. if alias in aliases:
  47. if name not in _formatter_cache:
  48. _load_formatters(module_name)
  49. return _formatter_cache[name]
  50. for _, cls in find_plugin_formatters():
  51. if alias in cls.aliases:
  52. return cls
  53. def get_formatter_by_name(_alias, **options):
  54. """
  55. Return an instance of a :class:`.Formatter` subclass that has `alias` in its
  56. aliases list. The formatter is given the `options` at its instantiation.
  57. Will raise :exc:`pygments.util.ClassNotFound` if no formatter with that
  58. alias is found.
  59. """
  60. cls = find_formatter_class(_alias)
  61. if cls is None:
  62. raise ClassNotFound(f"no formatter found for name {_alias!r}")
  63. return cls(**options)
  64. def load_formatter_from_file(filename, formattername="CustomFormatter", **options):
  65. """
  66. Return a `Formatter` subclass instance loaded from the provided file, relative
  67. to the current directory.
  68. The file is expected to contain a Formatter class named ``formattername``
  69. (by default, CustomFormatter). Users should be very careful with the input, because
  70. this method is equivalent to running ``eval()`` on the input file. The formatter is
  71. given the `options` at its instantiation.
  72. :exc:`pygments.util.ClassNotFound` is raised if there are any errors loading
  73. the formatter.
  74. .. versionadded:: 2.2
  75. """
  76. try:
  77. # This empty dict will contain the namespace for the exec'd file
  78. custom_namespace = {}
  79. with open(filename, 'rb') as f:
  80. exec(f.read(), custom_namespace)
  81. # Retrieve the class `formattername` from that namespace
  82. if formattername not in custom_namespace:
  83. raise ClassNotFound(f'no valid {formattername} class found in {filename}')
  84. formatter_class = custom_namespace[formattername]
  85. # And finally instantiate it with the options
  86. return formatter_class(**options)
  87. except OSError as err:
  88. raise ClassNotFound(f'cannot read {filename}: {err}')
  89. except ClassNotFound:
  90. raise
  91. except Exception as err:
  92. raise ClassNotFound(f'error when loading custom formatter: {err}')
  93. def get_formatter_for_filename(fn, **options):
  94. """
  95. Return a :class:`.Formatter` subclass instance that has a filename pattern
  96. matching `fn`. The formatter is given the `options` at its instantiation.
  97. Will raise :exc:`pygments.util.ClassNotFound` if no formatter for that filename
  98. is found.
  99. """
  100. fn = basename(fn)
  101. for modname, name, _, filenames, _ in FORMATTERS.values():
  102. for filename in filenames:
  103. if _fn_matches(fn, filename):
  104. if name not in _formatter_cache:
  105. _load_formatters(modname)
  106. return _formatter_cache[name](**options)
  107. for _name, cls in find_plugin_formatters():
  108. for filename in cls.filenames:
  109. if _fn_matches(fn, filename):
  110. return cls(**options)
  111. raise ClassNotFound(f"no formatter found for file name {fn!r}")
  112. class _automodule(types.ModuleType):
  113. """Automatically import formatters."""
  114. def __getattr__(self, name):
  115. info = FORMATTERS.get(name)
  116. if info:
  117. _load_formatters(info[0])
  118. cls = _formatter_cache[info[1]]
  119. setattr(self, name, cls)
  120. return cls
  121. raise AttributeError(name)
  122. oldmod = sys.modules[__name__]
  123. newmod = _automodule(__name__)
  124. newmod.__dict__.update(oldmod.__dict__)
  125. sys.modules[__name__] = newmod
  126. del newmod.newmod, newmod.oldmod, newmod.sys, newmod.types