a909c2ba47f0

GTFO whitespace.
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Fri, 11 Jun 2010 20:07:20 -0400
parents a55ffc78928c
children b0482e5ac2bf 1bc531c0495e
branches/tags (none)
files review/api.py review/tests/test_comment.py review/tests/test_diffs.py review/tests/test_init.py review/tests/test_signoff.py review/tests/util.py

Changes

--- a/review/api.py	Fri Jun 11 19:59:34 2010 -0400
+++ b/review/api.py	Fri Jun 11 20:07:20 2010 -0400
@@ -8,28 +8,27 @@
 from mercurial.node import hex
 from mercurial import ui as _ui
 
-
 try:
     from os.path import relpath
 except ImportError: # python < 2.6
     from os.path import curdir, abspath, sep, commonprefix, pardir, join
     def relpath(path, start=curdir):
         """Return a relative version of a path"""
-        
+
         if not path:
             raise ValueError("no path specified")
-        
+
         start_list = abspath(start).split(sep)
         path_list = abspath(path).split(sep)
-        
+
         # Work out how much of the filepath is shared by start and path.
         i = len(commonprefix([start_list, path_list]))
-        
+
         rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
         if not rel_list:
             return curdir
         return join(*rel_list)
-    
+
 
 DEFAULT_DATASTORE_DIRNAME = os.path.join('.hg', 'review')
 
@@ -38,20 +37,19 @@
     def __init__(self, committed):
         super(PreexistingDatastore, self).__init__()
         self.committed = committed
-    
 
 class UninitializedDatastore(Exception):
     """Raised when trying to access a datastore that does not exist.
-    
+
     The committed attribute will be True if someone else has already
     initialized code review for the target (i.e. the .hgreview file is present
     in the target repository), or False otherwise.
-    
+
     """
     def __init__(self, committed):
         super(UninitializedDatastore, self).__init__()
         self.committed = committed
-    
+
 
 class DatastoreRequiresRemotePath(Exception):
     """Raised when initializing a fresh datastore without a remote path."""
@@ -75,15 +73,14 @@
     def __init__(self, filename):
         super(FileNotInChangeset, self).__init__()
         self.filename = filename
-    
 
 
 def _split_path_dammit(p):
     """Take a file path (from the current platform) and split it.  Really.
-    
+
     os.path doesn't seem to have an easy way to say "Split this path into a
     list of pieces."
-    
+
     >>> _split_path_dammit('')
     []
     >>> _split_path_dammit('one')
@@ -94,22 +91,22 @@
     ['one', 'two', 'three']
     >>> _split_path_dammit('one/two/three.py')
     ['one', 'two', 'three.py']
-    
+
     """
     def _spd(p):
         p, i = os.path.split(p)
         while i or p:
             yield i
             p, i = os.path.split(p)
-    
+
     return filter(None, list(_spd(p)))[::-1]
 
 def _parse_hgrf(repo):
     """Parse the .hgreview file and return the data inside.
-    
+
     The .hgreview file will be pulled from the tip revision of the given
     repository.  If it is not committed it will not be found!
-    
+
     """
     data = {}
     hgrd = repo['tip']['.hgreview'].data().split('\n')
@@ -118,22 +115,22 @@
         label, _, path = [i.strip() for i in line.partition('=')]
         if label == 'remote':
             data['rpath'] = path
-    
+
     return data
 
 def _commitfunc(ui, repo, message, match, opts):
     """A function used by the guts of Mercurial.
-    
+
     Mercurial needs a "commit function" parameter when using cmdutil.commit.
     This is a simple function for *only* that purpose.
-    
+
     """
     return repo.commit(message, opts.get('user'), opts.get('date'), match)
 
 def _parse_data(data):
     """Parse the data (string) of a stored _ReviewObject and return a dict."""
     meta, _, message = data.partition('\n\n')
-    
+
     data = {}
     for m in meta.split('\n'):
         label, _, val = m.partition(':')
@@ -143,16 +140,16 @@
 
 def _datetime_from_hgdate(hgdate):
     """Return a datetime.datetime for the given Mecurial-style date tuple.
-    
+
     It will have NO timezone information -- the date and time are what a clock
     next to the current computer would have read at the instant represented
     by the Mercurial-style date!
-    
+
     """
     offset = abs(hgdate[1] - util.makedate()[1])
     later = offset < 0
     offset = datetime.timedelta(seconds=offset)
-    
+
     conversion_format = '%Y-%m-%d %H:%M:%S'
     bare = util.datestr(hgdate, format=conversion_format)
     bare_datetime = datetime.datetime.strptime(bare, conversion_format)
@@ -164,17 +161,17 @@
 
 def sanitize_path(p, repo=None):
     """Sanitize a (platform-specific) path.
-    
+
     If no repository is given, the path's separators will be replaced with
     forward slashes (the form Mercurial uses internally).
-    
+
     If a repository is given, the result will be relative to the root of the
     repository.  This is useful for turning relative paths into normalized
     paths that can be used to look up files from a changectx.
-    
+
     This function is idempotent.  If you sanitize a path multiple times
     against the same repository the result will not change.
-    
+
     """
     if repo:
         p = relpath(os.path.realpath(p), start=repo.root)
