From 84f8c846bb15e717e6fb78b307ebe0a5580f7aae Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 16 Feb 2012 15:20:52 -0500 Subject: [PATCH] Add support for loading author_hacks, year_hacks, and aliases from the config. --- README | 36 +++++++++++++++- update_copyright/project.py | 85 ++++++++++++++++++++----------------- 2 files changed, 79 insertions(+), 42 deletions(-) diff --git a/README b/README index 674e039..5f0d468 100644 --- 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 , Jane Smith + + [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 : John Doe, jdoe, J. Doe + 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 diff --git a/update_copyright/project.py b/update_copyright/project.py index ac93c2f..0dab126 100644 --- a/update_copyright/project.py +++ b/update_copyright/project.py @@ -16,43 +16,7 @@ # along with update-copyright. If not, see # . -"""Project-specific configuration. - -# Convert author names to canonical forms. -# ALIASES[] = -# for example, -# ALIASES = { -# 'John Doe ': -# ['John Doe', 'jdoe', 'J. Doe '], -# } -# 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[] = [] = -# 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, -- 2.26.2