main.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. """Pylama's shell support."""
  2. import sys
  3. import warnings
  4. from json import dumps
  5. from os import path as op
  6. from os import walk
  7. from pathlib import Path
  8. from typing import List, Optional
  9. from pylama.check_async import check_async
  10. from pylama.config import CURDIR, Namespace, parse_options, setup_logger
  11. from pylama.core import LOGGER, run
  12. from pylama.errors import Error
  13. from pylama.utils import read_stdin
  14. DEFAULT_FORMAT = "{filename}:{lnum}:{col} [{etype}] {number} {message} [{source}]"
  15. MESSAGE_FORMATS = {
  16. "pylint": "{filename}:{lnum}: [{etype}] {number} {message} [{source}]",
  17. "pycodestyle": "{filename}:{lnum}:{col} {number} {message} [{source}]",
  18. "parsable": DEFAULT_FORMAT,
  19. }
  20. def check_paths(
  21. paths: Optional[List[str]],
  22. options: Namespace,
  23. code: str = None,
  24. rootdir: Path = None,
  25. ) -> List[Error]:
  26. """Check the given paths.
  27. :param rootdir: Root directory (for making relative file paths)
  28. :param options: Parsed pylama options (from pylama.config.parse_options)
  29. """
  30. paths = paths or options.paths
  31. if not paths:
  32. return []
  33. if code is None:
  34. candidates = []
  35. for path in paths or options.paths:
  36. if not op.exists(path):
  37. continue
  38. if not op.isdir(path):
  39. candidates.append(op.abspath(path))
  40. for root, _, files in walk(path):
  41. candidates += [op.relpath(op.join(root, f), CURDIR) for f in files]
  42. else:
  43. candidates = [paths[0]]
  44. if not candidates:
  45. return []
  46. if rootdir is None:
  47. path = candidates[0]
  48. rootdir = Path(path if op.isdir(path) else op.dirname(path))
  49. candidates = [path for path in candidates if path.endswith(".py")]
  50. if options.concurrent:
  51. return check_async(candidates, code=code, options=options, rootdir=rootdir)
  52. errors = []
  53. for path in candidates:
  54. errors += run(path=path, code=code, rootdir=rootdir, options=options)
  55. return errors
  56. def check_path(
  57. options: Namespace,
  58. rootdir: str = None,
  59. candidates: List[str] = None,
  60. code: str = None, # noqa
  61. ) -> List[Error]:
  62. """Support legacy code."""
  63. warnings.warn(
  64. "pylama.main.check_path is depricated and will be removed in pylama 9",
  65. DeprecationWarning,
  66. )
  67. return check_paths(
  68. candidates,
  69. code=code,
  70. options=options,
  71. rootdir=rootdir and Path(rootdir) or None,
  72. )
  73. def shell(args: List[str] = None, error: bool = True):
  74. """Endpoint for console.
  75. Parse a command arguments, configuration files and run a checkers.
  76. """
  77. if args is None:
  78. args = sys.argv[1:]
  79. options = parse_options(args)
  80. setup_logger(options)
  81. LOGGER.info(options)
  82. # Install VSC hook
  83. if options.hook:
  84. from .hook import install_hook # noqa
  85. for path in options.paths:
  86. return install_hook(path)
  87. if options.from_stdin and not options.paths:
  88. LOGGER.error("--from-stdin requires a filename")
  89. return sys.exit(1)
  90. errors = check_paths(
  91. options.paths,
  92. code=read_stdin() if options.from_stdin else None,
  93. options=options,
  94. rootdir=CURDIR,
  95. )
  96. display_errors(errors, options)
  97. if error:
  98. sys.exit(int(bool(errors)))
  99. return errors
  100. def display_errors(errors: List[Error], options: Namespace):
  101. """Format and display the given errors."""
  102. if options.format == "json":
  103. LOGGER.warning(dumps([err.to_dict() for err in errors]))
  104. else:
  105. pattern = MESSAGE_FORMATS.get(options.format, DEFAULT_FORMAT)
  106. for err in errors:
  107. LOGGER.warning(err.format(pattern))
  108. if __name__ == "__main__":
  109. shell()
  110. # pylama:ignore=F0001