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/. 175 176 License files can contain <owner> and <email> 177 textual tags which will be replaced with cato informations. 178 179 Embedded license versions will be applied on the first empty line of 180 each source file scanned, if no empty line is found, no license is 181 applied. 182 183 In its normal behaviour cato apply the license to the files given as 184 arguments on the command line, while using -d option it scans the 185 given directory for file extensions specified as command line 186 arguments. If -r is provided in conjunction with -d, all the 187 directory tree is scanned starting from the supplied dir. 188 ''') 189 options, args = op.parse_args(args) 190 191 # scan license directory and populate licenses dictionary 192 licenses = [] 193 for lic_file in os.listdir(cato_licenser.license_dir): 194 licenses.append(lic_file[:lic_file.rfind('.')]) 195 196 # list available licenses 197 if options.lic_list: 198 for l in licenses: 199 print l 200 return 201 202 # overrides config file options 203 if options.owner: 204 cato_licenser.license_tags['<owner>'] = options.owner 205 if options.year: 206 cato_licenser.license_tags['<year>'] = options.year 207 if options.email: 208 cato_licenser.license_tags['<email>'] = options.email 209 if options.comment: 210 cato_licenser.comment_syntax = {'default': options.comment} 211 212 #extracts license infomrations 213 if not options.license: 214 license = licenses[0] #default license 215 else: 216 license = options.license 217 (extended_lic, embedded_lic) = cato_licenser.parse_license(license) 218 219 # Apply the license to given sources 220 if options.directory: 221 dir = options.directory 222 cato_licenser.patch_dir(dir, extended_lic) 223 if options.recursive: 224 for root, dirs, files in os.walk(dir): 225 for f in files: 226 match = False 227 for a in args: 228 if fnmatch.fnmatch(f, "*." + a): 229 match = True 230 if match: 231 print "Applying license to file: " + f 232 cato_licenser.patch_file(os.path.join(root, f), embedded_lic) 233 else: 234 for f in os.listdir(dir): 235 match = False 236 for a in args: 237 if fnmatch.fnmatch(f, "*." + a): 238 match = True 239 if match: 240 print "Applying license to file: " + f 241 cato_licenser.patch_file(os.path.join(root, f), embedded_lic) 242 else: 243 for f in args: 244 print "Applying license to file: " + f 245 cato_licenser.patch_file(f, embedded_lic)
246