1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

''' Utilities to allow inserting docstring fragments for common 

parameters into function and method docstrings''' 

 

from __future__ import division, print_function, absolute_import 

 

import sys 

 

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

'filldoc', 'unindent_dict', 'unindent_string'] 

 

 

def docformat(docstring, docdict=None): 

''' Fill a function docstring from variables in dictionary 

 

Adapt the indent of the inserted docs 

 

Parameters 

---------- 

docstring : string 

docstring from function, possibly with dict formatting strings 

docdict : dict, optional 

dictionary with keys that match the dict formatting strings 

and values that are docstring fragments to be inserted. The 

indentation of the inserted docstrings is set to match the 

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

indentation to all lines of the inserted string, except the 

first 

 

Returns 

------- 

outstring : string 

string with requested ``docdict`` strings inserted 

 

Examples 

-------- 

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

' Test string with inserted value' 

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

>>> inserted_string = "indented\\nstring" 

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

>>> docformat(docstring, docdict) 

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

''' 

if not docstring: 

return docstring 

if docdict is None: 

docdict = {} 

if not docdict: 

return docstring 

lines = docstring.expandtabs().splitlines() 

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

if len(lines) < 2: 

icount = 0 

else: 

icount = indentcount_lines(lines[1:]) 

indent = ' ' * icount 

# Insert this indent to dictionary docstrings 

indented = {} 

for name, dstr in docdict.items(): 

lines = dstr.expandtabs().splitlines() 

try: 

newlines = [lines[0]] 

for line in lines[1:]: 

newlines.append(indent+line) 

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

except IndexError: 

indented[name] = dstr 

return docstring % indented 

 

 

def inherit_docstring_from(cls): 

""" 

This decorator modifies the decorated function's docstring by 

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

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

 

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

docstring of `cls`s method. 

 

Parameters 

---------- 

cls : Python class or instance 

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

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

docstring of the decorated method. 

 

Returns 

------- 

f : function 

The decorator function that modifies the __doc__ attribute 

of its argument. 

 

Examples 

-------- 

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

docstring of `Foo.func`. 

 

>>> class Foo(object): 

... def func(self): 

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

... return 

... 

>>> class Bar(Foo): 

... @inherit_docstring_from(Foo) 

... def func(self): 

... '''%(super)s 

... Do it fast. 

... ''' 

... return 

... 

>>> b = Bar() 

>>> b.func.__doc__ 

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

 

""" 

def _doc(func): 

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

func_docstring = func.__doc__ 

if func_docstring is None: 

func.__doc__ = cls_docstring 

else: 

new_docstring = func_docstring % dict(super=cls_docstring) 

func.__doc__ = new_docstring 

return func 

return _doc 

 

 

def extend_notes_in_docstring(cls, notes): 

""" 

This decorator replaces the decorated function's docstring 

with the docstring from corresponding method in `cls`. 

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

the given `notes`. 

""" 

def _doc(func): 

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

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

if end_of_notes == -1: 

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

if end_of_notes == -1: 

end_of_notes = len(cls_docstring) 

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

cls_docstring[end_of_notes:]) 

return func 

return _doc 

 

 

def replace_notes_in_docstring(cls, notes): 

""" 

This decorator replaces the decorated function's docstring 

with the docstring from corresponding method in `cls`. 

It replaces the 'Notes' section of that docstring with 

the given `notes`. 

""" 

def _doc(func): 

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

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

# XXX The following assumes that there is a Notes section. 

start_of_notes = cls_docstring.find(notes_header) 

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

if end_of_notes == -1: 

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

if end_of_notes == -1: 

end_of_notes = len(cls_docstring) 

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

notes + 

cls_docstring[end_of_notes:]) 

return func 

return _doc 

 

 

def indentcount_lines(lines): 

''' Minimum indent for all lines in line list 

 

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

>>> indentcount_lines(lines) 

1 

>>> lines = [] 

>>> indentcount_lines(lines) 

0 

>>> lines = [' one'] 

>>> indentcount_lines(lines) 

1 

>>> indentcount_lines([' ']) 

0 

''' 

indentno = sys.maxsize 

for line in lines: 

stripped = line.lstrip() 

if stripped: 

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

if indentno == sys.maxsize: 

return 0 

return indentno 

 

 

def filldoc(docdict, unindent_params=True): 

''' Return docstring decorator using docdict variable dictionary 

 

Parameters 

---------- 

docdict : dictionary 

dictionary containing name, docstring fragment pairs 

unindent_params : {False, True}, boolean, optional 

If True, strip common indentation from all parameters in 

docdict 

 

Returns 

------- 

decfunc : function 

decorator that applies dictionary to input function docstring 

 

''' 

if unindent_params: 

docdict = unindent_dict(docdict) 

 

def decorate(f): 

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

return f 

return decorate 

 

 

def unindent_dict(docdict): 

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

can_dict = {} 

for name, dstr in docdict.items(): 

can_dict[name] = unindent_string(dstr) 

return can_dict 

 

 

def unindent_string(docstring): 

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

 

>>> unindent_string(' two') 

'two' 

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

'two\\n three' 

''' 

lines = docstring.expandtabs().splitlines() 

icount = indentcount_lines(lines) 

if icount == 0: 

return docstring 

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