Add support for loading author_hacks, year_hacks, and aliases from the config.
authorW. Trevor King <wking@drexel.edu>
Thu, 16 Feb 2012 20:20:52 +0000 (15:20 -0500)
committerW. Trevor King <wking@drexel.edu>
Thu, 16 Feb 2012 20:20:52 +0000 (15:20 -0500)
README
update_copyright/project.py

diff --git a/README b/README
index 674e039fcd18afed4b4f75510c5303ceb2fcfeb0..5f0d468616e23a7d8ab626b732274264c861906f 100644 (file)
--- a/README
+++ b/README
@@ -38,7 +38,7 @@ Installing by hand
 See the homepage_ for details.  To install the checkout, run the
 standard::
 
-    $ python setup.py install
+  $ python setup.py install
 
 Usage
 =====
@@ -114,12 +114,43 @@ is replaced by a new blurb, which is automatically generated from your
 configured long copyright string, with author names and edit years
 extracted from the VCS data for that file.
 
+Incomplete VCS history
+----------------------
+
+Sometimes files have authors or alterations not recorded in a
+project's VCS history.  You can use the ``author-hacks`` section to
+add authors to a file, and the ``year-hacks`` section to adjust the
+files original year.  Author names should be comma-separated.  For
+example::
+
+  [author-hacks]
+  path/to/file: John Doe <jdoe@a.com>, Jane Smith <jsmith@b.net>
+
+  [year-hacks]
+  path/to/another/file: 2009
+
+Add entries for as many files as you like.  Paths should be relative
+to your project root.  Always use forward slashes (``/``) to separate
+path elements.
+
+Aliases
+-------
+
+Occasionally names or email addresses used when committing to the VCS
+will go out of date.  Some VCSs have a built-in method of dealing with
+this (e.g. Git's .mailmap_).  For those without such a VCS, you can
+add an `aliases`` section to your config file, where the option names
+are the canonical name of the ...?.  For example::
+
+  [aliases]
+  John Doe <jdoe@a.com>: John Doe, jdoe, J. Doe <j@doe.net>
+
 Testing
 =======
 
 Run the internal unit tests with::
 
-    $ nosetests --with-doctest --doctest-tests update_copyright
+  $ nosetests --with-doctest --doctest-tests update_copyright
 
 Licence
 =======
@@ -144,4 +175,5 @@ wking@drexel.edu
   http://docs.python.org/dev/library/configparser.html#configparser.RawConfigParser
 .. _syntax documentation:
   http://docs.python.org/dev/library/configparser.html#supported-ini-file-structure
+.. _.mailmap: http://schacon.github.com/git/git-shortlog.html#_mapping_authors
 .. _GNU General Public License Version 3: http://www.gnu.org/licenses/gpl.html
index ac93c2ff069b20df7ce8c8823b73f844813418db..0dab1261117905b60fd13fa98139fce084d5ad12 100644 (file)
 # along with update-copyright.  If not, see
 # <http://www.gnu.org/licenses/>.
 
-"""Project-specific configuration.
-
-# Convert author names to canonical forms.
-# ALIASES[<canonical name>] = <list of aliases>
-# for example,
-# ALIASES = {
-#     'John Doe <jdoe@a.com>':
-#         ['John Doe', 'jdoe', 'J. Doe <j@doe.net>'],
-#     }
-# Git-based projects are encouraged to use .mailmap instead of
-# ALIASES.  See git-shortlog(1) for details.
-
-# List of paths that should not be scanned for copyright updates.
-# IGNORED_PATHS = ['./.git/']
-IGNORED_PATHS = ['./.git']
-# List of files that should not be scanned for copyright updates.
-# IGNORED_FILES = ['COPYING']
-IGNORED_FILES = ['COPYING']
-
-# Work around missing author holes in the VCS history.
-# AUTHOR_HACKS[<path tuple>] = [<missing authors]
-# for example, if John Doe contributed to module.py but wasn't listed
-# in the VCS history of that file:
-# AUTHOR_HACKS = {
-#     ('path', 'to', 'module.py'):['John Doe'],
-#     }
-AUTHOR_HACKS = {}
-
-# Work around missing year holes in the VCS history.
-# YEAR_HACKS[<path tuple>] = <original year>
-# for example, if module.py was published in 2008 but the VCS history
-# only goes back to 2010:
-# YEAR_HACKS = {
-#     ('path', 'to', 'module.py'):2008,
-#     }
-YEAR_HACKS = {}
-"""
+"""Project-specific configuration."""
 
 import ConfigParser as _configparser
 import fnmatch as _fnmatch
@@ -78,6 +42,9 @@ class Project (object):
                  short_copyright=None):
         self._name = name
         self._vcs = vcs
+        self._author_hacks = None
+        self._year_hacks = None
+        self._aliases = None
         self._copyright = None
         self._short_copyright = None
         self.with_authors = False
@@ -112,16 +79,21 @@ class Project (object):
         except _configparser.NoOptionError:
             pass
         else:
+            kwargs = {
+                'author_hacks': self._author_hacks,
+                'year_hacks': self._year_hacks,
+                'aliases': self._aliases,
+                }
             if vcs == 'Git':
-                self._vcs = _GitBackend()
+                self._vcs = _GitBackend(**kwargs)
             elif vcs == 'Bazaar':
                 if _BazaarBackend is None:
                     raise _bazaar_import_error
-                self._vcs = _BazaarBackend()
+                self._vcs = _BazaarBackend(**kwargs)
             elif vcs == 'Mercurial':
                 if _MercurialBackend is None:
                     raise _mercurial_import_error
-                self._vcs = _MercurialBackend()
+                self._vcs = _MercurialBackend(**kwargs)
             else:
                 raise NotImplementedError('vcs: {}'.format(vcs))
 
@@ -156,6 +128,39 @@ class Project (object):
         except _configparser.NoOptionError:
             pass
 
+    def _load_author_hacks_conf(self, parser, encoding=None):
+        if encoding is None:
+            encoding = self._encoding or _utils.ENCODING
+        author_hacks = {}
+        for path in parser.options('author-hacks'):
+            authors = parser.get('author-hacks', path)
+            author_hacks[tuple(path.split('/'))] = set(
+                unicode(a.strip(), encoding) for a in authors.split(','))
+        self._author_hacks = author_hacks
+        if self._vcs is not None:
+            self._vcs._author_hacks = self._author_hacks
+
+    def _load_year_hacks_conf(self, parser):
+        year_hacks = {}
+        for path in parser.options('year-hacks'):
+            year = parser.get('year-hacks', path)
+            year_hacks[tuple(path.split('/'))] = int(year)
+        self._year_hacks = year_hacks
+        if self._vcs is not None:
+            self._vcs._year_hacks = self._year_hacks
+
+    def _load_aliases_conf(self, parser, encoding=None):
+        if encoding is None:
+            encoding = self._encoding or _utils.ENCODING
+        aliases = {}
+        for author in parser.options('aliases'):
+            _aliases = parser.get('aliases', author)
+            aliases[author] = set(
+                unicode(a.strip(), encoding) for a in _aliases.split(','))
+        self._aliases = aliases
+        if self._vcs is not None:
+            self._vcs._aliases = self._aliases
+
     def _info(self):
         return {
             'project': self._name,