Module cato
[hide private]
[frames] | no frames]

Source Code for Module cato

  1  # coding=utf-8 
  2   
  3  # 
  4  # 
  5  #    Copyright (C) 2012  Marco Bartolini, marco.bartolini@gmail.com 
  6  # 
  7  #    This program is free software: you can redistribute it and/or modify 
  8  #    it under the terms of the GNU General Public License as published by 
  9  #    the Free Software Foundation, either version 3 of the License, or 
 10  #    (at your option) any later version. 
 11  # 
 12  #    This program is distributed in the hope that it will be useful, 
 13  #    but WITHOUT ANY WARRANTY; without even the implied warranty of 
 14  #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 15  #    GNU General Public License for more details. 
 16  # 
 17  #    You should have received a copy of the GNU General Public License 
 18  #    along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 19  # 
 20  """ 
 21  This module implements the necessary routines used to embed license 
 22  informations into source code project files. 
 23  The package installation also installs a command line utility along with the 
 24  license files in textual format in a hidden directory inside the user home. 
 25   
 26  @author: Marco Bartolini 
 27  @contact: marco.bartolini@gmail.com 
 28  @version: 1.0 
 29  """ 
 30   
 31  import os 
 32  import getpass 
 33  import datetime 
 34  import re 
 35   
36 -class Cato(object):
37 - def __init__(self):
38 """ 39 Default constructor sets default values for cato options. 40 """ 41 self.home_path = os.path.expanduser("~") 42 self.cato_dir = os.path.join(self.home_path, ".cato") 43 self.license_dir = os.path.join(self.cato_dir, "licenses") 44 self.end_phrase = "END OF TERMS AND CONDITIONS" 45 46 self.comment_syntax = {"c" : "//", 47 "cpp" : "//", 48 "cc" : "//", 49 "h" : "//", 50 "hpp" : "//", 51 "py" : "#", 52 "java" : "//", 53 "f" : "!", 54 "rb" : "#", 55 "default" : "*", 56 } 57 58 self.license_tags = {"<year>" : str(datetime.datetime.now().year), 59 "<owner>" : getpass.getuser(), 60 "<email>" : getpass.getuser() + "@example.com", 61 } 62 63 self.eol = "\n" 64 self.loaded = False
65
66 - def parse_license(self, lic_name):
67 """ 68 Parse a license file and split it into the full flagged license to 69 be included into a \"LICENSE\" file and an embedded version to be 70 included at the beginning of each source file. It also substitute the 71 license tags in the license text. 72 @type lic_name: string 73 @param lic_name: license file name key 74 @return: (full_licensem, embedded_license) 75 """ 76 licenses = {} 77 for license_file in os.listdir(self.license_dir): 78 licenses[license_file[:license_file.rfind(".")]] = \ 79 os.path.abspath(os.path.join(self.license_dir, license_file)) 80 try: 81 with open(licenses[lic_name], "rt") as f: 82 text = f.read() 83 except KeyError: 84 raise KeyError("License " + lic_name + " not found in " + 85 self.license_dir) 86 for tag, substitution in self.license_tags.iteritems(): 87 pattern = re.compile(tag) 88 text = re.subn(pattern, substitution, text)[0] 89 license_parts = text.split(self.end_phrase) 90 extended_license = license_parts[0] 91 if len(license_parts) == 1: 92 embedded_license = license_parts[0].split(self.eol) 93 else: 94 embedded_license = license_parts[1].split(self.eol) 95 return (extended_license, embedded_license)
96
97 - def patch_file(self, filename, embedded_license):
98 """ 99 Embed the license in the given source code file. The license is inserted 100 where the first empty line is found. If no empty line is present no license 101 is embedded. 102 @param filename: file path 103 @param embedded_license: the embedded license as obtained by 104 L{parse_license} function 105 @return: True if a license has been embedded 106 """ 107 extension = filename.split(".")[-1] 108 comment = self.comment_syntax.get(extension, self.comment_syntax["default"]) 109 embedded = False 110 with open(filename, "rt") as input: 111 with open(filename + ".cato", "wt") as output: 112 for input_line in input: 113 output.write(input_line) 114 if not embedded and input_line.strip() == "": 115 for embed_line in embedded_license: 116 output.write(comment + embed_line + self.eol) 117 embedded = True 118 os.rename(filename + ".cato", filename) 119 return embedded
120
121 - def patch_dir(self, dirname, extended_license):
122 """ 123 Insert a license file into a directory naming it \"LICENSE\". 124 @param dirname: the target directory path 125 @param extended_license: the license text as obtained by L{parse_license} 126 function 127 """ 128 with open(os.path.join(dirname, "LICENSE"), "wt") as license_file: 129 license_file.write(extended_license)
130
131 -def command_line_util(args):
132 """ 133 Function that parses command line options and invokes cato methods on 134 selected files. Use --help for online help. 135 """ 136 from optparse import OptionParser #for compatibility with python2.5 137 from ConfigParser import SafeConfigParser 138 import os 139 import fnmatch 140 141 cato_licenser = Cato() 142 143 # Parsing configuration file options 144 scp = SafeConfigParser() 145 scp.read(os.path.join(cato_licenser.cato_dir, "cato.cfg")) 146 if 'Cato' in scp.sections(): 147 if 'owner' in scp.options('Cato'): 148 cato_licenser.license_tags['<owner>'] = scp.get('Cato', 'owner') 149 if 'email' in scp.options('Cato'): 150 cato_licenser.license_tags['<email>'] = scp.get('Cato', 'email') 151 if 'end_phrase' in scp.options('Cato'): 152 cato_licenser.end_phrase = scp.get('Cato', 'end_phrase') 153 if 'Comments' in scp.sections(): 154 for c in scp.options('Comments'): 155 cato_licenser.comment_syntax[c] = scp.get('Comments', c) 156 157 # Parsing command line options 158 op = OptionParser() 159 op.add_option("--list", action="store_true", default=False, 160 dest="lic_list", help="Lists available licenses and quit") 161 op.add_option("-l", "--license", dest="license", help="The license name, use --list to list all licenses available") 162 op.add_option("-o", "--owner", dest="owner", help="the copyright owner") 163 op.add_option("-e", "--email", dest="email", help="email contact of the copyright owner") 164 op.add_option("-y", "--year", dest="year", help="the copyright year, defaults to current year") 165 op.add_option("-d", "--directory", dest="directory", help="a target directory where to find sources and add a LICENSE file") 166 op.add_option("-r", action="store_true", default="False", dest="recursive", 167 help="only with -d. If set recursively parses directory tree") 168 op.add_option("-c", "--comment", dest="comment", help="overrides comment syntax") 169 op.set_usage("cato -l gpl-3.0 -o \"John Doe\" -e john@doe.com -y 2012 *.py") 170 op.set_description(''' 171 cato is free software for applying licenses to your source code 172 files. You can customize cato changing the cato.cfg file that you 173 find in ~/.cato/ and adding license textual files in 174 ~/cato/licenses/.\n 175 License files can contain <owner> and <email> 176 textual tags which will be replaced with cato informations.\n 177 Embedded license versions will be applied on the first empty line of 178 each source file scanned, if no empty line is found, no license is 179 applied.\n 180 In its normal behaviour cato apply the license to the files given as 181 arguments on the command line, while using -d option it scans the 182 given directory for file extensions specified as command line 183 arguments. If -r is provided in conjunction with -d, all the 184 directory tree is scanned starting from the supplied dir. 185 ''') 186 options, args = op.parse_args(args) 187 188 # scan license directory and populate licenses dictionary 189 licenses = [] 190 for lic_file in os.listdir(cato_licenser.license_dir): 191 licenses.append(lic_file[:lic_file.rfind('.')]) 192 193 # list available licenses 194 if options.lic_list: 195 for l in licenses: 196 print l 197 return 198 199 # overrides config file options 200 if options.owner: 201 cato_licenser.license_tags['<owner>'] = options.owner 202 if options.year: 203 cato_licenser.license_tags['<year>'] = options.year 204 if options.email: 205 cato_licenser.license_tags['<email>'] = options.email 206 if options.comment: 207 cato_licenser.comment_syntax = {'default': options.comment} 208 209 #extracts license infomrations 210 if not options.license: 211 license = licenses[0] #default license 212 else: 213 license = options.license 214 (extended_lic, embedded_lic) = cato_licenser.parse_license(license) 215 216 # Apply the license to given sources 217 if options.directory: 218 dir = options.directory 219 cato_licenser.patch_dir(dir, extended_lic) 220 if options.recursive: 221 for root, dirs, files in os.walk(dir): 222 for f in files: 223 match = False 224 for a in args: 225 if fnmatch.fnmatch(f, "*." + a): 226 match = True 227 if match: 228 print "Applying license to file: " + f 229 cato_licenser.patch_file(os.path.join(root, f), embedded_lic) 230 else: 231 root = os.path.abspath(dir) 232 for f in os.listdir(root): 233 if os.path.isfile(os.path.join(root, f)): 234 match = False 235 for a in args: 236 if fnmatch.fnmatch(f, "*." + a): 237 match = True 238 if match: 239 print "Applying license to file: " + f 240 cato_licenser.patch_file(os.path.join(root, f), embedded_lic) 241 else: 242 for f in args: 243 print "Applying license to file: " + f 244 cato_licenser.patch_file(f, embedded_lic)
245