Coverage for src/pytest_vulture/vulture/manager.py: 0.00%

1"""Manages vulture."""

2

3 import re

4 from pathlib import Path

5 from typing import (

6 List,

7 Optional,

8)

9

10 from vulture import Vulture

11 from vulture.core import Item

12

13 from pytest_vulture.conf.reader import IniReader

14 from pytest_vulture.setup_manager import SetupManager

15 from pytest_vulture.vulture.comment import CommentFinder

16

17

18 class VultureManager:

19 """The vulture manager for pytest."""

20

21 __root_dir: Path

22 _results: List[Item]

23 _config: IniReader

24 _setup_manager: SetupManager

25 _COMMENT_FINDER: CommentFinder = CommentFinder()

26

27 def __init__(self, root_dir: Path, config: IniReader) -> None:

28 """Type of root_dir can be py._path.local.LocalPath. It depends on the version of pytest."""

29 self.__root_dir = root_dir

30 self._config = config

31 self._results = []

32 self.__setup_manager = SetupManager(self._config)

33

34 def call(self) -> None:

35 """Call vulture on a folder

36 Example::

37 >>> import py

38 >>> config = IniReader(path_ini=Path("vulture.ini"), is_pyproject=False)

39 >>> config.read_ini()

40 >>> manager = VultureManager(Path("/tmp"), config)

41 >>> manager.call()

42 """

43 vulture_configuration = self._config.vulture_configuration

44 vulture = Vulture(

45 ignore_names=vulture_configuration.ignore_names,

46 ignore_decorators=vulture_configuration.ignore_decorators,

47 )

48 vulture.scavenge([str(self.__root_dir)], exclude=vulture_configuration.exclude)

49 self._results = [elem for elem in vulture.get_unused_code() if not self.__filter_results(elem)]

50

51 def get_file_errors(self, path: str) -> Optional[str]:

52 """Get file errors

53 Example::

54 >>> import py

55 >>> from pathlib import Path

56 >>> Path("/tmp/package").mkdir(exist_ok=True)

57 >>> Path("/tmp/package/test.py").write_text("def test():pass")

58 15

59 >>> config = IniReader(path_ini=Path("vulture.ini"))

60 >>> config.read_ini()

61 >>> manager = VultureManager(Path("/tmp/package"), config)

62 >>> manager.call()

63 >>> manager.get_file_errors(Path("/tmp/package/test.py"))

64 "line 1 : unused function 'test'"

65 """

66 errors = []

67 for error_vulture in self._results:

68 if self.__path_equals(path, error_vulture.filename.as_posix(), str(self.__root_dir)):

69 errors.append(f"line {error_vulture.first_lineno} : {error_vulture.message}") # noqa: PERF401

70 if not errors:

71 return None

72 return "\n".join(errors)

73

74 def __filter_results(self, vulture_output: Item) -> bool:

75 """Check if the vulture output concerns an ignore path, an entry point or a # vulture: ignore."""

76 for ignored_path in self._config.vulture_configuration.ignore:

77 if self.__check_path(ignored_path, vulture_output.filename):

78 return True

79 return (

80 self.__setup_manager.is_entry_point(vulture_output)

81 or self.__check_ignore_type(vulture_output)

82 or self._COMMENT_FINDER.check_comment(vulture_output)

83 )

84

85 def __check_path(self, path_exclude: str, path_to_check: Path) -> bool:

86 match = re.compile(path_exclude.replace("*", ".*")).match(path_to_check.as_posix()) or re.compile(

87 str(self.__root_dir / path_exclude).replace("*", ".*")

88 ).match(path_to_check.as_posix())

89 if not match:

90 return self.__path_equals(path_exclude, path_to_check.absolute().as_posix(), str(self.__root_dir))

91 return match.group() == path_to_check.as_posix()

92

93 def __check_ignore_type(self, vulture_output: Item) -> bool:

94 return vulture_output.typ in self._config.vulture_configuration.ignore_types

95

96 @classmethod

97 def __path_equals(cls, path_1: str, path_2: str, root_dir: str) -> bool:

98 """Manage tox and pytest weird path usages, to simplify."""

99 path_1_full = (Path(root_dir) / Path(str(path_1))).as_posix()

100 path_2_full = (Path(root_dir) / Path(str(path_2))).as_posix()

101 return path_1_full == path_2_full