@@ -185,36 +182,36 @@
     """The code review data for a particular repository."""
     def __init__(self, ui, repo, lpath=None, rpath=None, create=False):
         """Initialize a ReviewDatastore for a Mercurial repository.
-        
+
         To get a ReviewDatastore for a repository that has already been
         initialized for code reviewing:
-        
+
             review_data = ReviewDatastore(ui, repo)
-        
+
         To set up a repository to support code review:
-        
+
             review_data = ReviewDatastore(ui, repo, create=True)
-        
+
         If you want to specify your own path to the code review repository for
         this repo, pass the FULL path to the repository as the lpath parameter.
-        
+
         Error handling is a bit tricky at the moment.  I need to refactor
         and/or document this.
-        
+
         """
         self.ui = ui
         self.target = repo
         self.lpath = lpath or os.path.join(
             self.target.root, DEFAULT_DATASTORE_DIRNAME
         )
-        
+
         if not create:
             if not '.hgreview' in repo['tip']:
                 raise UninitializedDatastore(False)
-            
+
             data = _parse_hgrf(repo)
             self.rpath = data['rpath']
-            
+
             try:
                 self.repo = hg.repository(_ui.ui(), self.lpath)
             except error.RepoError:
@@ -222,10 +219,10 @@
         elif '.hgreview' in repo['tip']:
             data = _parse_hgrf(self.target)
             self.rpath = data['rpath']
-            
+
             if self.rpath.startswith('.'):
                 raise RelativeRemotePath
-            
+
             try:
                 hg.repository(ui, self.lpath)
             except error.RepoError:
@@ -241,64 +238,63 @@
                 raise RelativeRemotePath
             else:
                 self.rpath = rpath
-            
+
             with open(os.path.join(self.target.root, '.hgreview'), 'w') as hgrf:
                 hgrf.write('remote = %s\n' % self.rpath)
             self.target.add(['.hgreview'])
-            
+
             self.repo = hg.repository(ui, self.lpath, create)
-    
+
     def __getitem__(self, rev):
         """Return a ReviewChangeset for the given revision."""
         node = hex(self.target[str(rev)].node())
         return ReviewChangeset(self.ui, self.repo, self.target, node)
-    
 
 class ReviewChangeset(object):
     """The review data about one changeset in the target repository.
-    
+
     Individual changesets can be retrieved from a ReviewDatastore.
-    
+
     Each ReviewChangeset stores a list of ReviewComment objects and a list
     of ReviewSignoff objects:
-    
+
         rcset = rd['tip']
         rcset.comments
         rcset.signoffs
-    
+
     Comments and signoffs should be added to a changeset by using the
     add_comment and add_signoff methods:
-    
+
         rcset = rd['tip']
         rcset.add_comment(...)
         rcset.add_signoff(...)
-    
+
     Diffs for files modified in a changeset can be retrived with the diffs
     and full_diffs methods.  See the docs of those methods for more info.
-    
+
     """
     def __init__(self, ui, repo, target, node):
         """Initialize a ReviewChangeset.
-        
+
         You shouldn't need to create these directly -- use a ReviewDatastore
         object to get them:
-        
+
             review_data = ReviewDatastore(ui, repo)
             tip_review_data = review_data['tip']
-        
+
         """
         self.repo = repo
         self.target = target
         self.ui = ui
         self.node = node
-        
+
         if '%s/.exists' % self.node in self.repo['tip']:
             _match = lambda p: lambda fn: fn.startswith(p)
-            
+
             relevant = filter(_match(node), self.repo['tip'])
             commentfns = filter(_match('%s/comments' % node), relevant)
             signofffns = filter(_match('%s/signoffs' % node), relevant)
-            
+
             self.comments = []
             for fn in commentfns:
                 data = _parse_data(self.repo['tip'][fn].data())
@@ -308,7 +304,7 @@
                 data['identifier'] = _split_path_dammit(fn)[-1]
                 self.comments.append(ReviewComment(**data))
             self.comments.sort(key=operator.attrgetter('local_datetime'))
-            
+
             self.signoffs = []
             for fn in signofffns:
                 data = _parse_data(self.repo['tip'][fn].data())
@@ -319,77 +315,77 @@
         else:
             self.comments = []
             self.signoffs = []
-            
+
             path = os.path.join(self.repo.root, self.node)
             os.mkdir(path)
             with open(os.path.join(path, '.exists'), 'w') as e:
                 pass
-            
+
             cmdutil.commit(ui, self.repo, _commitfunc,
                 [os.path.join(path, '.exists')],
                 { 'message': 'Initialize review data for changeset %s' % self.node,
                   'addremove': True, })
-    
+
     def signoffs_for_user(self, username):
         return filter(lambda s: s.author == username, self.signoffs)
-    
+
     def signoffs_for_current_user(self):
         return self.signoffs_for_user(self.ui.username())
-    
+
     def add_signoff(self, message, opinion='', force=False):
         """Add (and commit) a signoff for the given revision.
-        
+
         The opinion argument should be 'yes', 'no', or ''.
-        
+
         If a signoff from the user already exists, a SignoffExists exception
         will be raised unless the force argument is used.
-        
+
         """
         existing = self.signoffs_for_current_user()
