pytest.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. """ py.test plugin for checking files with pylama. """
  2. from __future__ import absolute_import
  3. import pathlib
  4. from os import path as op
  5. import pytest
  6. from pylama.config import CURDIR
  7. from pylama.main import DEFAULT_FORMAT, check_paths, parse_options
  8. HISTKEY = "pylama/mtimes"
  9. def pytest_load_initial_conftests(early_config, *_):
  10. # Marks have to be registered before usage
  11. # to not fail with --strict command line argument
  12. early_config.addinivalue_line(
  13. "markers", "pycodestyle: Mark test as using pylama code audit tool."
  14. )
  15. def pytest_addoption(parser):
  16. group = parser.getgroup("general")
  17. group.addoption(
  18. "--pylama",
  19. action="store_true",
  20. help="perform some pylama code checks on .py files",
  21. )
  22. def pytest_sessionstart(session):
  23. config = session.config
  24. if config.option.pylama and getattr(config, "cache", None):
  25. config._pylamamtimes = config.cache.get(HISTKEY, {})
  26. def pytest_sessionfinish(session):
  27. config = session.config
  28. if hasattr(config, "_pylamamtimes"):
  29. config.cache.set(HISTKEY, config._pylamamtimes)
  30. def pytest_collect_file(path, parent):
  31. config = parent.config
  32. if config.option.pylama and path.ext == ".py":
  33. return PylamaFile.from_parent(parent, path=pathlib.Path(path))
  34. return None
  35. class PylamaError(Exception):
  36. """indicates an error during pylama checks."""
  37. class PylamaFile(pytest.File):
  38. def collect(self):
  39. return [PylamaItem.from_parent(self, name="pylama")]
  40. class PylamaItem(pytest.Item):
  41. def __init__(self, *args, **kwargs):
  42. super().__init__(*args, **kwargs)
  43. self.add_marker("pycodestyle")
  44. self.cache = None
  45. self._pylamamtimes = None
  46. def setup(self):
  47. if not getattr(self.config, "cache", None):
  48. return False
  49. self.cache = True
  50. self._pylamamtimes = self.fspath.mtime()
  51. pylamamtimes = self.config._pylamamtimes
  52. old = pylamamtimes.get(str(self.fspath), 0)
  53. if old == self._pylamamtimes:
  54. pytest.skip("file(s) previously passed Pylama checks")
  55. return True
  56. def runtest(self):
  57. errors = check_file(self.fspath)
  58. if errors:
  59. out = "\n".join(err.format(DEFAULT_FORMAT) for err in errors)
  60. raise PylamaError(out)
  61. # update mtime only if test passed
  62. # otherwise failures would not be re-run next time
  63. if self.cache:
  64. self.config._pylamamtimes[str(self.fspath)] = self._pylamamtimes
  65. def repr_failure(self, excinfo, style=None):
  66. if excinfo.errisinstance(PylamaError):
  67. return excinfo.value.args[0]
  68. return super().repr_failure(excinfo, style)
  69. def check_file(path):
  70. options = parse_options()
  71. path = op.relpath(str(path), CURDIR)
  72. return check_paths([path], options, rootdir=CURDIR)
  73. # pylama:ignore=D,E1002,W0212,F0001,C0115,C0116