Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/soupsieve/util.py: 35%

54 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-01 06:54 +0000

1"""Utility.""" 

2from __future__ import annotations 

3from functools import wraps, lru_cache 

4import warnings 

5import re 

6from typing import Callable, Any 

7 

8DEBUG = 0x00001 

9 

10RE_PATTERN_LINE_SPLIT = re.compile(r'(?:\r\n|(?!\r\n)[\n\r])|$') 

11 

12UC_A = ord('A') 

13UC_Z = ord('Z') 

14 

15 

16@lru_cache(maxsize=512) 

17def lower(string: str) -> str: 

18 """Lower.""" 

19 

20 new_string = [] 

21 for c in string: 

22 o = ord(c) 

23 new_string.append(chr(o + 32) if UC_A <= o <= UC_Z else c) 

24 return ''.join(new_string) 

25 

26 

27class SelectorSyntaxError(Exception): 

28 """Syntax error in a CSS selector.""" 

29 

30 def __init__(self, msg: str, pattern: str | None = None, index: int | None = None) -> None: 

31 """Initialize.""" 

32 

33 self.line = None 

34 self.col = None 

35 self.context = None 

36 

37 if pattern is not None and index is not None: 

38 # Format pattern to show line and column position 

39 self.context, self.line, self.col = get_pattern_context(pattern, index) 

40 msg = '{}\n line {}:\n{}'.format(msg, self.line, self.context) 

41 

42 super().__init__(msg) 

43 

44 

45def deprecated(message: str, stacklevel: int = 2) -> Callable[..., Any]: # pragma: no cover 

46 """ 

47 Raise a `DeprecationWarning` when wrapped function/method is called. 

48 

49 Usage: 

50 

51 @deprecated("This method will be removed in version X; use Y instead.") 

52 def some_method()" 

53 pass 

54 """ 

55 

56 def _wrapper(func: Callable[..., Any]) -> Callable[..., Any]: 

57 @wraps(func) 

58 def _deprecated_func(*args: Any, **kwargs: Any) -> Any: 

59 warnings.warn( 

60 f"'{func.__name__}' is deprecated. {message}", 

61 category=DeprecationWarning, 

62 stacklevel=stacklevel 

63 ) 

64 return func(*args, **kwargs) 

65 return _deprecated_func 

66 return _wrapper 

67 

68 

69def warn_deprecated(message: str, stacklevel: int = 2) -> None: # pragma: no cover 

70 """Warn deprecated.""" 

71 

72 warnings.warn( 

73 message, 

74 category=DeprecationWarning, 

75 stacklevel=stacklevel 

76 ) 

77 

78 

79def get_pattern_context(pattern: str, index: int) -> tuple[str, int, int]: 

80 """Get the pattern context.""" 

81 

82 last = 0 

83 current_line = 1 

84 col = 1 

85 text = [] # type: list[str] 

86 line = 1 

87 offset = None # type: int | None 

88 

89 # Split pattern by newline and handle the text before the newline 

90 for m in RE_PATTERN_LINE_SPLIT.finditer(pattern): 

91 linetext = pattern[last:m.start(0)] 

92 if not len(m.group(0)) and not len(text): 

93 indent = '' 

94 offset = -1 

95 col = index - last + 1 

96 elif last <= index < m.end(0): 

97 indent = '--> ' 

98 offset = (-1 if index > m.start(0) else 0) + 3 

99 col = index - last + 1 

100 else: 

101 indent = ' ' 

102 offset = None 

103 if len(text): 

104 # Regardless of whether we are presented with `\r\n`, `\r`, or `\n`, 

105 # we will render the output with just `\n`. We will still log the column 

106 # correctly though. 

107 text.append('\n') 

108 text.append('{}{}'.format(indent, linetext)) 

109 if offset is not None: 

110 text.append('\n') 

111 text.append(' ' * (col + offset) + '^') 

112 line = current_line 

113 

114 current_line += 1 

115 last = m.end(0) 

116 

117 return ''.join(text), line, col