-        
+
         if existing:
             if not force:
                 raise SignoffExists
             existing[0]._delete(self.ui, self.repo)
-        
+
         signoff = ReviewSignoff(self.ui.username(), util.makedate(),
             self.node, opinion, message)
         signoff._commit(self.ui, self.repo)
-    
+
     def add_comment(self, message, filename='', lines=[]):
         """Add (and commit) a comment for the given file and lines.
-        
+
         The filename should be normalized to the format Mercurial expects,
         that is: relative to the root of the repository and using forward
         slashes as the separator.  Paths can be converted with the
         sanitize_path function in this module.
-        
+
         If the comment is on one or more lines, a filename *must* be given.
-        
+
         Line numbers should be passed as a list, even if there is only one.
         See the full_diffs function for how to refer to line numbers.
-        
+
         """
         if filename and filename not in self.target[self.node].files():
             raise FileNotInChangeset(filename)
-        
+
         comment = ReviewComment(self.ui.username(), util.makedate(),
             self.node, filename, lines, message)
         comment._commit(self.ui, self.repo)
-    
-    
+
+
     def full_diffs(self, filenames=None, opts={}):
         """Return full diffs of the given files (or all files).
-        
+
         If the filenames argument is not used, diffs for every file in the
         changeset will be returned.
-        
+
         The diffs are returned as a dictionary in the form:
-        
+
             { 'filename': 'string of the diff' }
-        
+
         All headers are stripped, so the an entire diff looks like this:
-        
+
              unchanged line
              unchanged line
             -removed line
@@ -400,52 +396,52 @@
             -removed line
              unchanged line
              unchanged line
-        
+
         When adding a comment, the line number given should be the line
         number from this diff (starting at 0).  To comment on the first two
         removed lines in the above example you would pass [2, 3].
-        
+
         """
-        
+
         target_files = self.target[self.node].files()
         if not filenames:
             filenames = target_files
         else:
             filenames = filter(lambda f: self.has_diff(f), filenames)
-        
+
         opts['unified'] = '100000'
         node2 = self.node
         node1 = self.target[node2].parents()[0].node()
-        
+
         diffs = {}
         for filename in filenames:
             m = cmdutil.matchfiles(self.target, [filename])
             d = patch.diff(self.target, node1, node2, match=m,
                 opts=patch.diffopts(self.ui, opts))
-            
+
             # patch.diff will give us back a generator with two items
             # the first is the diff --git header, which we don't care about
             d.next()
-            
+
             # the second is the diff's contents, which is what we want,
             # minus the header
             diffs[filename] = '\n'.join(d.next().splitlines()[3:])
-        
+
         return diffs
-    
+
     def diffs(self, filenames=None, context=5):
         """Return a mapping of diff lines for the given files (or all).
-        
+
         If the filenames argument is not used, diffs for every file in the
         changeset will be returned.
-        
+
         The diffs are returned in a dictionary of the form:
-        
-        { 
+
+        {
             'filename': {
                 # the line number of the last line of the FULL diff
                 'max': 90,
-                
+
                 # A sorted list of tuples of (line_number, line_content)
                 'content': [
                     (10, ' context line'),
@@ -463,70 +459,70 @@
                 ],
             },
         }
-        
+
         There's a lot of structure there, but it will provide everything you
         need to display contextualized diffs.
         """
-        
+
         ds = self.full_diffs(filenames, {})
-        
+
         def _filter_diff(d):
             for n, line in enumerate(d):
                 start = n - context if n > context else 0
                 end = n + context + 1
                 if any(filter(lambda l: l[0] in '+-', d[start:end])):
                     yield (n, line)
-        
+
         for filename, content in ds.iteritems():
             content = content.splitlines()
             ds[filename] = {
                 'max': len(content) - 1,
                 'content': list(_filter_diff(content)),
             }
-        
+
         return ds
-    
+
     def annotated_diff(self, filename, context=5):
         """Return a generator that yields annotated lines of a diff.
-        
+
         The first item yielded will be a simple integer of the last line
         number of the diff.  This is ugly but useful when creating monospaced
         line-number-prefixed output.
-        
+
         Each line yielded will be of the form:
-        
+
         {
             # If 'skipped' is not None, this line is a "skip" line, which
             # represents a group of lines that were skipped due to context.
             'skipped': 23,
-            
+
             # The line number of this line, or None for skip lines.
             'number': 585,
-            
+
             # The actual content of this line, or None for skip lines.
             'content': '+added line',
-            
+
             # Any comments that apply to this line.
             # If the line is a skip line, this will be any comments that apply
             # to any line in the skipped group.
             'comments': [ReviewComment(), ReviewComment()],
         }
         """
-        
+
         diffs = self.diffs([filename], context).values()
         if not diffs:
             return
-        
+
         diff = diffs[0]
         max_line, content = diff['max'], diff['content']
         line_level_comments = self.line_level_comments(filename)
         previous_n = -1
