Added pyfile output to update_copyright.py.
authorW. Trevor King <wking@drexel.edu>
Fri, 30 Jul 2010 19:15:40 +0000 (15:15 -0400)
committerW. Trevor King <wking@drexel.edu>
Fri, 30 Jul 2010 19:15:40 +0000 (15:15 -0400)
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
hooke/ui/__init__.py
hooke/ui/commandline.py
hooke/ui/gui/__init__.py
update_copyright.py

index f0447adb638276c32706459f09a5ad5088c33430..c8b1253e99422cfba8f52d269855c8a81b50942c 100644 (file)
@@ -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::
index 47f97351d2f747bbaca71c30470e0e3fe64d5ed4..cdb220e625ef7ace3b37e30c2a487cc0aff9e9f7 100644 (file)
@@ -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:
index c5f3353b000d71a9e8dd8652dda0b6e09492efc1..197a80e9b6b1706ca68e81d9c0503d59adc69300 100644 (file)
@@ -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):
index be0c7f2a33b5ea5eff7883b6ce173495ea0e692d..329058431df7f4e4389540d9f76fc53acd43d0e4 100644 (file)
@@ -242,7 +242,10 @@ class HookeFrame (wx.Frame):
     def _on_about(self, *args):\r
         dialog = wx.MessageDialog(\r
             parent=self,\r
-            message=self.gui._splash_text(),\r
+            message=self.gui._splash_text(extra_info={\r
+                    'get-warrenty':'click "help->warrenty"',\r
+                    'get-details':'click "help->license"',\r
+                    }),\r
             caption='About Hooke',\r
             style=wx.OK|wx.ICON_INFORMATION)\r
         dialog.ShowModal()\r
index 6730a9caacb2180566c15d20a1e9caf4f67e0128..ef2799a7955ba994c5ec028d8745e76e1f2e8184 100755 (executable)
@@ -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
 <http://www.gnu.org/licenses/>.
 """.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 <jdoe@a.com>')
     ['J Doe']
     >>> _strip_email('J Doe <jdoe@a.com>', 'JJJ Smith <jjjs@a.com>')
@@ -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 <jjjs@a.com>':['Jingly <jjjs@b.edu>'],
     ...     None:['Anonymous <a@a.com>'],
     ...     }
-    >>> _replace_aliases(['JJJ Smith <jjjs@a.com>', 'Johnny <jdoe@b.edu>',
-    ...                   'Jingly <jjjs@b.edu>', 'Anonymous <a@a.com>'],
-    ...                  with_email=True, aliases=aliases)
+    >>> authors = [
+    ...     'JJJ Smith <jjjs@a.com>', 'Johnny <jdoe@b.edu>',
+    ...     'Jingly <jjjs@b.edu>', 'J Doe <jdoe@a.com>', 'Anonymous <a@a.com>']
+    >>> _replace_aliases(authors, with_email=True, aliases=aliases)
     ['J Doe <jdoe@a.com>', 'JJJ Smith <jjjs@a.com>']
-    >>> _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 <jjjs@a.com>', 'Johnny <jdoe@b.edu>',
-    ...                   'Jingly <jjjs@b.edu>', 'J Doe <jdoe@a.com>'],
-    ...                  with_email=True, aliases=aliases)
-    ['J Doe <jdoe@a.com>', 'JJJ Smith <jjjs@a.com>']
     """
     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 <b@b.edu>
     <BLANKLINE>
     This file...
+    >>> print _copyright_string(original_year=2005,
+    ...                         final_year=2005,
+    ...                         authors=['A <a@a.com>', 'B <b@b.edu>'],
+    ...                         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 <a@a.com>, B <b@b.edu>
+    <BLANKLINE>
+    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)