Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/smart_open/doctools.py: 73%

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

126 statements  

1# -*- coding: utf-8 -*- 

2# 

3# Copyright (C) 2019 Radim Rehurek <me@radimrehurek.com> 

4# 

5# This code is distributed under the terms and conditions 

6# from the MIT License (MIT). 

7# 

8 

9"""Common functions for working with docstrings. 

10 

11For internal use only. 

12""" 

13 

14import contextlib 

15import inspect 

16import io 

17import os.path 

18import re 

19import sys 

20 

21from . import compression 

22from . import transport 

23 

24# 

25# Python 3.13+ automatically trims docstrings (like inspect.cleandoc), 

26# so we need to adjust the placeholder and indentation accordingly. 

27# 

28if sys.version_info >= (3, 13): 

29 PLACEHOLDER = 'smart_open/doctools.py magic goes here' 

30 LPAD = '' 

31else: 

32 PLACEHOLDER = ' smart_open/doctools.py magic goes here' 

33 LPAD = ' ' 

34 

35 

36def extract_kwargs(docstring): 

37 """Extract keyword argument documentation from a function's docstring. 

38 

39 Parameters 

40 ---------- 

41 docstring: str 

42 The docstring to extract keyword arguments from. 

43 

44 Returns 

45 ------- 

46 list of (str, str, list str) 

47 

48 str 

49 The name of the keyword argument. 

50 str 

51 Its type. 

52 str 

53 Its documentation as a list of lines. 

54 

55 Notes 

56 ----- 

57 The implementation is rather fragile. It expects the following: 

58 

59 1. The parameters are under an underlined Parameters section 

60 2. Keyword parameters have the literal ", optional" after the type 

61 3. Names and types are not indented 

62 4. Descriptions are indented with 4 spaces 

63 5. The Parameters section ends with an empty line. 

64 

65 Examples 

66 -------- 

67 

68 >>> docstring = '''The foo function. 

69 ... Parameters 

70 ... ---------- 

71 ... bar: str, optional 

72 ... This parameter is the bar. 

73 ... baz: int, optional 

74 ... This parameter is the baz. 

75 ... 

76 ... ''' 

77 >>> kwargs = extract_kwargs(docstring) 

78 >>> kwargs[0] 

79 ('bar', 'str, optional', ['This parameter is the bar.']) 

80 

81 """ 

82 if not docstring: 

83 return [] 

84 

85 lines = inspect.cleandoc(docstring).split('\n') 

86 kwargs = [] 

87 

88 # 

89 # 1. Find the underlined 'Parameters' section 

90 # 2. Once there, continue parsing parameters until we hit an empty line 

91 # 

92 while lines and lines[0] != 'Parameters': 

93 lines.pop(0) 

94 

95 if not lines: 

96 return [] 

97 

98 lines.pop(0) 

99 lines.pop(0) 

100 

101 for line in lines: 

102 if not line.strip(): # stop at the first empty line encountered 

103 break 

104 is_arg_line = not line.startswith(' ') 

105 if is_arg_line: 

106 name, type_ = line.split(':', 1) 

107 name, type_, description = name.strip(), type_.strip(), [] 

108 kwargs.append([name, type_, description]) 

109 continue 

110 is_description_line = line.startswith(' ') 

111 if is_description_line: 

112 kwargs[-1][-1].append(line.strip()) 

113 

114 return kwargs 

115 

116 

117def to_docstring(kwargs, lpad=''): 

118 """Reconstruct a docstring from keyword argument info. 

119 

120 Basically reverses :func:`extract_kwargs`. 

121 

122 Parameters 

123 ---------- 

124 kwargs: list 

125 Output from the extract_kwargs function 

126 lpad: str, optional 

127 Padding string (from the left). 

128 

129 Returns 

130 ------- 

131 str 

132 The docstring snippet documenting the keyword arguments. 

133 

134 Examples 

135 -------- 

136 

137 >>> kwargs = [ 

138 ... ('bar', 'str, optional', ['This parameter is the bar.']), 

139 ... ('baz', 'int, optional', ['This parameter is the baz.']), 

140 ... ] 

141 >>> print(to_docstring(kwargs), end='') 

142 bar: str, optional 

143 This parameter is the bar. 

144 baz: int, optional 

145 This parameter is the baz. 

146 

147 """ 

148 buf = io.StringIO() 

149 for name, type_, description in kwargs: 

