Coverage for sphinxlint/sphinxlint.py: 85%

52 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-11-24 18:46 +0100

1from collections import Counter 

2from dataclasses import dataclass 

3from os.path import splitext 

4from pathlib import Path 

5from typing import Optional 

6 

7from sphinxlint.utils import PER_FILE_CACHES, hide_non_rst_blocks, po2rst 

8 

9 

10@dataclass(frozen=True) 

11class LintError: 

12 """A linting error found by one of the checkers""" 

13 

14 filename: str 

15 line_no: int 

16 msg: str 

17 checker_name: str 

18 

19 def __str__(self): 

20 return f"{self.filename}:{self.line_no}: {self.msg} ({self.checker_name})" 

21 

22 

23class CheckersOptions: 

24 """Configuration options for checkers.""" 

25 

26 max_line_length = 80 

27 

28 @classmethod 

29 def from_argparse(cls, namespace): 

30 options = cls() 

31 options.max_line_length = namespace.max_line_length 

32 return options 

33 

34 

35def check_text(filename, text, checkers, options=None): 

36 if options is None: 

37 options = CheckersOptions() 

38 errors = [] 

39 ext = splitext(filename)[1] 

40 checkers = {checker for checker in checkers if ext in checker.suffixes} 

41 lines = tuple(text.splitlines(keepends=True)) 

42 if any(checker.rst_only for checker in checkers): 

43 lines_with_rst_only = hide_non_rst_blocks(lines) 

44 for check in checkers: 

45 if ext not in check.suffixes: 

46 continue 

47 for lno, msg in check( 

48 filename, lines_with_rst_only if check.rst_only else lines, options 

49 ): 

50 errors.append(LintError(filename, lno, msg, check.name)) 

51 return errors 

52 

53 

54def check_file(filename, checkers, options: Optional[CheckersOptions] = None): 

55 try: 

56 ext = splitext(filename)[1] 

57 if not any(ext in checker.suffixes for checker in checkers): 

58 return Counter() 

59 try: 

60 text = Path(filename).read_text(encoding="UTF-8") 

61 if filename.endswith(".po"): 

62 text = po2rst(text) 

63 except OSError as err: 

64 return [f"{filename}: cannot open: {err}"] 

65 except UnicodeDecodeError as err: 

66 return [f"{filename}: cannot decode as UTF-8: {err}"] 

67 return check_text(filename, text, checkers, options) 

68 finally: 

69 for memoized_function in PER_FILE_CACHES: 

70 memoized_function.cache_clear()