discover_files.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. """Functions related to discovering paths."""
  2. import logging
  3. import os.path
  4. from typing import Callable
  5. from typing import Generator
  6. from typing import Sequence
  7. from flake8 import utils
  8. LOG = logging.getLogger(__name__)
  9. def _filenames_from(
  10. arg: str,
  11. *,
  12. predicate: Callable[[str], bool],
  13. ) -> Generator[str, None, None]:
  14. """Generate filenames from an argument.
  15. :param arg:
  16. Parameter from the command-line.
  17. :param predicate:
  18. Predicate to use to filter out filenames. If the predicate
  19. returns ``True`` we will exclude the filename, otherwise we
  20. will yield it. By default, we include every filename
  21. generated.
  22. :returns:
  23. Generator of paths
  24. """
  25. if predicate(arg):
  26. return
  27. if os.path.isdir(arg):
  28. for root, sub_directories, files in os.walk(arg):
  29. # NOTE(sigmavirus24): os.walk() will skip a directory if you
  30. # remove it from the list of sub-directories.
  31. for directory in tuple(sub_directories):
  32. joined = os.path.join(root, directory)
  33. if predicate(joined):
  34. sub_directories.remove(directory)
  35. for filename in files:
  36. joined = os.path.join(root, filename)
  37. if not predicate(joined):
  38. yield joined
  39. else:
  40. yield arg
  41. def expand_paths(
  42. *,
  43. paths: Sequence[str],
  44. stdin_display_name: str,
  45. filename_patterns: Sequence[str],
  46. exclude: Sequence[str],
  47. is_running_from_diff: bool,
  48. ) -> Generator[str, None, None]:
  49. """Expand out ``paths`` from commandline to the lintable files."""
  50. if not paths:
  51. paths = ["."]
  52. def is_excluded(arg: str) -> bool:
  53. if arg == "-":
  54. # if the stdin_display_name is the default, always include it
  55. if stdin_display_name == "stdin":
  56. return False
  57. arg = stdin_display_name
  58. return utils.matches_filename(
  59. arg,
  60. patterns=exclude,
  61. log_message='"%(path)s" has %(whether)sbeen excluded',
  62. logger=LOG,
  63. )
  64. def is_included(arg: str, fname: str) -> bool:
  65. # while running from a diff, the arguments aren't _explicitly_
  66. # listed so we still filter them
  67. if is_running_from_diff:
  68. return utils.fnmatch(fname, filename_patterns)
  69. else:
  70. return (
  71. # always lint `-`
  72. fname == "-"
  73. # always lint explicitly passed (even if not matching filter)
  74. or arg == fname
  75. # otherwise, check the file against filtered patterns
  76. or utils.fnmatch(fname, filename_patterns)
  77. )
  78. return (
  79. filename
  80. for path in paths
  81. for filename in _filenames_from(path, predicate=is_excluded)
  82. if is_included(path, filename)
  83. )