legacy.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. """Module containing shims around Flake8 2.x behaviour.
  2. Previously, users would import :func:`get_style_guide` from ``flake8.engine``.
  3. In 3.0 we no longer have an "engine" module but we maintain the API from it.
  4. """
  5. from __future__ import annotations
  6. import argparse
  7. import logging
  8. import os.path
  9. from typing import Any
  10. from flake8.discover_files import expand_paths
  11. from flake8.formatting import base as formatter
  12. from flake8.main import application as app
  13. from flake8.options.parse_args import parse_args
  14. LOG = logging.getLogger(__name__)
  15. __all__ = ("get_style_guide",)
  16. class Report:
  17. """Public facing object that mimic's Flake8 2.0's API.
  18. .. note::
  19. There are important changes in how this object behaves compared to
  20. the object provided in Flake8 2.x.
  21. .. warning::
  22. This should not be instantiated by users.
  23. .. versionchanged:: 3.0.0
  24. """
  25. def __init__(self, application: app.Application) -> None:
  26. """Initialize the Report for the user.
  27. .. warning:: This should not be instantiated by users.
  28. """
  29. assert application.guide is not None
  30. self._application = application
  31. self._style_guide = application.guide
  32. self._stats = self._style_guide.stats
  33. @property
  34. def total_errors(self) -> int:
  35. """Return the total number of errors."""
  36. return self._application.result_count
  37. def get_statistics(self, violation: str) -> list[str]:
  38. """Get the list of occurrences of a violation.
  39. :returns:
  40. List of occurrences of a violation formatted as:
  41. {Count} {Error Code} {Message}, e.g.,
  42. ``8 E531 Some error message about the error``
  43. """
  44. return [
  45. f"{s.count} {s.error_code} {s.message}"
  46. for s in self._stats.statistics_for(violation)
  47. ]
  48. class StyleGuide:
  49. """Public facing object that mimic's Flake8 2.0's StyleGuide.
  50. .. note::
  51. There are important changes in how this object behaves compared to
  52. the StyleGuide object provided in Flake8 2.x.
  53. .. warning::
  54. This object should not be instantiated directly by users.
  55. .. versionchanged:: 3.0.0
  56. """
  57. def __init__(self, application: app.Application) -> None:
  58. """Initialize our StyleGuide."""
  59. self._application = application
  60. self._file_checker_manager = application.file_checker_manager
  61. @property
  62. def options(self) -> argparse.Namespace:
  63. """Return application's options.
  64. An instance of :class:`argparse.Namespace` containing parsed options.
  65. """
  66. assert self._application.options is not None
  67. return self._application.options
  68. @property
  69. def paths(self) -> list[str]:
  70. """Return the extra arguments passed as paths."""
  71. assert self._application.options is not None
  72. return self._application.options.filenames
  73. def check_files(self, paths: list[str] | None = None) -> Report:
  74. """Run collected checks on the files provided.
  75. This will check the files passed in and return a :class:`Report`
  76. instance.
  77. :param paths:
  78. List of filenames (or paths) to check.
  79. :returns:
  80. Object that mimic's Flake8 2.0's Reporter class.
  81. """
  82. assert self._application.options is not None
  83. self._application.options.filenames = paths
  84. self._application.run_checks()
  85. self._application.report_errors()
  86. return Report(self._application)
  87. def excluded(self, filename: str, parent: str | None = None) -> bool:
  88. """Determine if a file is excluded.
  89. :param filename:
  90. Path to the file to check if it is excluded.
  91. :param parent:
  92. Name of the parent directory containing the file.
  93. :returns:
  94. True if the filename is excluded, False otherwise.
  95. """
  96. def excluded(path: str) -> bool:
  97. paths = tuple(
  98. expand_paths(
  99. paths=[path],
  100. stdin_display_name=self.options.stdin_display_name,
  101. filename_patterns=self.options.filename,
  102. exclude=self.options.exclude,
  103. )
  104. )
  105. return not paths
  106. return excluded(filename) or (
  107. parent is not None and excluded(os.path.join(parent, filename))
  108. )
  109. def init_report(
  110. self,
  111. reporter: type[formatter.BaseFormatter] | None = None,
  112. ) -> None:
  113. """Set up a formatter for this run of Flake8."""
  114. if reporter is None:
  115. return
  116. if not issubclass(reporter, formatter.BaseFormatter):
  117. raise ValueError(
  118. "Report should be subclass of "
  119. "flake8.formatter.BaseFormatter."
  120. )
  121. self._application.formatter = reporter(self.options)
  122. self._application.guide = None
  123. # NOTE(sigmavirus24): This isn't the intended use of
  124. # Application#make_guide but it works pretty well.
  125. # Stop cringing... I know it's gross.
  126. self._application.make_guide()
  127. self._application.file_checker_manager = None
  128. self._application.make_file_checker_manager([])
  129. def input_file(
  130. self,
  131. filename: str,
  132. lines: Any | None = None,
  133. expected: Any | None = None,
  134. line_offset: Any | None = 0,
  135. ) -> Report:
  136. """Run collected checks on a single file.
  137. This will check the file passed in and return a :class:`Report`
  138. instance.
  139. :param filename:
  140. The path to the file to check.
  141. :param lines:
  142. Ignored since Flake8 3.0.
  143. :param expected:
  144. Ignored since Flake8 3.0.
  145. :param line_offset:
  146. Ignored since Flake8 3.0.
  147. :returns:
  148. Object that mimic's Flake8 2.0's Reporter class.
  149. """
  150. return self.check_files([filename])
  151. def get_style_guide(**kwargs: Any) -> StyleGuide:
  152. r"""Provision a StyleGuide for use.
  153. :param \*\*kwargs:
  154. Keyword arguments that provide some options for the StyleGuide.
  155. :returns:
  156. An initialized StyleGuide
  157. """
  158. application = app.Application()
  159. application.plugins, application.options = parse_args([])
  160. # We basically want application.initialize to be called but with these
  161. # options set instead before we make our formatter, notifier, internal
  162. # style guide and file checker manager.
  163. options = application.options
  164. for key, value in kwargs.items():
  165. try:
  166. getattr(options, key)
  167. setattr(options, key, value)
  168. except AttributeError:
  169. LOG.error('Could not update option "%s"', key)
  170. application.make_formatter()
  171. application.make_guide()
  172. application.make_file_checker_manager([])
  173. return StyleGuide(application)