-        
+
         if content:
             yield content[-1][0]
         else:
             yield 0
-        
+
         for n, line in content:
             if n - 1 > previous_n:
                 yield {
@@ -537,7 +533,7 @@
                         line_level_comments
                     ),
                 }
-            
+
             yield {
                 'skipped': None,
                 'number': n, 'content': line,
@@ -545,9 +541,9 @@
                     lambda c: max(c.lines) == n, line_level_comments
                 )
             }
-            
+
             previous_n = n
-        
+
         if previous_n < max_line:
             yield {
                 'skipped': max_line - previous_n,
@@ -557,26 +553,26 @@
                     line_level_comments
                 ),
             }
-    
+
     def has_diff(self, filename):
         """Return whether the given filename has a diff in this revision."""
         return filename in self.files()
-    
+
     def files(self):
         """Return the list of files in the revision for this ReviewChangeset."""
         return self.target[self.node].files()
-    
-    
+
+
     def review_level_comments(self):
         """Comments on this changeset which aren't on a particular file."""
         return filter(lambda c: not c.filename, self.comments)
-    
+
     def file_level_comments(self, filename=None):
         """Comments on this changeset that are on a file, but not a line.
-        
+
         If a file is given only comments for that file will be returned.
         """
-        
+
         if filename:
             return filter(
                 lambda c: filename == c.filename and not c.lines, self.comments
@@ -585,13 +581,13 @@
             return filter(
                 lambda c: filename and not c.lines, self.comments
             )
-    
+
     def line_level_comments(self, filename=None):
         """Comments on this changeset that are on a line of file.
-        
+
         If a file is given only comments for that file will be returned.
         """
-        
+
         if filename:
             return filter(
                 lambda c: filename == c.filename and c.lines, self.comments
@@ -600,7 +596,6 @@
             return filter(
                 lambda c: filename and c.lines, self.comments
             )
-    
 
 class _ReviewObject(object):
     """A base object for some kind of review data (a signoff or comment)."""
@@ -608,52 +603,51 @@
         self.container = container
         self.commit_message = commit_message
         self.delete_message = delete_message
-    
+
     def _commit(self, ui, repo):
         """Write and commit this object to the given repo."""
-        
+
         path = os.path.join(repo.root, self.node, self.container)
         if not os.path.exists(path):
             os.mkdir(path)
-        
+
         data = self._render_data()
         filename = util.sha1(data).hexdigest()
         objectpath = os.path.join(path, filename)
-        
+
         with open(objectpath, 'w') as objectfile:
             objectfile.write(data)
-        
+
         cmdutil.commit(ui, repo, _commitfunc, [objectpath],
             { 'message': self.commit_message % self.node, 'addremove': True, })
-    
+
     def _delete(self, ui, repo):
         """Delete and commit this object in the given repo."""
-        
+
         if not self.delete_message:
             raise CannotDeleteObject
-        
+
         data = self._render_data()
         filename = util.sha1(data).hexdigest()
         objectpath = os.path.join(repo.root, self.node, self.container, filename)
-        
+
         os.remove(objectpath)
-        
+
         cmdutil.commit(ui, repo, _commitfunc, [objectpath],
             { 'message': self.delete_message % self.node, 'addremove': True, })
-    
-    
+
+
     @property
     def local_datetime(self):
         return _datetime_from_hgdate(self.hgdate)
-    
 
 class ReviewComment(_ReviewObject):
     """A single review comment.
-    
+
     A list of comments can be retrieved from a ReviewChangeset.
-    
+
     The following pieces of information are stored for comments:
-    
+
         comment = rcset.comments[0]
         comment.author
         comment.hgdate
@@ -663,30 +657,30 @@
         comment.message
         comment.local_datetime
         comment.identifier
-    
+
     Each item is a string, except for lines, hgdate, and local_datetime.
-    
+
     lines is a list of ints.
-    
+
     hgdate is a tuple of (seconds from the epoch, seconds offset from UTC),
     which is the format Mercurial itself uses internally.
-    
+
     local_datetime is a datetime object representing what a clock on the wall
     next to the current computer would have read at the instant the comment
     was added.
-    
+
     """
     def __init__(self, author, hgdate, node, filename, lines, message, identifier=None, **extra):
         """Initialize a ReviewComment.
-        
+
         You shouldn't need to create these directly -- use a ReviewChangeset
         to add comments and retrieve existing ones:
-        
+
             review_data = ReviewDatastore(ui, repo)
             tip_review = review_data['tip']
             tip_review.add_comment(...)
             tip_comments = tip_review.comments
-        
+
         """
         super(ReviewComment, self).__init__(
             container='comments', commit_message=messages.COMMIT_COMMENT,
@@ -698,22 +692,22 @@
         self.lines = lines
         self.message = message
         self.identifier = identifier
-    
+
     def _render_data(self):
         """Render the data of this comment into a string for writing to disk.
-        
+
         You probably don't need to call this directly, the add_comment method
         of a ReviewChangeset will handle it for you.
-        
+
         """
         rendered_date = util.datestr(self.hgdate)
         lines = ','.join(self.lines)
         return file_templates.COMMENT_FILE_TEMPLATE % ( self.author, rendered_date,
             self.node, self.filename, lines, self.message )
-    
+
     def __str__(self):
         """Stringify this comment for easy printing (for debugging)."""
-        
+
         return '\n'.join(map(str, [
             self.author,
             self.hgdate,
@@ -723,15 +717,14 @@
             self.message,
             '\n',
         ]))
-    
 
 class ReviewSignoff(_ReviewObject):
     """A single review signoff.
-    
+
     A list of signoffs can be retrieved from a ReviewChangeset.
-    
+
     The following pieces of information are stored for signoffs:
-    
+
         signoff = rcset.comments[0]
         signoff.author
         signoff.hgdate
@@ -739,28 +732,28 @@
         signoff.opinion
         signoff.message
         signoff.identifier
-    
+
     Each item is a string, except for hgdate and local_datetime.
-    
+
     hgdate is a tuple of (seconds from the epoch, seconds offset from UTC),
     which is the format Mercurial itself uses internally.
-    
+
     local_datetime is a datetime object representing what a clock on the wall
     next to the current computer would have read at the instant the signoff
     was added.
-    
+
     """
     def __init__(self, author, hgdate, node, opinion, message, identifier=None, **extra):
         """Initialize a ReviewSignoff.
-        
+
         You shouldn't need to create these directly -- use a ReviewChangeset
         to add signoffs and retrieve existing ones:
-        
+
             review_data = ReviewDatastore(ui, repo)
             tip_review = review_data['tip']
             tip_review.add_signoff(...)
             tip_signoffs = tip_review.signoffs
-        
+
         """
         super(ReviewSignoff, self).__init__(
             container='signoffs', commit_message=messages.COMMIT_SIGNOFF,
@@ -772,16 +765,16 @@
         self.opinion = opinion
         self.message = message
         self.identifier = identifier
-    
+
     def _render_data(self):
         """Render the data of this signoff into a string for writing to disk.
-        
+
         You probably don't need to call this directly, the add_signoff method
         of a ReviewChangeset will handle it for you.
-        
+
         """
         rendered_date = util.datestr(self.hgdate)
         return file_templates.SIGNOFF_FILE_TEMPLATE % ( self.author, rendered_date,
             self.node, self.opinion, self.message )
-    
 
+
--- a/review/tests/test_comment.py	Fri Jun 11 19:59:34 2010 -0400
+++ b/review/tests/test_comment.py	Fri Jun 11 20:07:20 2010 -0400
@@ -6,7 +6,6 @@
 from mercurial import util as hgutil
 from mercurial.node import hex
 
-
 a1, a2 = (messages.REVIEW_LOG_COMMENT_AUTHOR % '|').split('|')
 
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
@@ -14,7 +13,6 @@
     output = review()
     assert messages.REVIEW_LOG_COMMENTS % (0, 0) in output
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_blank_comment():
     try:
@@ -25,77 +23,73 @@
     else:
         assert False, 'The correct error message was not printed.'
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_add_comments_to_parent_rev():
     review(comment=True, message='Test comment one.')
-    
+
     output = review()
     assert messages.REVIEW_LOG_COMMENTS % (1, 1) in output
-    
+
     assert a1 in output
     assert a2 in output
-    
+
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' in output
-    
+
     review(comment=True, message='Test comment two.')
-    
+
     output = review()
     assert messages.REVIEW_LOG_COMMENTS % (2, 1) in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment two.' in output
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_add_comments_to_specific_rev():
     review(comment=True, message='Test comment one.', rev='0')
-    
+
     output = review(rev='0')
     assert messages.REVIEW_LOG_COMMENTS % (1, 1) in output
-    
+
     assert a1 in output
     assert a2 in output
-    
+
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' in output
-    
+
     output = review()
     assert messages.REVIEW_LOG_COMMENTS % (0, 0) in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' not in output
-    
+
     review(comment=True, message='Test comment two.', rev='0')
-    
+
     output = review(rev='0')
     assert messages.REVIEW_LOG_COMMENTS % (2, 1) in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment two.' in output
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_add_comments_to_file():
     review(comment=True, message='Test comment one.', rev='1', files=['file_one'])
-    
+
     output = review(rev='1', files=['file_one'])
     assert a1 in output
     assert a2 in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' in output
-    
+
     output = review(rev='1', files=['file_two'])
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' not in output
-    
+
     output = review(rev='0', files=['file_one'])
     assert a1 not in output
     assert a2 not in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' not in output
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_add_comments_to_multiple_files():
     review(comment=True, message='Test comment.', rev='1',
         files=['file_one', 'always_changing'])
-    
+
     output = review(rev='1')
     assert output.count(messages.REVIEW_LOG_COMMENT_LINE % 'Test comment.') == 2
-    
+
     try:
         review(comment=True, rev='1', message='Test bad comment.', lines='1',
             files=['file_one', 'always_changing'])
@@ -105,7 +99,6 @@
     else:
         assert False, 'The correct error message was not printed.'
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_add_comments_to_bad_file():
     try:
@@ -116,7 +109,6 @@
     else:
         assert False, 'The correct error message was not printed.'
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_add_comments_to_file_line():
     try:
@@ -126,17 +118,17 @@
         assert messages.COMMENT_LINES_REQUIRE_FILE in error
     else:
         assert False, 'The correct error message was not printed.'
-    
+
     review(comment=True, rev='1', message='Test comment one.',
         files=['file_one'], lines='1')
-    
+
     output = review(rev='1', files=['file_one'])
-    
+
     # Make sure the comment is present at all.
     assert a1 in output
     assert a2 in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' in output
-    
+
     # Make sure it's in the correct place
     output = output.splitlines()
     for n, line in enumerate(output):
@@ -144,19 +136,18 @@
             assert output[n-1].strip().startswith('1')
             break
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_add_comments_to_file_lines():
     review(comment=True, rev='1', message='Test comment one.',
         files=['file_one'], lines='1,2')
-    
+
     output = review(rev='1', files=['file_one'])
-    
+
     # Make sure the comment is present at all.
     assert a1 in output
     assert a2 in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' in output
-    
+
     # Make sure it's in the correct place
     output = output.splitlines()
     for n, line in enumerate(output):
@@ -164,96 +155,92 @@
             assert output[n-1].strip().startswith('2')
             break
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_add_comments_to_file_in_subdir():
     filename = os.path.join('test_dir', 'test_file')
-    
+
     review(comment=True, message='Test comment one.', rev='1', files=[filename])
-    
+
     output = review(rev='1', files=[filename])
     assert a1 in output
     assert a2 in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' in output
-    
+
     output = review(rev='1', files=['file_two'])
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' not in output
-    
+
     output = review(rev='0', files=[filename])
     assert a1 not in output
     assert a2 not in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' not in output
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_add_comments_to_file_in_cwd():
     os.chdir('test_dir')
     review(comment=True, message='Test comment one.', rev='1', files=['test_file'])
-    
+
     output = review(rev='1', files=['test_file'])
     assert a1 in output
     assert a2 in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' in output
-    
+
     output = review(rev='1', files=['file_two'])
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' not in output
-    
+
     output = review(rev='0', files=['test_file'])
     assert a1 not in output
     assert a2 not in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' not in output
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_add_comments_to_file_in_reldir():
     filename = os.path.join('..', 'file_three')
-    
+
     os.chdir('test_dir')
     review(comment=True, message='Test comment one.', rev='1', files=[filename])
-    
+
     output = review(rev='1', files=[filename])
     assert a1 in output
     assert a2 in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' in output
-    
+
     output = review(rev='1', files=['file_two'])
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' not in output
-    
+
     output = review(rev='0', files=[filename])
     assert a1 not in output
     assert a2 not in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' not in output
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_comment_identifiers():
     review(comment=True, message='Test comment one.', rev='1', files=['file_one'])
-    
+
     rd = api.ReviewDatastore(get_ui(), get_sandbox_repo())
     dsr = get_datastore_repo()
-    
+
     comment = rd['1'].comments[0]
-    
+
     identifier = comment.identifier
     short_identifier = identifier[:12]
-    
+
     comment_filename = api._split_path_dammit(dsr['tip'].files()[0])[-1]
     comment_cset = hex(dsr['tip'].node())
-    
+
     assert identifier == comment_filename
     assert identifier != comment_cset
-    
+
     verbose_identifier = messages.REVIEW_LOG_IDENTIFIER % identifier[:12]
     debug_identifier = messages.REVIEW_LOG_IDENTIFIER % identifier
-    
+
     normal_output = review(rev='1')
     assert verbose_identifier not in normal_output
     assert debug_identifier not in normal_output
-    
+
     verbose_output = review(rev='1', verbose=True)
     assert verbose_identifier in verbose_output
     assert debug_identifier not in verbose_output
-    
+
     debug_output = review(rev='1', debug=True)
     assert verbose_identifier not in debug_output
     assert debug_identifier in debug_output
--- a/review/tests/test_diffs.py	Fri Jun 11 19:59:34 2010 -0400
+++ b/review/tests/test_diffs.py	Fri Jun 11 20:07:20 2010 -0400
@@ -2,74 +2,68 @@
 from util import *
 from .. import messages
 
-import os
-
-
 a1, a2 = (messages.REVIEW_LOG_COMMENT_AUTHOR % '|').split('|')
 s1, s2 = (messages.REVIEW_LOG_SKIPPED % 1111).split('1111')
 
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_review_diff_default_context():
     output = review(rev='1', files=['long_file'], unified='5')
-    
+
     assert ' 0:' not in output
     assert messages.REVIEW_LOG_SKIPPED % 1 in output
-    
+
     for n in range(1, 20):
         assert '%2d:' % n in output
-    
+
     assert '-g' in output
     assert '+X' in output
     assert '-m' in output
     assert '+Y' in output
-    
+
     assert '20:' not in output
     assert messages.REVIEW_LOG_SKIPPED % 2 in output
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_review_diff_full_context():
     output = review(rev='1', files=['long_file'], unified='10000')
-    
+
     assert s1 not in output
     assert s2 not in output
-    
+
     for n in range(0, 21):
         assert '%2d:' % n in output
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_review_diff_small_context():
     output = review(rev='1', files=['long_file'], unified='2')
-    
+
     assert ' 3:' not in output
     assert messages.REVIEW_LOG_SKIPPED % 4 in output
-    
+
     assert '-g' in output
     assert '+X' in output
-    
+
     assert '10:' not in output
     assert messages.REVIEW_LOG_SKIPPED % 2 in output
-    
+
     assert '-m' in output
     assert '+Y' in output
-    
+
     assert '17:' not in output
     assert messages.REVIEW_LOG_SKIPPED % 5 in output
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_review_diff_with_comment():
     review(comment=True, rev='1', message='Test comment one.',
         files=['long_file'], lines='6,7')
-    
+
     output = review(rev='1', files=['long_file'], unified=0)
-    
+
     # Make sure the comment is present at all.
     assert a1 in output
     assert a2 in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' in output
-    
+
     # Make sure it's in the correct place
     output = output.splitlines()
     for n, line in enumerate(output):
@@ -77,19 +71,18 @@
             assert output[n-1].strip().startswith('7')
             break
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_review_diff_with_skipped_comment():
     review(comment=True, rev='1', message='Test comment one.',
         files=['long_file'], lines='3')
-    
+
     output = review(rev='1', files=['long_file'], unified=0)
-    
+
     # Make sure the comment is present at all.
     assert a1 in output
     assert a2 in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' in output
-    
+
     # Make sure it's in the correct place
     output = output.splitlines()
     for n, line in enumerate(output):
--- a/review/tests/test_init.py	Fri Jun 11 19:59:34 2010 -0400
+++ b/review/tests/test_init.py	Fri Jun 11 20:07:20 2010 -0400
@@ -7,24 +7,22 @@
 import os
 from mercurial import util as hgutil
 
-
 @with_setup(setup_sandbox, teardown_sandbox)
 def test_init():
     sandbox = get_sandbox_repo()
-    
+
     output = review(init=True, remote_path='/sandbox-review')
     assert messages.INIT_SUCCESS_UNCOMMITTED in output
-    
+
     assert '.hgreview' not in sandbox['tip']
     assert os.path.exists('.hgreview')
     assert os.path.isdir(api.DEFAULT_DATASTORE_DIRNAME)
     assert get_datastore_repo(api.DEFAULT_DATASTORE_DIRNAME)
-    
+
     with open('.hgreview', 'r') as hgrf:
         hgr = hgrf.read()
         assert 'remote = /sandbox-review' in hgr
 
-
 @with_setup(setup_sandbox, teardown_sandbox)
 def test_init_without_remote_path():
     try:
@@ -35,11 +33,10 @@
     else:
         assert False, 'The correct error message was not printed.'
 
-
 @with_setup(setup_sandbox, teardown_sandbox)
 def test_init_twice():
     review(init=True, remote_path='/sandbox-review')
-    
+
     try:
         review(init=True, remote_path='/sandbox-review')
     except hgutil.Abort, e:
@@ -48,21 +45,20 @@
     else:
         assert False, 'The correct error message was not printed.'
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_init_clone():
     review(comment=True, message='Test comment one.')
     review(comment=True, rev='0', message='Test comment two.')
-    
+
     clone_sandbox_repo()
     os.chdir(sandbox_clone_path)
-    
+
     review(init=True)
-    
+
     output = review()
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment two.' not in output
-    
+
     output = review(rev='0')
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' not in output
     assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment two.' in output
--- a/review/tests/test_signoff.py	Fri Jun 11 19:59:34 2010 -0400
+++ b/review/tests/test_signoff.py	Fri Jun 11 20:07:20 2010 -0400
@@ -1,11 +1,8 @@
 from nose import *
 from util import *
-from .. import messages
-
-import os
 from mercurial import util as hgutil
 from mercurial.node import hex
-
+from .. import messages
 
 s1, s2 = (messages.REVIEW_LOG_SIGNOFF_AUTHOR % ('|', 'neutral')).split('|')
 sy1, sy2 = (messages.REVIEW_LOG_SIGNOFF_AUTHOR % ('|', 'yes')).split('|')
@@ -16,7 +13,6 @@
     output = review()
     assert messages.REVIEW_LOG_SIGNOFFS % (0, 0, 0, 0) in output
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_blank_signoff():
     try:
@@ -27,34 +23,31 @@
     else:
         assert False, 'The correct error message was not printed.'
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_signoff_on_parent_rev():
     review(signoff=True, message='Test signoff one.')
-    
+
     output = review()
     assert messages.REVIEW_LOG_SIGNOFFS % (1, 0, 0, 1) in output
-    
+
     assert s1 in output
     assert s1 in output
     assert messages.REVIEW_LOG_SIGNOFF_LINE % 'Test signoff one.' in output
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_signoff_on_specific_rev():
     review(signoff=True, message='Test signoff one.', rev='0')
-    
+
     output = review(rev='0')
     assert messages.REVIEW_LOG_SIGNOFFS % (1, 0, 0, 1) in output
-    
+
     output = review()
     assert messages.REVIEW_LOG_SIGNOFFS % (0, 0, 0, 0) in output
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_multiple_signoffs():
     review(signoff=True, message='Test signoff one.')
-    
+
     try:
         review(signoff=True, message='Test signoff two.')
     except hgutil.Abort, e:
@@ -62,66 +55,63 @@
         assert messages.SIGNOFF_EXISTS in error
     else:
         assert False, 'The correct error message was not printed.'
-    
+
     review(signoff=True, message='Test signoff two.', force=True)
-    
+
     output = review()
     assert messages.REVIEW_LOG_SIGNOFFS % (1, 0, 0, 1) in output
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_signoff_yes():
     review(signoff=True, yes=True, message='Test signoff one.')
-    
+
     output = review()
     assert messages.REVIEW_LOG_SIGNOFFS % (1, 1, 0, 0) in output
-    
+
     assert sy1 in output
     assert sy1 in output
     assert messages.REVIEW_LOG_SIGNOFF_LINE % 'Test signoff one.' in output
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_signoff_no():
     review(signoff=True, no=True, message='Test signoff one.')
-    
+
     output = review()
     assert messages.REVIEW_LOG_SIGNOFFS % (1, 0, 1, 0) in output
-    
+
     assert sn1 in output
     assert sn1 in output
     assert messages.REVIEW_LOG_SIGNOFF_LINE % 'Test signoff one.' in output
 
-
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
 def test_signoff_identifiers():
     review(signoff=True, message='Test signoff one.', rev='0')
-    
+
     rd = api.ReviewDatastore(get_ui(), get_sandbox_repo())
     dsr = get_datastore_repo()
-    
+
     signoff = rd['0'].signoffs[0]
-    
+
     identifier = signoff.identifier
     short_identifier = identifier[:12]
-    
+
     signoff_filename = api._split_path_dammit(dsr['tip'].files()[0])[-1]
     signoff_cset = hex(dsr['tip'].node())
-    
+
     assert identifier == signoff_filename
     assert identifier != signoff_cset
-    
+
     verbose_identifier = messages.REVIEW_LOG_IDENTIFIER % identifier[:12]
     debug_identifier = messages.REVIEW_LOG_IDENTIFIER % identifier
-    
+
     normal_output = review(rev='0')
     assert verbose_identifier not in normal_output
     assert debug_identifier not in normal_output
-    
+
     verbose_output = review(rev='0', verbose=True)
     assert verbose_identifier in verbose_output
     assert debug_identifier not in verbose_output
-    
+
     debug_output = review(rev='0', debug=True)
     assert verbose_identifier not in debug_output
     assert debug_identifier in debug_output
--- a/review/tests/util.py	Fri Jun 11 19:59:34 2010 -0400
+++ b/review/tests/util.py	Fri Jun 11 20:07:20 2010 -0400
@@ -1,19 +1,20 @@
+from __future__ import with_statement
+
 """Utilities for writing unit tests for hg-review."""
-from __future__ import with_statement
+
 import os, shutil
 import sample_data
 from mercurial import cmdutil, commands, hg, ui
 from .. import api, extension_ui
 
-
 _ui = ui.ui()
 def review(init=False, comment=False, signoff=False, yes=False, no=False,
     force=False, message='', rev='.', remote_path='', lines='', files=None,
     unified='5', web=False, verbose=False, debug=False):
-    
+
     if not files:
         files = []
-    
+
     _ui.pushbuffer()
     if debug:
         _ui.debugflag = True
@@ -29,7 +30,7 @@
     )
     _ui.verbose, _ui.debugflag = False, False
     output = _ui.popbuffer()
-    
+
     print output
     return output
 
@@ -41,29 +42,29 @@
 def setup_sandbox():
     os.mkdir(sandbox_path)
     os.chdir(sandbox_path)
-    
+
     os.mkdir(sandbox_repo_path)
     os.chdir(sandbox_repo_path)
-    
+
     commands.init(_ui)
     sandbox = get_sandbox_repo()
-    
+
     opts = { 'addremove': True, 'date': None, 'user': 'Review Tester',
              'logfile': None, 'message': "Sandbox commit.", }
     for state in sample_data.log:
         for filename in state:
             dirname, key = None, filename
-            
+
             # Support one-level-deep directories in the sample data.
             if '/' in filename:
                 dirname, _, filename = filename.partition('/')
                 if not os.path.exists(dirname):
                     os.mkdir(dirname)
                 os.chdir(dirname)
-            
+
             with open(filename, 'w') as f:
                 f.write(state[key])
-            
+
             if dirname:
                 os.chdir('..')
         commands.commit(_ui, sandbox, **opts)
@@ -71,10 +72,10 @@
 def setup_reviewed_sandbox():
     setup_sandbox()
     sandbox = get_sandbox_repo()
-    
+
     rpath = os.path.join(sandbox.root, api.DEFAULT_DATASTORE_DIRNAME)
     review(init=True, remote_path=rpath)
-    
+
     opts = { 'addremove': True, 'date': None, 'user': 'Review Tester',
              'logfile': None, 'message': "Add the code review.", }
     commands.commit(_ui, sandbox, **opts)