From bf293ebc6a54c65097f3c10fe716d90d38ac317e Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 30 Jul 2010 15:15:40 -0400 Subject: [PATCH] Added pyfile output to update_copyright.py. This creates hooke/license.py with licensing strings and functions. I've updated the copyright blurbs in the command line startup message and the GUI about message, but haven't actually added the advertised 'warrenty' and 'license' details yet. --- hooke/__init__.py | 7 ++ hooke/ui/__init__.py | 7 +- hooke/ui/commandline.py | 5 +- hooke/ui/gui/__init__.py | 5 +- update_copyright.py | 178 +++++++++++++++++++++++++++++++-------- 5 files changed, 160 insertions(+), 42 deletions(-) diff --git a/hooke/__init__.py b/hooke/__init__.py index f0447ad..c8b1253 100644 --- a/hooke/__init__.py +++ b/hooke/__init__.py @@ -26,6 +26,13 @@ The available submodules are: * :mod:`hooke.config` * :mod:`hooke.compat` """ +try: + from .license import LICENSE as __license__ +except ImportError, e: + import logging + logging.warn('Could not load LICENSE from hooke.license') + __license__ = 'All rights reserved.' + __version__ = (0, 9, 0, 'devel', None, 'Kenzo') """Version tuple:: diff --git a/hooke/ui/__init__.py b/hooke/ui/__init__.py index 47f9735..cdb220e 100644 --- a/hooke/ui/__init__.py +++ b/hooke/ui/__init__.py @@ -22,6 +22,7 @@ import ConfigParser as configparser from .. import version +from ..license import short_license from ..config import Setting from ..util.pluggable import IsSubclass, construct_odict @@ -92,13 +93,13 @@ class UserInterface (object): # Assorted useful tidbits for subclasses - def _splash_text(self): + def _splash_text(self, extra_info): return (""" Hooke version %s -COPYRIGHT +%s ---- -""" % version()).strip() +""" % (version(), short_license(extra_info))).strip() def _playlist_status(self, playlist): if len(playlist) > 0: diff --git a/hooke/ui/commandline.py b/hooke/ui/commandline.py index c5f3353..197a80e 100644 --- a/hooke/ui/commandline.py +++ b/hooke/ui/commandline.py @@ -411,7 +411,10 @@ class CommandLine (UserInterface): def run(self, commands, ui_to_command_queue, command_to_ui_queue): cmd = self._cmd(commands, ui_to_command_queue, command_to_ui_queue) - cmd.cmdloop(self._splash_text()) + cmd.cmdloop(self._splash_text(extra_info={ + 'get-warrenty':'run `warrenty`', + 'get-details':'run `license`', + })) def run_lines(self, commands, ui_to_command_queue, command_to_ui_queue, lines): diff --git a/hooke/ui/gui/__init__.py b/hooke/ui/gui/__init__.py index be0c7f2..3290584 100644 --- a/hooke/ui/gui/__init__.py +++ b/hooke/ui/gui/__init__.py @@ -242,7 +242,10 @@ class HookeFrame (wx.Frame): def _on_about(self, *args): dialog = wx.MessageDialog( parent=self, - message=self.gui._splash_text(), + message=self.gui._splash_text(extra_info={ + 'get-warrenty':'click "help->warrenty"', + 'get-details':'click "help->license"', + }), caption='About Hooke', style=wx.OK|wx.ICON_INFORMATION) dialog.ShowModal() diff --git a/update_copyright.py b/update_copyright.py index 6730a9c..ef2799a 100755 --- a/update_copyright.py +++ b/update_copyright.py @@ -29,8 +29,8 @@ import difflib import email.utils import os import os.path -import re import sys +import textwrap import time @@ -40,7 +40,7 @@ PROJECT_INFO = { } # Break "copyright" into "copy" and "right" to avoid matching the -# REGEXP. +# REGEXP if we decide to go back to regexps. COPY_RIGHT_TEXT=""" This file is part of %(project)s. @@ -59,6 +59,13 @@ License along with %(project)s. If not, see . """.strip() +SHORT_COPY_RIGHT_TEXT=""" +%(project)s comes with ABSOLUTELY NO WARRANTY; %(get-warrenty)s +for details. This is free software, and you are welcome to +redistribute it under certain conditions; %(get-details)s +for details. +""".strip() + COPY_RIGHT_TAG='-xyz-COPY' + '-RIGHT-zyx-' # unlikely to occur in the wild :p # Convert author names to canonical forms. @@ -194,13 +201,18 @@ if PROJECT_INFO['vcs'] == 'Git': status,stdout,stderr = invoke(['git'] + list(args)) return stdout.rstrip('\n') - def original_year(filename, year_hacks=YEAR_HACKS): - output = git_cmd('log', '--follow', - '--format=format:%ad', # Author date - '--date=short', # YYYY-MM-DD - filename) + def original_year(filename=None, year_hacks=YEAR_HACKS): + args = [ + '--format=format:%ad', # Author date + '--date=short', # YYYY-MM-DD + ] + if filename != None: + args.extend(['--follow', filename]) + output = git_cmd('log', *args) years = [int(line.split('-', 1)[0]) for line in output.splitlines()] - if splitpath(filename) in year_hacks: + if filename == None: + years.extend(year_hacks.values()) + elif splitpath(filename) in year_hacks: years.append(year_hacks[splitpath(filename)]) years.sort() return years[0] @@ -249,13 +261,18 @@ elif PROJECT_INFO['vcs'] == 'Mercurial': return (tmp_stdout.getvalue().rstrip('\n'), tmp_stderr.getvalue().rstrip('\n')) - def original_year(filename, year_hacks=YEAR_HACKS): - # shortdate filter: YEAR-MONTH-DAY - output,error = mercurial_cmd('log', '--follow', - '--template', '{date|shortdate}\n', - filename) + def original_year(filename=None, year_hacks=YEAR_HACKS): + args = [ + '--template', '{date|shortdate}\n', + # shortdate filter: YEAR-MONTH-DAY + ] + if filename != None: + args.extend(['--follow', filename]) + output,error = mercurial_cmd('log', *args) years = [int(line.split('-', 1)[0]) for line in output.splitlines()] - if splitpath(filename) in year_hacks: + if filename == None: + years.extend(year_hacks.values()) + elif splitpath(filename) in year_hacks: years.append(year_hacks[splitpath(filename)]) years.sort() return years[0] @@ -310,12 +327,17 @@ elif PROJECT_INFO['vcs'] == 'Bazaar': authors = revision.rev.get_apparent_authors() self.to_file.write('\n'.join(authors)+'\n') - def original_year(filename, year_hacks=YEAR_HACKS): + def original_year(filename=None, year_hacks=YEAR_HACKS): cmd = bzrlib.builtins.cmd_log() cmd.outf = StringIO.StringIO() - cmd.run(file_list=[filename], log_format=YearLogFormatter, levels=0) + kwargs = {'log_format':YearLogFormatter, 'levels':0} + if filename != None: + kwargs['file_list'] = [filenme] + cmd.run(**kwargs) years = [int(year) for year in set(cmd.outf.getvalue().splitlines())] - if splitpath(filename) in year_hacks: + if filename == None: + years.append(year_hacks.values()) + elif splitpath(filename) in year_hacks: years.append(year_hacks[splitpath(filename)]) years.sort() return years[0] @@ -356,6 +378,8 @@ def _strip_email(*args): Examples -------- + >>> _strip_email('J Doe') + ['J Doe'] >>> _strip_email('J Doe ') ['J Doe'] >>> _strip_email('J Doe ', 'JJJ Smith ') @@ -366,6 +390,8 @@ def _strip_email(*args): if arg == None: continue author,addr = email.utils.parseaddr(arg) + if author == '': + author = arg args[i] = author return args @@ -409,23 +435,16 @@ def _replace_aliases(authors, with_email=True, aliases=None): ... 'JJJ Smith ':['Jingly '], ... None:['Anonymous '], ... } - >>> _replace_aliases(['JJJ Smith ', 'Johnny ', - ... 'Jingly ', 'Anonymous '], - ... with_email=True, aliases=aliases) + >>> authors = [ + ... 'JJJ Smith ', 'Johnny ', + ... 'Jingly ', 'J Doe ', 'Anonymous '] + >>> _replace_aliases(authors, with_email=True, aliases=aliases) ['J Doe ', 'JJJ Smith '] - >>> _replace_aliases(['JJJ Smith', 'Johnny', 'Jingly', 'Anonymous'], - ... with_email=False, aliases=aliases) + >>> _replace_aliases(authors, with_email=False, aliases=aliases) ['J Doe', 'JJJ Smith'] - >>> _replace_aliases(['JJJ Smith ', 'Johnny ', - ... 'Jingly ', 'J Doe '], - ... with_email=True, aliases=aliases) - ['J Doe ', 'JJJ Smith '] """ if aliases == None: aliases = ALIASES - if with_email == False: - aliases = dict([(_strip_email(author)[0], _strip_email(*_aliases)) - for author,_aliases in aliases.items()]) rev_aliases = _reverse_aliases(aliases) for i,author in enumerate(authors): if author in rev_aliases: @@ -433,9 +452,41 @@ def _replace_aliases(authors, with_email=True, aliases=None): authors = sorted(list(set(authors))) if None in authors: authors.remove(None) + if with_email == False: + authors = _strip_email(*authors) return authors -def _copyright_string(original_year, final_year, authors, prefix=''): +def _long_author_formatter(copyright_year_string, authors): + """ + >>> print '\\n'.join(_long_author_formatter( + ... copyright_year_string='Copyright (C) 1990-2010', + ... authors=['Jack', 'Jill', 'John'])) + Copyright (C) 1990-2010 Jack + Jill + John + """ + lines = ['%s %s' % (copyright_year_string, authors[0])] + for author in authors[1:]: + lines.append(' '*(len(copyright_year_string)+1) + author) + return lines + +def _short_author_formatter(copyright_year_string, authors, **kwargs): + """ + >>> print '\\n'.join(_short_author_formatter( + ... copyright_year_string='Copyright (C) 1990-2010', + ... authors=['Jack', 'Jill', 'John']*5, + ... width=50)) + Copyright (C) 1990-2010 Jack, Jill, John, Jack, + Jill, John, Jack, Jill, John, Jack, Jill, John, + Jack, Jill, John + """ + blurb = '%s %s' % (copyright_year_string, ', '.join(authors)) + return textwrap.wrap(blurb, **kwargs) + +def _copyright_string(original_year, final_year, authors, prefix='', + text=COPY_RIGHT_TEXT, extra_info={}, + author_format_fn=_long_author_formatter, + formatter_kwargs={}): """ >>> print _copyright_string(original_year=2005, ... final_year=2005, @@ -454,17 +505,36 @@ def _copyright_string(original_year, final_year, authors, prefix=''): B This file... + >>> print _copyright_string(original_year=2005, + ... final_year=2005, + ... authors=['A ', 'B '], + ... prefix='', + ... text=SHORT_COPY_RIGHT_TEXT, + ... author_format_fn=_short_author_formatter, + ... extra_info={ + ... 'get-warrenty':'%(get-warrenty)s', + ... 'get-details':'%(get-details)s', + ... }, + ... formatter_kwargs={'width': 50}, + ... ) # doctest: +ELLIPSIS + Copyright (C) 2005 A , B + + Hooke comes with ABSOLUTELY NO WARRANTY; %(get-warrenty)s. + This is free software, and you are welcome to redistribute it + under certain conditions; %(get-details)s for details. """ if original_year == final_year: date_range = '%s' % original_year else: date_range = '%s-%s' % (original_year, final_year) - lines = ['Copyright (C) %s %s' % (date_range, authors[0])] - for author in authors[1:]: - lines.append(' '*(len('Copyright (C) ')+len(date_range)+1) + - author) + copyright_year_string = 'Copyright (C) %s' % date_range + lines = author_format_fn(copyright_year_string, authors, + **formatter_kwargs) lines.append('') - lines.extend((COPY_RIGHT_TEXT % PROJECT_INFO).splitlines()) + info = dict(PROJECT_INFO) + for key,value in extra_info.items(): + info[key] = value + lines.extend((text % info).splitlines()) for i,line in enumerate(lines): lines[i] = (prefix + line).rstrip() return '\n'.join(lines) @@ -623,6 +693,34 @@ def update_files(files=None, dry_run=False, verbose=0): continue update_file(filename, dry_run=dry_run, verbose=verbose) +def update_pyfile(path, original_year_fn=original_year, + authors_fn=authors_list, dry_run=False, verbose=0): + original_year = original_year_fn() + current_year = time.gmtime()[0] + authors = authors_fn() + authors = _replace_aliases(authors, with_email=False, aliases=ALIASES) + lines = [ + _copyright_string(original_year, current_year, authors, prefix='# '), + '', + 'LICENSE = """', + _copyright_string(original_year, current_year, authors, prefix=''), + '""".strip()', + '', + 'def short_license(extra_info):', + ' return """', + _copyright_string(original_year, current_year, authors, prefix='', + text=SHORT_COPY_RIGHT_TEXT, + author_format_fn=_short_author_formatter, + extra_info={ + 'get-warrenty':'%(get-warrenty)s', + 'get-details':'%(get-details)s', + }), + '""".strip() % extra_info', + ] + new_contents = '\n'.join(lines)+'\n' + _set_contents(path, new_contents, dry_run=dry_run, verbose=verbose) + + def test(): import doctest doctest.testmod() @@ -646,6 +744,9 @@ If no files are given, a list of files to update is generated automatically. """ % PROJECT_INFO p = optparse.OptionParser(usage) + p.add_option('--pyfile', dest='pyfile', default='hooke/license.py', + metavar='PATH', + help='Write project license info to a Python module at PATH') p.add_option('--test', dest='test', default=False, action='store_true', help='Run internal tests and exit') p.add_option('--dry-run', dest='dry_run', default=False, @@ -658,5 +759,8 @@ automatically. test() sys.exit(0) - update_authors(dry_run=options.dry_run, verbose=options.verbose) - update_files(files=args, dry_run=options.dry_run, verbose=options.verbose) + #update_authors(dry_run=options.dry_run, verbose=options.verbose) + #update_files(files=args, dry_run=options.dry_run, verbose=options.verbose) + if options.pyfile != None: + update_pyfile(path=options.pyfile, + dry_run=options.dry_run, verbose=options.verbose) -- 2.26.2