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''' Utilities to allow inserting docstring fragments for common 

2parameters into function and method docstrings''' 

3 

4import sys 

5 

6__all__ = ['docformat', 'inherit_docstring_from', 'indentcount_lines', 

7 'filldoc', 'unindent_dict', 'unindent_string', 'doc_replace'] 

8 

9 

10def docformat(docstring, docdict=None): 

11 ''' Fill a function docstring from variables in dictionary 

12 

13 Adapt the indent of the inserted docs 

14 

15 Parameters 

16 ---------- 

17 docstring : string 

18 docstring from function, possibly with dict formatting strings 

19 docdict : dict, optional 

20 dictionary with keys that match the dict formatting strings 

21 and values that are docstring fragments to be inserted. The 

22 indentation of the inserted docstrings is set to match the 

23 minimum indentation of the ``docstring`` by adding this 

24 indentation to all lines of the inserted string, except the 

25 first. 

26 

27 Returns 

28 ------- 

29 outstring : string 

30 string with requested ``docdict`` strings inserted 

31 

32 Examples 

33 -------- 

34 >>> docformat(' Test string with %(value)s', {'value':'inserted value'}) 

35 ' Test string with inserted value' 

36 >>> docstring = 'First line\\n Second line\\n %(value)s' 

37 >>> inserted_string = "indented\\nstring" 

38 >>> docdict = {'value': inserted_string} 

39 >>> docformat(docstring, docdict) 

40 'First line\\n Second line\\n indented\\n string' 

41 ''' 

42 if not docstring: 

43 return docstring 

44 if docdict is None: 

45 docdict = {} 

46 if not docdict: 

47 return docstring 

48 lines = docstring.expandtabs().splitlines() 

49 # Find the minimum indent of the main docstring, after first line 

50 if len(lines) < 2: 

51 icount = 0 

52 else: 

53 icount = indentcount_lines(lines[1:]) 

54 indent = ' ' * icount 

55 # Insert this indent to dictionary docstrings 

56 indented = {} 

57 for name, dstr in docdict.items(): 

58 lines = dstr.expandtabs().splitlines() 

59 try: 

60 newlines = [lines[0]] 

61 for line in lines[1:]: 

62 newlines.append(indent+line) 

63 indented[name] = '\n'.join(newlines) 

64 except IndexError: 

65 indented[name] = dstr 

66 return docstring % indented 

67 

68 

69def inherit_docstring_from(cls): 

70 """ 

71 This decorator modifies the decorated function's docstring by 

72 replacing occurrences of '%(super)s' with the docstring of the 

73 method of the same name from the class `cls`. 

74 

75 If the decorated method has no docstring, it is simply given the 

76 docstring of `cls`s method. 

77 

78 Parameters 

79 ---------- 

80 cls : Python class or instance 

81 A class with a method with the same name as the decorated method. 

82 The docstring of the method in this class replaces '%(super)s' in the 

83 docstring of the decorated method. 

84 

85 Returns 

86 ------- 

87 f : function 

88 The decorator function that modifies the __doc__ attribute 

89 of its argument. 

90 

91 Examples 

92 -------- 

93 In the following, the docstring for Bar.func created using the 

94 docstring of `Foo.func`. 

95 

96 >>> class Foo(object): 

97 ... def func(self): 

98 ... '''Do something useful.''' 

99 ... return 

100 ... 

101 >>> class Bar(Foo): 

102 ... @inherit_docstring_from(Foo) 

103 ... def func(self): 

104 ... '''%(super)s 

105 ... Do it fast. 

106 ... ''' 

107 ... return 

108 ... 

109 >>> b = Bar() 

110 >>> b.func.__doc__ 

111 'Do something useful.\n Do it fast.\n ' 

112 

113 """ 

114 def _doc(func): 

115 cls_docstring = getattr(cls, func.__name__).__doc__ 

116 func_docstring = func.__doc__ 

117 if func_docstring is None: 

118 func.__doc__ = cls_docstring 

119 else: 

120 new_docstring = func_docstring % dict(super=cls_docstring) 

121 func.__doc__ = new_docstring 

122 return func 

123 return _doc 

124 

125 

126def extend_notes_in_docstring(cls, notes): 

127 """ 

128 This decorator replaces the decorated function's docstring 

129 with the docstring from corresponding method in `cls`. 

130 It extends the 'Notes' section of that docstring to include 

131 the given `notes`. 

132 """ 

133 def _doc(func): 

134 cls_docstring = getattr(cls, func.__name__).__doc__ 

135 # If python is called with -OO option, 

