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
« 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
8DEBUG = 0x00001
10RE_PATTERN_LINE_SPLIT = re.compile(r'(?:\r\n|(?!\r\n)[\n\r])|$')
12UC_A = ord('A')
13UC_Z = ord('Z')
16@lru_cache(maxsize=512)
17def lower(string: str) -> str:
18 """Lower."""
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)
27class SelectorSyntaxError(Exception):
28 """Syntax error in a CSS selector."""
30 def __init__(self, msg: str, pattern: str | None = None, index: int | None = None) -> None:
31 """Initialize."""
33 self.line = None
34 self.col = None
35 self.context = None
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)
42 super().__init__(msg)
45def deprecated(message: str, stacklevel: int = 2) -> Callable[..., Any]: # pragma: no cover
46 """
47 Raise a `DeprecationWarning` when wrapped function/method is called.
49 Usage:
51 @deprecated("This method will be removed in version X; use Y instead.")
52 def some_method()"
53 pass
54 """
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
69def warn_deprecated(message: str, stacklevel: int = 2) -> None: # pragma: no cover
70 """Warn deprecated."""
72 warnings.warn(
73 message,
74 category=DeprecationWarning,
75 stacklevel=stacklevel
76 )
79def get_pattern_context(pattern: str, index: int) -> tuple[str, int, int]:
80 """Get the pattern context."""
82 last = 0
83 current_line = 1
84 col = 1
85 text = [] # type: list[str]
86 line = 1
87 offset = None # type: int | None
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
114 current_line += 1
115 last = m.end(0)
117 return ''.join(text), line, col