Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/soupsieve/pretty.py: 100%
19 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-03 06:10 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-03 06:10 +0000
1"""
2Format a pretty string of a `SoupSieve` object for easy debugging.
4This won't necessarily support all types and such, and definitely
5not support custom outputs.
7It is mainly geared towards our types as the `SelectorList`
8object is a beast to look at without some indentation and newlines.
9The format and various output types is fairly known (though it
10hasn't been tested extensively to make sure we aren't missing corners).
12Example:
14```
15>>> import soupsieve as sv
16>>> sv.compile('this > that.class[name=value]').selectors.pretty()
17SelectorList(
18 selectors=(
19 Selector(
20 tag=SelectorTag(
21 name='that',
22 prefix=None),
23 ids=(),
24 classes=(
25 'class',
26 ),
27 attributes=(
28 SelectorAttribute(
29 attribute='name',
30 prefix='',
31 pattern=re.compile(
32 '^value$'),
33 xml_type_pattern=None),
34 ),
35 nth=(),
36 selectors=(),
37 relation=SelectorList(
38 selectors=(
39 Selector(
40 tag=SelectorTag(
41 name='this',
42 prefix=None),
43 ids=(),
44 classes=(),
45 attributes=(),
46 nth=(),
47 selectors=(),
48 relation=SelectorList(
49 selectors=(),
50 is_not=False,
51 is_html=False),
52 rel_type='>',
53 contains=(),
54 lang=(),
55 flags=0),
56 ),
57 is_not=False,
58 is_html=False),
59 rel_type=None,
60 contains=(),
61 lang=(),
62 flags=0),
63 ),
64 is_not=False,
65 is_html=False)
66```
67"""
68from __future__ import annotations
69import re
70from typing import Any
72RE_CLASS = re.compile(r'(?i)[a-z_][_a-z\d\.]+\(')
73RE_PARAM = re.compile(r'(?i)[_a-z][_a-z\d]+=')
74RE_EMPTY = re.compile(r'\(\)|\[\]|\{\}')
75RE_LSTRT = re.compile(r'\[')
76RE_DSTRT = re.compile(r'\{')
77RE_TSTRT = re.compile(r'\(')
78RE_LEND = re.compile(r'\]')
79RE_DEND = re.compile(r'\}')
80RE_TEND = re.compile(r'\)')
81RE_INT = re.compile(r'\d+')
82RE_KWORD = re.compile(r'(?i)[_a-z][_a-z\d]+')
83RE_DQSTR = re.compile(r'"(?:\\.|[^"\\])*"')
84RE_SQSTR = re.compile(r"'(?:\\.|[^'\\])*'")
85RE_SEP = re.compile(r'\s*(,)\s*')
86RE_DSEP = re.compile(r'\s*(:)\s*')
88TOKENS = {
89 'class': RE_CLASS,
90 'param': RE_PARAM,
91 'empty': RE_EMPTY,
92 'lstrt': RE_LSTRT,
93 'dstrt': RE_DSTRT,
94 'tstrt': RE_TSTRT,
95 'lend': RE_LEND,
96 'dend': RE_DEND,
97 'tend': RE_TEND,
98 'sqstr': RE_SQSTR,
99 'sep': RE_SEP,
100 'dsep': RE_DSEP,
101 'int': RE_INT,
102 'kword': RE_KWORD,
103 'dqstr': RE_DQSTR
104}
107def pretty(obj: Any) -> str: # pragma: no cover
108 """Make the object output string pretty."""
110 sel = str(obj)
111 index = 0
112 end = len(sel) - 1
113 indent = 0
114 output = []
116 while index <= end:
117 m = None
118 for k, v in TOKENS.items():
119 m = v.match(sel, index)
121 if m:
122 name = k
123 index = m.end(0)
124 if name in ('class', 'lstrt', 'dstrt', 'tstrt'):
125 indent += 4
126 output.append('{}\n{}'.format(m.group(0), " " * indent))
127 elif name in ('param', 'int', 'kword', 'sqstr', 'dqstr', 'empty'):
128 output.append(m.group(0))
129 elif name in ('lend', 'dend', 'tend'):
130 indent -= 4
131 output.append(m.group(0))
132 elif name in ('sep',):
133 output.append('{}\n{}'.format(m.group(1), " " * indent))
134 elif name in ('dsep',):
135 output.append('{} '.format(m.group(1)))
136 break
138 return ''.join(output)