Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/sphinxcontrib/serializinghtml/__init__.py: 22%

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

108 statements  

1from __future__ import annotations 

2 

3import os 

4import pickle 

5import types 

6from os import path 

7from typing import TYPE_CHECKING 

8 

9from sphinx.application import ENV_PICKLE_FILENAME, Sphinx 

10from sphinx.builders.html import BuildInfo, StandaloneHTMLBuilder 

11from sphinx.locale import get_translation 

12from sphinx.util.osutil import SEP, copyfile, ensuredir, os_path 

13 

14from sphinxcontrib.serializinghtml import jsonimpl 

15 

16if TYPE_CHECKING: 

17 from collections.abc import Sequence 

18 from typing import Any, Protocol 

19 

20 class SerialisingImplementation(Protocol): 

21 def dump(self, obj: Any, file: Any, *args: Any, **kwargs: Any) -> None: ... 

22 def dumps(self, obj: Any, *args: Any, **kwargs: Any) -> str | bytes: ... 

23 def load(self, file: Any, *args: Any, **kwargs: Any) -> Any: ... 

24 def loads(self, data: Any, *args: Any, **kwargs: Any) -> Any: ... 

25 

26__version__ = '2.0.0' 

27__version_info__ = (2, 0, 0) 

28 

29package_dir = path.abspath(path.dirname(__file__)) 

30 

31__ = get_translation(__name__, 'console') 

32 

33 

34#: the filename for the "last build" file (for serializing builders) 

35LAST_BUILD_FILENAME = 'last_build' 

36 

37 

38class SerializingHTMLBuilder(StandaloneHTMLBuilder): 

39 """ 

40 An abstract builder that serializes the generated HTML. 

41 """ 

42 #: the serializing implementation to use. Set this to a module that 

43 #: implements a `dump`, `load`, `dumps` and `loads` functions 

44 #: (pickle, json etc.) 

45 implementation: SerialisingImplementation 

46 implementation_dumps_unicode = False 

47 #: additional arguments for dump() 

48 additional_dump_args: Sequence[Any] = () 

49 

50 #: the filename for the global context file 

51 globalcontext_filename: str = '' 

52 

53 supported_image_types = ['image/svg+xml', 'image/png', 

54 'image/gif', 'image/jpeg'] 

55 

56 def init(self) -> None: 

57 self.build_info = BuildInfo(self.config, self.tags) 

58 self.imagedir = '_images' 

59 self.current_docname = '' 

60 self.theme = None # type: ignore[assignment] # no theme necessary 

61 self.templates = None # no template bridge necessary 

62 self.init_templates() 

63 self.init_highlighter() 

64 self.init_css_files() 

65 self.init_js_files() 

66 self.use_index = self.get_builder_config('use_index', 'html') 

67 

68 def get_target_uri(self, docname: str, typ: str | None = None) -> str: 

69 if docname == 'index': 

70 return '' 

71 if docname.endswith(SEP + 'index'): 

72 return docname[:-5] # up to sep 

73 return docname + SEP 

74 

75 def dump_context(self, context: dict[str, Any], filename: str | os.PathLike[str]) -> None: 

76 context = context.copy() 

77 if 'css_files' in context: 

78 context['css_files'] = [css.filename for css in context['css_files']] 

79 if 'script_files' in context: 

80 context['script_files'] = [js.filename for js in context['script_files']] 

81 if self.implementation_dumps_unicode: 

82 with open(filename, 'w', encoding='utf-8') as ft: 

83 self.implementation.dump(context, ft, *self.additional_dump_args) 

84 else: 

85 with open(filename, 'wb') as fb: 

86 self.implementation.dump(context, fb, *self.additional_dump_args) 

87 

88 def handle_page(self, pagename: str, ctx: dict[str, Any], templatename: str = 'page.html', 

89 outfilename: str | None = None, event_arg: Any = None) -> None: 

90 ctx['current_page_name'] = pagename 

91 ctx.setdefault('pathto', lambda p: p) 

92 self.add_sidebars(pagename, ctx) 

93 

94 if not outfilename: 

95 outfilename = path.join(self.outdir, 

96 os_path(pagename) + self.out_suffix) 

97 

98 # we're not taking the return value here, since no template is 

99 # actually rendered 

100 self.app.emit('html-page-context', pagename, templatename, ctx, event_arg) 

101 

102 # make context object serializable 

103 for key in list(ctx): 

104 if isinstance(ctx[key], types.FunctionType): 

105 del ctx[key] 

106 

107 ensuredir(path.dirname(outfilename)) 

108 self.dump_context(ctx, outfilename) 

109 

110 # if there is a source file, copy the source file for the 

111 # "show source" link 

112 if ctx.get('sourcename'): 

113 source_name = path.join(self.outdir, '_sources', 

114 os_path(ctx['sourcename'])) 

115 ensuredir(path.dirname(source_name)) 

116 copyfile(self.env.doc2path(pagename), source_name) 

117 

118 def handle_finish(self) -> None: 

119 # dump the global context 

120 outfilename = path.join(self.outdir, self.globalcontext_filename) 

121 self.dump_context(self.globalcontext, outfilename) 

122 

123 # super here to dump the search index 

124 super().handle_finish() 

125 

126 # copy the environment file from the doctree dir to the output dir 

127 # as needed by the web app 

128 copyfile(path.join(self.doctreedir, ENV_PICKLE_FILENAME), 

129 path.join(self.outdir, ENV_PICKLE_FILENAME)) 

130 

131 # touch 'last build' file, used by the web application to determine 

132 # when to reload its environment and clear the cache 

133 open(path.join(self.outdir, LAST_BUILD_FILENAME), 'w').close() 

134 

135 

136class PickleHTMLBuilder(SerializingHTMLBuilder): 

137 """ 

138 A Builder that dumps the generated HTML into pickle files. 

139 """ 

140 name = 'pickle' 

141 epilog = __('You can now process the pickle files in %(outdir)s.') 

142 

143 implementation = pickle 

144 implementation_dumps_unicode = False 

145 additional_dump_args: tuple[Any] = (pickle.HIGHEST_PROTOCOL,) 

146 indexer_format = pickle 

147 indexer_dumps_unicode = False 

148 out_suffix = '.fpickle' 

149 globalcontext_filename = 'globalcontext.pickle' 

150 searchindex_filename = 'searchindex.pickle' 

151 

152 

153class JSONHTMLBuilder(SerializingHTMLBuilder): 

154 """ 

155 A builder that dumps the generated HTML into JSON files. 

156 """ 

157 name = 'json' 

158 epilog = __('You can now process the JSON files in %(outdir)s.') 

159 

160 implementation = jsonimpl 

161 implementation_dumps_unicode = True 

162 indexer_format = jsonimpl 

163 indexer_dumps_unicode = True 

164 out_suffix = '.fjson' 

165 globalcontext_filename = 'globalcontext.json' 

166 searchindex_filename = 'searchindex.json' 

167 

168 

169def setup(app: Sphinx) -> dict[str, Any]: 

170 app.require_sphinx('5.0') 

171 app.setup_extension('sphinx.builders.html') 

172 app.add_builder(JSONHTMLBuilder) 

173 app.add_builder(PickleHTMLBuilder) 

174 app.add_message_catalog(__name__, path.join(package_dir, 'locales')) 

175 

176 return { 

177 'version': __version__, 

178 'parallel_read_safe': True, 

179 'parallel_write_safe': True, 

180 }