136 # there is no docstring 

137 if cls_docstring is None: 

138 return func 

139 end_of_notes = cls_docstring.find(' References\n') 

140 if end_of_notes == -1: 

141 end_of_notes = cls_docstring.find(' Examples\n') 

142 if end_of_notes == -1: 

143 end_of_notes = len(cls_docstring) 

144 func.__doc__ = (cls_docstring[:end_of_notes] + notes + 

145 cls_docstring[end_of_notes:]) 

146 return func 

147 return _doc 

148 

149 

150def replace_notes_in_docstring(cls, notes): 

151 """ 

152 This decorator replaces the decorated function's docstring 

153 with the docstring from corresponding method in `cls`. 

154 It replaces the 'Notes' section of that docstring with 

155 the given `notes`. 

156 """ 

157 def _doc(func): 

158 cls_docstring = getattr(cls, func.__name__).__doc__ 

159 notes_header = ' Notes\n -----\n' 

160 # If python is called with -OO option, 

161 # there is no docstring 

162 if cls_docstring is None: 

163 return func 

164 start_of_notes = cls_docstring.find(notes_header) 

165 end_of_notes = cls_docstring.find(' References\n') 

166 if end_of_notes == -1: 

167 end_of_notes = cls_docstring.find(' Examples\n') 

168 if end_of_notes == -1: 

169 end_of_notes = len(cls_docstring) 

170 func.__doc__ = (cls_docstring[:start_of_notes + len(notes_header)] + 

171 notes + 

172 cls_docstring[end_of_notes:]) 

173 return func 

174 return _doc 

175 

176 

177def indentcount_lines(lines): 

178 ''' Minimum indent for all lines in line list 

179 

180 >>> lines = [' one', ' two', ' three'] 

181 >>> indentcount_lines(lines) 

182 1 

183 >>> lines = [] 

184 >>> indentcount_lines(lines) 

185 0 

186 >>> lines = [' one'] 

187 >>> indentcount_lines(lines) 

188 1 

189 >>> indentcount_lines([' ']) 

190 0 

191 ''' 

192 indentno = sys.maxsize 

193 for line in lines: 

194 stripped = line.lstrip() 

195 if stripped: 

196 indentno = min(indentno, len(line) - len(stripped)) 

197 if indentno == sys.maxsize: 

198 return 0 

199 return indentno 

200 

201 

202def filldoc(docdict, unindent_params=True): 

203 ''' Return docstring decorator using docdict variable dictionary 

204 

205 Parameters 

206 ---------- 

207 docdict : dictionary 

208 dictionary containing name, docstring fragment pairs 

209 unindent_params : {False, True}, boolean, optional 

210 If True, strip common indentation from all parameters in 

211 docdict 

212 

213 Returns 

214 ------- 

215 decfunc : function 

216 decorator that applies dictionary to input function docstring 

217 

218 ''' 

219 if unindent_params: 

220 docdict = unindent_dict(docdict) 

221 

222 def decorate(f): 

223 f.__doc__ = docformat(f.__doc__, docdict) 

224 return f 

225 return decorate 

226 

227 

228def unindent_dict(docdict): 

229 ''' Unindent all strings in a docdict ''' 

230 can_dict = {} 

231 for name, dstr in docdict.items(): 

232 can_dict[name] = unindent_string(dstr) 

233 return can_dict 

234 

235 

236def unindent_string(docstring): 

237 ''' Set docstring to minimum indent for all lines, including first 

238 

239 >>> unindent_string(' two') 

240 'two' 

241 >>> unindent_string(' two\\n three') 

242 'two\\n three' 

243 ''' 

244 lines = docstring.expandtabs().splitlines() 

245 icount = indentcount_lines(lines) 

246 if icount == 0: 

247 return docstring 

248 return '\n'.join([line[icount:] for line in lines]) 

249 

250 

251def doc_replace(obj, oldval, newval): 

252 """Decorator to take the docstring from obj, with oldval replaced by newval 

253 

254 Equivalent to ``func.__doc__ = obj.__doc__.replace(oldval, newval)`` 

255 

256 Parameters 

257 ---------- 

258 obj: object 

259 The object to take the docstring from. 

260 oldval: string 

261 The string to replace from the original docstring. 

262 newval: string 

263 The string to replace ``oldval`` with. 

264 """ 

265 # __doc__ may be None for optimized Python (-OO) 

266 doc = (obj.__doc__ or '').replace(oldval, newval) 

267 

268 def inner(func): 

269 func.__doc__ = doc 

270 return func 

271 

272 return inner