Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/soupsieve/util.py: 100%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Utility."""
2from __future__ import annotations
3from functools import wraps, lru_cache
4import warnings
5import re
6from typing import Callable, Any
8DEBUG = 0x00001
9NOCACHE = 0x00002
10NOSTRICT = 0x00004
12RE_PATTERN_LINE_SPLIT = re.compile(r'(?:\r\n|(?!\r\n)[\n\r])|$')
14UC_A = ord('A')
15UC_Z = ord('Z')
18@lru_cache(maxsize=512)
19def lower(string: str) -> str:
20 """Lower."""
22 new_string = []
23 for c in string:
24 o = ord(c)
25 new_string.append(chr(o + 32) if UC_A <= o <= UC_Z else c)
26 return ''.join(new_string)
29class SelectorSyntaxError(Exception):
30 """Syntax error in a CSS selector."""
32 def __init__(self, msg: str, pattern: str | None = None, index: int | None = None) -> None:
33 """Initialize."""
35 self.line = None
36 self.col = None
37 self.context = None
39 if pattern is not None and index is not None:
40 # Format pattern to show line and column position
41 self.context, self.line, self.col = get_pattern_context(pattern, index)
42 msg = f'{msg}\n line {self.line}:\n{self.context}'
44 super().__init__(msg)
47def deprecated(message: str, stacklevel: int = 2) -> Callable[..., Any]: # pragma: no cover
48 """
49 Raise a `DeprecationWarning` when wrapped function/method is called.
51 Usage:
53 @deprecated("This method will be removed in version X; use Y instead.")
54 def some_method()"
55 pass
56 """
58 def _wrapper(func: Callable[..., Any]) -> Callable[..., Any]:
59 @wraps(func)
60 def _deprecated_func(*args: Any, **kwargs: Any) -> Any:
61 warnings.warn(
62 f"'{func.__name__}' is deprecated. {message}",
63 category=DeprecationWarning,
64 stacklevel=stacklevel
65 )
66 return func(*args, **kwargs)
67 return _deprecated_func
68 return _wrapper
71def warn_deprecated(message: str, stacklevel: int = 2) -> None: # pragma: no cover
72 """Warn deprecated."""
74 warnings.warn(
75 message,
76 category=DeprecationWarning,
77 stacklevel=stacklevel
78 )
81def get_pattern_context(pattern: str, index: int) -> tuple[str, int, int]:
82 """Get the pattern context."""
84 last = 0
85 current_line = 1
86 col = 1
87 text: list[str] = []
88 line = 1
89 offset: int | None = None
91 # Split pattern by newline and handle the text before the newline
92 for m in RE_PATTERN_LINE_SPLIT.finditer(pattern):
93 linetext = pattern[last:m.start(0)]
94 if not len(m.group(0)) and not len(text):
95 indent = ''
96 offset = -1
97 col = index - last + 1
98 elif last <= index < m.end(0):
99 indent = '--> '
100 offset = (-1 if index > m.start(0) else 0) + 3
101 col = index - last + 1
102 else:
103 indent = ' '
104 offset = None
105 if len(text):
106 # Regardless of whether we are presented with `\r\n`, `\r`, or `\n`,
107 # we will render the output with just `\n`. We will still log the column
108 # correctly though.
109 text.append('\n')
110 text.append(f'{indent}{linetext}')
111 if offset is not None:
112 text.append('\n')
113 text.append(' ' * (col + offset) + '^')
114 line = current_line
116 current_line += 1
117 last = m.end(0)
119 return ''.join(text), line, col