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

1""" 

2Format a pretty string of a `SoupSieve` object for easy debugging. 

3 

4This won't necessarily support all types and such, and definitely 

5not support custom outputs. 

6 

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). 

11 

12Example: 

13 

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 

71 

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*') 

87 

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} 

105 

106 

107def pretty(obj: Any) -> str: # pragma: no cover 

108 """Make the object output string pretty.""" 

109 

110 sel = str(obj) 

111 index = 0 

112 end = len(sel) - 1 

113 indent = 0 

114 output = [] 

115 

116 while index <= end: 

117 m = None 

118 for k, v in TOKENS.items(): 

119 m = v.match(sel, index) 

120 

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 

137 

138 return ''.join(output)