150 buf.write('%s%s: %s\n' % (lpad, name, type_)) 

151 for line in description: 

152 buf.write('%s %s\n' % (lpad, line)) 

153 return buf.getvalue() 

154 

155 

156def extract_examples_from_readme_rst(indent=None): 

157 """Extract examples from this project's README.rst file. 

158 

159 Parameters 

160 ---------- 

161 indent: str 

162 Prepend each line with this string. Should contain some number of spaces. 

163 

164 Returns 

165 ------- 

166 str 

167 The examples. 

168 

169 Notes 

170 ----- 

171 Quite fragile, depends on named labels inside the README.rst file. 

172 """ 

173 if indent is None: 

174 indent = LPAD 

175 curr_dir = os.path.dirname(os.path.abspath(__file__)) 

176 readme_path = os.path.join(curr_dir, '..', 'README.rst') 

177 try: 

178 with open(readme_path) as fin: 

179 lines = list(fin) 

180 start = lines.index('.. _doctools_before_examples:\n') 

181 end = lines.index(".. _doctools_after_examples:\n") 

182 lines = lines[start+4:end-2] 

183 return ''.join([indent + re.sub('^ ', '', line) for line in lines]) 

184 except Exception: 

185 return indent + 'See README.rst' 

186 

187 

188def tweak_open_docstring(f): 

189 buf = io.StringIO() 

190 seen = set() 

191 

192 root_path = os.path.dirname(os.path.dirname(__file__)) 

193 

194 with contextlib.redirect_stdout(buf): 

195 print(f'{LPAD}smart_open supports the following transport mechanisms:') 

196 print() 

197 for scheme, submodule in sorted(transport._REGISTRY.items()): 

198 if scheme == transport.NO_SCHEME or submodule in seen: 

199 continue 

200 seen.add(submodule) 

201 

202 try: 

203 schemes = submodule.SCHEMES 

204 except AttributeError: 

205 schemes = [scheme] 

206 

207 relpath = os.path.relpath(submodule.__file__, start=root_path) 

208 heading = '%s (%s)' % ("/".join(schemes), relpath) 

209 print(f'{LPAD}{heading}') 

210 print(f'{LPAD}{"~" * len(heading)}') 

211 print(f'{LPAD}{submodule.__doc__.split(chr(10))[0]}') 

212 print() 

213 

214 kwargs = extract_kwargs(submodule.open.__doc__) 

215 if kwargs: 

216 print(to_docstring(kwargs, lpad=LPAD)) 

217 

218 print(f'{LPAD}Examples') 

219 print(f'{LPAD}--------') 

220 print() 

221 print(extract_examples_from_readme_rst(indent=LPAD)) 

222 

223 print(f'{LPAD}This function also supports transparent compression and decompression ') 

224 print(f'{LPAD}using the following codecs:') 

225 print() 

226 for extension in compression.get_supported_extensions(): 

227 print(f'{LPAD}* {extension}') 

228 print() 

229 print(f'{LPAD}The function depends on the file extension to determine the appropriate codec.') 

230 

231 # 

232 # The docstring can be None if -OO was passed to the interpreter. 

233 # 

234 if f.__doc__: 

235 f.__doc__ = f.__doc__.replace(PLACEHOLDER, buf.getvalue()) 

236 

237 

238def tweak_parse_uri_docstring(f): 

239 buf = io.StringIO() 

240 seen = set() 

241 schemes = [] 

242 examples = [] 

243 

244 for scheme, submodule in sorted(transport._REGISTRY.items()): 

245 if scheme == transport.NO_SCHEME or submodule in seen: 

246 continue 

247 

248 seen.add(submodule) 

249 

250 try: 

251 examples.extend(submodule.URI_EXAMPLES) 

252 except AttributeError: 

253 pass 

254 

255 try: 

256 schemes.extend(submodule.SCHEMES) 

257 except AttributeError: 

258 schemes.append(scheme) 

259 

260 with contextlib.redirect_stdout(buf): 

261 print(f'{LPAD}Supported URI schemes are:') 

262 print() 

263 for scheme in schemes: 

264 print(f'{LPAD}* {scheme}') 

265 print() 

266 print(f'{LPAD}Valid URI examples::') 

267 print() 

268 for example in examples: 

269 print(f'{LPAD}* {example}') 

270 

271 if f.__doc__: 

272 f.__doc__ = f.__doc__.replace(PLACEHOLDER, buf.getvalue())