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

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

119 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 

19 

20from . import compression 

21from . import transport 

22 

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

24 

25 

26def extract_kwargs(docstring): 

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

28 

29 Parameters 

30 ---------- 

31 docstring: str 

32 The docstring to extract keyword arguments from. 

33 

34 Returns 

35 ------- 

36 list of (str, str, list str) 

37 

38 str 

39 The name of the keyword argument. 

40 str 

41 Its type. 

42 str 

43 Its documentation as a list of lines. 

44 

45 Notes 

46 ----- 

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

48 

49 1. The parameters are under an underlined Parameters section 

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

51 3. Names and types are not indented 

52 4. Descriptions are indented with 4 spaces 

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

54 

55 Examples 

56 -------- 

57 

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

59 ... Parameters 

60 ... ---------- 

61 ... bar: str, optional 

62 ... This parameter is the bar. 

63 ... baz: int, optional 

64 ... This parameter is the baz. 

65 ... 

66 ... ''' 

67 >>> kwargs = extract_kwargs(docstring) 

68 >>> kwargs[0] 

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

70 

71 """ 

72 if not docstring: 

73 return [] 

74 

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

76 kwargs = [] 

77 

78 # 

79 # 1. Find the underlined 'Parameters' section 

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

81 # 

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

83 lines.pop(0) 

84 

85 if not lines: 

86 return [] 

87 

88 lines.pop(0) 

89 lines.pop(0) 

90 

91 for line in lines: 

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

93 break 

94 is_arg_line = not line.startswith(' ') 

95 if is_arg_line: 

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

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

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

99 continue 

100 is_description_line = line.startswith(' ') 

101 if is_description_line: 

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

103 

104 return kwargs 

105 

106 

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

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

109 

110 Basically reverses :func:`extract_kwargs`. 

111 

112 Parameters 

113 ---------- 

114 kwargs: list 

115 Output from the extract_kwargs function 

116 lpad: str, optional 

117 Padding string (from the left). 

118 

119 Returns 

120 ------- 

121 str 

122 The docstring snippet documenting the keyword arguments. 

123 

124 Examples 

125 -------- 

126 

127 >>> kwargs = [ 

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

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

130 ... ] 

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

132 bar: str, optional 

133 This parameter is the bar. 

134 baz: int, optional 

135 This parameter is the baz. 

136 

137 """ 

138 buf = io.StringIO() 

139 for name, type_, description in kwargs: 

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

141 for line in description: 

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

143 return buf.getvalue() 

144 

145 

146def extract_examples_from_readme_rst(indent=' '): 

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

148 

149 Parameters 

150 ---------- 

151 indent: str 

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

153 

154 Returns 

155 ------- 

156 str 

157 The examples. 

158 

159 Notes 

160 ----- 

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

162 """ 

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

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

165 try: 

166 with open(readme_path) as fin: 

167 lines = list(fin) 

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

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

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

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

172 except Exception: 

173 return indent + 'See README.rst' 

174 

175 

176def tweak_open_docstring(f): 

177 buf = io.StringIO() 

178 seen = set() 

179 

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

181 

182 with contextlib.redirect_stdout(buf): 

183 print(' smart_open supports the following transport mechanisms:') 

184 print() 

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

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

187 continue 

188 seen.add(submodule) 

189 

190 try: 

191 schemes = submodule.SCHEMES 

192 except AttributeError: 

193 schemes = [scheme] 

194 

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

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

197 print(' %s' % heading) 

198 print(' %s' % ('~' * len(heading))) 

199 print(' %s' % submodule.__doc__.split('\n')[0]) 

200 print() 

201 

202 kwargs = extract_kwargs(submodule.open.__doc__) 

203 if kwargs: 

204 print(to_docstring(kwargs, lpad=u' ')) 

205 

206 print(' Examples') 

207 print(' --------') 

208 print() 

209 print(extract_examples_from_readme_rst()) 

210 

211 print(' This function also supports transparent compression and decompression ') 

212 print(' using the following codecs:') 

213 print() 

214 for extension in compression.get_supported_extensions(): 

215 print(' * %s' % extension) 

216 print() 

217 print(' The function depends on the file extension to determine the appropriate codec.') 

218 

219 # 

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

221 # 

222 if f.__doc__: 

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

224 

225 

226def tweak_parse_uri_docstring(f): 

227 buf = io.StringIO() 

228 seen = set() 

229 schemes = [] 

230 examples = [] 

231 

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

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

234 continue 

235 

236 seen.add(submodule) 

237 

238 try: 

239 examples.extend(submodule.URI_EXAMPLES) 

240 except AttributeError: 

241 pass 

242 

243 try: 

244 schemes.extend(submodule.SCHEMES) 

245 except AttributeError: 

246 schemes.append(scheme) 

247 

248 with contextlib.redirect_stdout(buf): 

249 print(' Supported URI schemes are:') 

250 print() 

251 for scheme in schemes: 

252 print(' * %s' % scheme) 

253 print() 

254 print(' Valid URI examples::') 

255 print() 

256 for example in examples: 

257 print(' * %s' % example) 

258 

259 if f.__doc__: 

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