License New BSD license
Lines 92
Keywords
docstring (1) wrap (1)
Permissions
Viewable by Everyone
Editable by All Siafoo Users
Hide
Need a quick chart or graph for your blog? Try our reStructured Text renderer. Join Siafoo Now or Learn More

Re-Wrap Python Docstrings Atom Feed 0

In Brief Ever add something to a docstring, realize that line is then too long, and spend the next five minutes rewrapping the docstring? With this program, you'll never have to do that again! It wraps all of your docstrings to a set width, so they will display nicely in documentation.... more
# 's
  1#!/usr/bin/env python
2
3# Copyright 2007 Regents of the University of California
4# Written by David Isaacson at the University of California, Davis
5# BSD License
6
7import sys
8import re
9import getopt
10
11tabwidth = 2
12verbose = 0
13count_whitespace = 1
14
15def usage():
16 print "Usage: string_wrap.py chars input_file.py [output_file.py]"
17
18def check_pos_int(string,title):
19 try:
20 retval = int(string)
21 if retval <= 0: raise ValueError
22 return retval
23 except ValueError:
24 print "ERROR: '%s' must be a positive integer." % title
25 usage()
26 sys.exit(2)
27
28#input argv stuff
29try: opts,args = getopt.getopt(sys.argv[1:],"t:vw")
30except getopt.GetoptError:
31 usage()
32 sys.exit(2)
33
34for opt,arg in opts:
35 if opt == '-t':
36 tabwidth = check_pos_int(arg,'Tab width')
37 if opt == '-v':
38 verbose = 1
39 if opt == '-w':
40 count_whitespace = 0
41
42if len(args) == 2:
43 chars = check_pos_int(args[0],'chars')
44 in_file = args[1]
45 out_file = args[1]
46elif len(args) == 3:
47 chars = check_pos_int(args[0],'chars')
48 in_file = args[1]
49 out_file = args[2]
50else:
51 usage()
52 sys.exit(2)
53
54#open, parse, and close file
55file_object = open(in_file,"r")
56file = file_object.read()
57file_object.close()
58
59matches = re.finditer('([ \\t]*)\"\"\"[\\r\\n]*(.*?)[\\r\\n]*\"\"\"',file,re.S)
60
61for match in matches:
62 original_spaces = match.group(1)
63 string = match.group(0)
64
65 #if there are two consecutive newlines, split into multiple strings
66 lines = []
67 this_chars = chars
68 if count_whitespace: this_chars -= original_spaces.count(' ') + tabwidth * original_spaces.count('\t')
69
70 for raw in re.split('([\\r\\n]+[ \\t]*[\\r\\n]+)',string):
71
72 raw = re.sub('\"\"\"','',raw) #strip quotes
73 raw = re.sub('^[\\r\\n]+|[\\r\\n]+$','',raw) #strip beginning and ending whitespace
74
75 #special case: if first line of raw ends in '.py', assume we're describing a file/author/copyright
76 #and don't mess it up
77 if not re.search('^[^\\.\\r\\n]+\.py[ \\t]*[\\r\\n]+',raw):
78
79 #get raw string
80 raw = re.sub('(?m)^[ \\t]+','',raw) #strip beginning whitespace per line
81 raw = re.sub('\\t',' '*tabwidth,raw) #turn tabs into spaces - this is not strictly necessary, but makes life simpler
82 raw = re.sub('(\\S)[\\r\\n]+(\\S)','\\1 \\2',raw) #replace 'constricted' newlines with spaces
83 raw = re.sub('[\\r\\n]+','',raw) #delete other newlines
84
85 #reparse raw into lines
86 while len(raw) > chars:
87 eol = raw.rfind(' ',0,this_chars)
88 lines.append(raw[:eol])
89 raw = raw[eol+1:]
90 lines.append(raw)
91
92 else: #if a .py line
93 if verbose: print "Warning: Ignoring a section because first line ends in '.py'."
94 lines.extend(re.split('[\\r\\n]',raw))
95
96 #strip leading/trailing spaces
97 for i in xrange(len(lines)):
98 lines[i] = re.sub('^ | $','',lines[i])
99
100 #put lines back together
101 lines.insert(0,'\"\"\"')
102 lines.append('\"\"\"')
103 delimiter = '\n'+original_spaces
104 string_new = original_spaces + delimiter.join(lines)
105 if verbose: print string_new
106
107 #replace old string with new string
108 file = re.sub(re.escape(string),string_new,file)
109
110
111#write to file
112file_object = open(out_file,"w")
113file_object.write(file)
114file_object.close()

Ever add something to a docstring, realize that line is then too long, and spend the next five minutes rewrapping the docstring? With this program, you'll never have to do that again! It wraps all of your docstrings to a set width, so they will display nicely in documentation.

Call the program with: python docstring_wrap.py [-t tab_width] [-v] [-w] count input_file [output_file]

  • count is the number of characters you would like to wrap at.
  • input_file is the file to parse. If output_file is not specified, the resulting file will be saved to this location.
  • -t tab_width specifies the tab width
  • -v enables verbosity
  • -w includes whitespace on the left of a docstring in the wrapping width (default behavior is to make the non-whitespace part of the line count characters wide)

As an added bonus, this program won't mess with any docstring with a first line ending in '.py', so as to not destroy any file/author/copyright docstrings.