Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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 = '%s_include_%ss' % (class_name, class_name) 

29 rows2 = [] 

30 for node in doctree.traverse(class_node_list): 

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

32 rows2.append("tag={0} do={1}".format(breftag, app.config[incconf])) 

33 if len(rows2) == 0: 

34 return False 

35 

36 attr_name = '%s_all_%ss' % (class_name, class_name) 

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 "len(bloc_list_env)={0}".format(len(bloc_list_env)), ] 

45 rows.extend(rows2) 

46 rows.extend(["fromdocname='{0}'".format(fromdocname), 

47 "entry_name='{0}'".format(entry_name), 

48 "class_name='{0}'".format(class_name), 

49 "class_node='{0}'".format(class_node), 

50 "class_node_list='{0}'".format(class_node_list), 

51 "doctree='{0}'".format(type(doctree)), 

52 "#doctree={0}".format(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