Coverage for pyquickhelper/sphinxext/sphinx_ext_helper.py: 96%

79 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-03 02:21 +0200

1""" 

2@file 

3@brief Few helpers for :epkg:`Sphinx`. 

4""" 

5import io 

6import logging 

7import os 

8import pickle 

9import urllib.parse as urllib_parse 

10import urllib.request as urllib_request 

11from urllib.parse import urljoin 

12from sphinx.util.inventory import InventoryFile 

13 

14 

15def info_blocref(app, doctree, fromdocname, class_name, 

16 entry_name, class_node, class_node_list): 

17 """ 

18 Log information with :epkg:`Sphinx`. 

19 

20 @param app application (Sphinx) 

21 @param doctree document tree 

22 @param fromdocname document currently being compiled 

23 @param class_name name of the node 

24 @param entry_name entry name in ``TITLES`` 

25 @param class_node class node (@see cl blocref_node) 

26 @param class_node_list class node list (@see cl blocreflist) 

27 """ 

28 incconf = f'{class_name}_include_{class_name}s' 

29 rows2 = [] 

30 for node in doctree.traverse(class_node_list): 

31 breftag = node.get("breftag", None) 

32 rows2.append(f"tag={breftag} do={app.config[incconf]}") 

33 if len(rows2) == 0: 

34 return False 

35 

36 attr_name = f'{class_name}_all_{class_name}s' 

37 env = app.builder.env 

38 if hasattr(env, attr_name): 

39 bloc_list_env = getattr(env, attr_name) 

40 else: 

41 bloc_list_env = [] 

42 

43 rows = [" [info_blocref]", 

44 f"len(bloc_list_env)={len(bloc_list_env)}", ] 

45 rows.extend(rows2) 

46 rows.extend([f"fromdocname='{fromdocname}'", 

47 f"entry_name='{entry_name}'", 

48 f"class_name='{class_name}'", 

49 f"class_node='{class_node}'", 

50 f"class_node_list='{class_node_list}'", 

51 f"doctree='{type(doctree)}'", 

52 f"#doctree={len(doctree)}"]) 

53 message = " ".join(rows) 

54 logger = logging.getLogger("info_blocref") 

55 logger.info(message) 

56 return True 

57 

58 

59def sphinx_lang(env, default_value='en'): 

60 """ 

61 Returns the language defined in the configuration file. 

62 

63 @param env environment 

64 @param default_value default value 

65 @return language 

66 """ 

67 if hasattr(env, "settings"): 

68 settings = env.settings 

69 if hasattr(settings, "language_code"): 

70 lang = env.settings.language_code # pragma: no cover 

71 else: 

72 lang = "en" 

73 else: 

74 settings = None # pragma: no cover 

75 lang = "en" # pragma: no cover 

76 return lang 

77 

78 

79class TinyNode: 

80 """ 

81 Returned by @see fn traverse. 

82 """ 

83 

84 def __init__(self, parent): 

85 """ 

86 Create a note 

87 

88 @param parent parent node 

89 """ 

90 self.parent = parent 

91 

92 

93class NodeEnter(TinyNode): 

94 """ 

95 Returned by function @see fn traverse. 

96 """ 

97 pass 

98 

99 

100class NodeLeave(TinyNode): 

101 """ 

102 Returned by function @see fn traverse. 

103 """ 

104 pass 

105 

106 

107class WrappedNode: 

108 """ 

109 Wraps a docutils node. 

110 """ 

111 

112 def __init__(self, node): 

113 self.node = node 

114 

115 

116def traverse(node, depth=0): 

117 """ 

118 Enumerates through all children but insert a node whenever 

119 digging or leaving the childrens nodes. 

120 

121 @param node node (from doctree) 

122 @param depth current depth 

123 @return enumerate (depth, node) 

124 

125 @see cl NodeEnter and @see cl NodeLeave are returned whenever entering or leaving nodes. 

126 """ 

127 if isinstance(node, WrappedNode): 

128 node = node.node 

129 ne = NodeEnter(node) 

130 nl = NodeLeave(node) 

131 yield (depth, ne) 

132 yield (depth, node) 

133 for n in node.children: 

134 for r in traverse(n, depth + 1): 

135 yield r 

136 yield (depth, nl) 

137 

138 

139def _get_data(url): 

140 """ 

141 Loads file ``objects.inv`` generated by 

142 extension :epkg:`sphinx.ext.intersphinx`. 

143 

144 @param url url of documentation, example 

145 ``https://pandas.pydata.org/docs/`` 

146 @return instance of `InventoryFile` 

147 """ 

148 url_inv = urljoin(url, "objects.inv") 

149 if urllib_parse.urlparse(url_inv).scheme in ('http', 'https'): 

150 user_agent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11' # noqa: E501 

151 headers = {'User-Agent': user_agent} 

152 req = urllib_request.Request(url_inv, None, headers) 

153 resp = urllib_request.urlopen(req) 

154 data = resp.read() 

155 else: 

156 with open(url, 'rb') as fid: 

157 data = fid.read() 

158 

159 inv = InventoryFile.load(io.BytesIO(data), url, urljoin) 

160 return inv 

161 

162 

163def get_index(index_url, cache_dir): 

164 """ 

165 Retrieves documentation data for a specific module. 

166 

167 @param url url of documentation, example 

168 ``https://pandas.pydata.org/docs/`` 

169 @param cache_dir restore a cached inventory stored with pickle 

170 @return instance of `InventoryFile` 

171 """ 

172 if cache_dir is not None: 

173 base_file = index_url.replace("/", "_").split(':')[-1] + ".pkl" 

174 full_file = os.path.join(cache_dir, base_file) 

175 if os.path.exists(full_file): 

176 with open(full_file, "rb") as f: 

177 return pickle.load(f) 

178 index = _get_data(index_url) 

179 if cache_dir is not None: 

180 with open(full_file, "wb") as f: 

181 pickle.dump(index, f) 

182 return index