Rev 4844: (igc) Faster log by less outf.write() calls in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Tue Dec 1 02:59:20 GMT 2009


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 4844 [merge]
revision-id: pqm at pqm.ubuntu.com-20091201024020-8uqp6v8p2xqppu36
parent: pqm at pqm.ubuntu.com-20091201001004-vflb5bqsnrlkfh7p
parent: ian.clatworthy at canonical.com-20091201015532-h7apkk3ig1gbvs93
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Tue 2009-12-01 02:40:20 +0000
message:
  (igc) Faster log by less outf.write() calls
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/log.py                  log.py-20050505065812-c40ce11702fe5fb1
  bzrlib/osutils.py              osutils.py-20050309040759-eeaff12fbf77ac86
  bzrlib/revision.py             revision.py-20050309040759-e77802c08f3999d5
  bzrlib/tests/test_osutils.py   test_osutils.py-20051201224856-e48ee24c12182989
=== modified file 'NEWS'
--- a/NEWS	2009-11-30 04:49:31 +0000
+++ b/NEWS	2009-11-30 12:24:55 +0000
@@ -60,6 +60,8 @@
 Improvements
 ************
 
+* ``bzr log`` is now faster. (Ian Clatworthy)
+
 * ``bzrlib.urlutils.local_path_from_url`` now accepts
   'file://localhost/' as well as 'file:///' URLs on POSIX.  (Michael
   Hudson)

=== modified file 'bzrlib/log.py'
--- a/bzrlib/log.py	2009-10-29 03:32:42 +0000
+++ b/bzrlib/log.py	2009-11-30 12:24:55 +0000
@@ -83,6 +83,7 @@
     )
 from bzrlib.osutils import (
     format_date,
+    format_date_with_offset_in_original_timezone,
     get_terminal_encoding,
     re_compile_checked,
     terminal_width,
@@ -384,27 +385,28 @@
         :return: An iterator yielding LogRevision objects.
         """
         rqst = self.rqst
+        levels = rqst.get('levels')
+        limit = rqst.get('limit')
+        diff_type = rqst.get('diff_type')
         log_count = 0
         revision_iterator = self._create_log_revision_iterator()
         for revs in revision_iterator:
             for (rev_id, revno, merge_depth), rev, delta in revs:
                 # 0 levels means show everything; merge_depth counts from 0
-                levels = rqst.get('levels')
                 if levels != 0 and merge_depth >= levels:
                     continue
-                diff = self._format_diff(rev, rev_id)
+                if diff_type is None:
+                    diff = None
+                else:
+                    diff = self._format_diff(rev, rev_id, diff_type)
                 yield LogRevision(rev, revno, merge_depth, delta,
                     self.rev_tag_dict.get(rev_id), diff)
-                limit = rqst.get('limit')
                 if limit:
                     log_count += 1
                     if log_count >= limit:
                         return
 
-    def _format_diff(self, rev, rev_id):
-        diff_type = self.rqst.get('diff_type')
-        if diff_type is None:
-            return None
+    def _format_diff(self, rev, rev_id, diff_type):
         repo = self.branch.repository
         if len(rev.parent_ids) == 0:
             ancestor_id = _mod_revision.NULL_REVISION
@@ -1291,7 +1293,7 @@
     preferred_levels = 0
 
     def __init__(self, to_file, show_ids=False, show_timezone='original',
-                 delta_format=None, levels=None, show_advice=False):
+            delta_format=None, levels=None, show_advice=False):
         """Create a LogFormatter.
 
         :param to_file: the file to output to
@@ -1367,48 +1369,64 @@
         else:
             return ''
 
-    def show_foreign_info(self, rev, indent):
+    def show_properties(self, revision, indent):
+        """Displays the custom properties returned by each registered handler.
+
+        If a registered handler raises an error it is propagated.
+        """
+        for line in self.custom_properties(revision):
+            self.to_file.write("%s%s\n" % (indent, line))
+
+    def custom_properties(self, revision):
+        """Format the custom properties returned by each registered handler.
+
+        If a registered handler raises an error it is propagated.
+
+        :return: a list of formatted lines (excluding trailing newlines)
+        """
+        lines = self._foreign_info_properties(revision)
+        for key, handler in properties_handler_registry.iteritems():
+            lines.extend(self._format_properties(handler(revision)))
+        return lines
+
+    def _foreign_info_properties(self, rev):
         """Custom log displayer for foreign revision identifiers.
 
         :param rev: Revision object.
         """
         # Revision comes directly from a foreign repository
         if isinstance(rev, foreign.ForeignRevision):
-            self._write_properties(indent, rev.mapping.vcs.show_foreign_revid(
-                rev.foreign_revid))
-            return
+            return rev.mapping.vcs.show_foreign_revid(rev.foreign_revid)
 
         # Imported foreign revision revision ids always contain :
         if not ":" in rev.revision_id:
-            return
+            return []
 
         # Revision was once imported from a foreign repository
         try:
             foreign_revid, mapping = \
                 foreign.foreign_vcs_registry.parse_revision_id(rev.revision_id)
         except errors.InvalidRevisionId:
-            return
+            return []
 
-        self._write_properties(indent, 
+        return self._format_properties(
             mapping.vcs.show_foreign_revid(foreign_revid))
 
-    def show_properties(self, revision, indent):
-        """Displays the custom properties returned by each registered handler.
-
-        If a registered handler raises an error it is propagated.
-        """
-        for key, handler in properties_handler_registry.iteritems():
-            self._write_properties(indent, handler(revision))
-
-    def _write_properties(self, indent, properties):
+    def _format_properties(self, properties):
+        lines = []
         for key, value in properties.items():
-            self.to_file.write(indent + key + ': ' + value + '\n')
+            lines.append(key + ': ' + value)
+        return lines
 
     def show_diff(self, to_file, diff, indent):
         for l in diff.rstrip().split('\n'):
             to_file.write(indent + '%s\n' % (l,))
 
 
+# Separator between revisions in long format
+_LONG_SEP = '-' * 60
+
+
 class LongLogFormatter(LogFormatter):
 
     supports_merge_revisions = True
@@ -1417,46 +1435,59 @@
     supports_tags = True
     supports_diff = True
 
+    def __init__(self, *args, **kwargs):
+        super(LongLogFormatter, self).__init__(*args, **kwargs)
+        if self.show_timezone == 'original':
+            self.date_string = self._date_string_original_timezone
+        else:
+            self.date_string = self._date_string_with_timezone
+
+    def _date_string_with_timezone(self, rev):
+        return format_date(rev.timestamp, rev.timezone or 0,
+                           self.show_timezone)
+
+    def _date_string_original_timezone(self, rev):
+        return format_date_with_offset_in_original_timezone(rev.timestamp,
+            rev.timezone or 0)
+
     def log_revision(self, revision):
         """Log a revision, either merged or not."""
         indent = '    ' * revision.merge_depth
-        to_file = self.to_file
-        to_file.write(indent + '-' * 60 + '\n')
+        lines = [_LONG_SEP]
         if revision.revno is not None:
-            to_file.write(indent + 'revno: %s%s\n' % (revision.revno,
+            lines.append('revno: %s%s' % (revision.revno,
                 self.merge_marker(revision)))
         if revision.tags:
-            to_file.write(indent + 'tags: %s\n' % (', '.join(revision.tags)))
+            lines.append('tags: %s' % (', '.join(revision.tags)))
         if self.show_ids:
-            to_file.write(indent + 'revision-id: ' + revision.rev.revision_id)
-            to_file.write('\n')
+            lines.append('revision-id: %s' % (revision.rev.revision_id,))
             for parent_id in revision.rev.parent_ids:
-                to_file.write(indent + 'parent: %s\n' % (parent_id,))
-        self.show_foreign_info(revision.rev, indent)
-        self.show_properties(revision.rev, indent)
+                lines.append('parent: %s' % (parent_id,))
+        lines.extend(self.custom_properties(revision.rev))
 
         committer = revision.rev.committer
         authors = revision.rev.get_apparent_authors()
         if authors != [committer]:
-            to_file.write(indent + 'author: %s\n' % (", ".join(authors),))
-        to_file.write(indent + 'committer: %s\n' % (committer,))
+            lines.append('author: %s' % (", ".join(authors),))
+        lines.append('committer: %s' % (committer,))
 
         branch_nick = revision.rev.properties.get('branch-nick', None)
         if branch_nick is not None:
-            to_file.write(indent + 'branch nick: %s\n' % (branch_nick,))
-
-        date_str = format_date(revision.rev.timestamp,
-                               revision.rev.timezone or 0,
-                               self.show_timezone)
-        to_file.write(indent + 'timestamp: %s\n' % (date_str,))
-
-        to_file.write(indent + 'message:\n')
+            lines.append('branch nick: %s' % (branch_nick,))
+
+        lines.append('timestamp: %s' % (self.date_string(revision.rev),))
+
+        lines.append('message:')
         if not revision.rev.message:
-            to_file.write(indent + '  (no message)\n')
+            lines.append('  (no message)')
         else:
             message = revision.rev.message.rstrip('\r\n')
             for l in message.split('\n'):
-                to_file.write(indent + '  %s\n' % (l,))
+                lines.append('  %s' % (l,))
+
+        # Dump the output, appending the delta and diff if requested
+        to_file = self.to_file
+        to_file.write("%s%s\n" % (indent, ('\n' + indent).join(lines)))
         if revision.delta is not None:
             # We don't respect delta_format for compatibility
             revision.delta.show(to_file, self.show_ids, indent=indent,
@@ -1515,7 +1546,6 @@
                             self.show_timezone, date_fmt="%Y-%m-%d",
                             show_offset=False),
                 tags, self.merge_marker(revision)))
-        self.show_foreign_info(revision.rev, indent+offset)
         self.show_properties(revision.rev, indent+offset)
         if self.show_ids:
             to_file.write(indent + offset + 'revision-id:%s\n'

=== modified file 'bzrlib/osutils.py'
--- a/bzrlib/osutils.py	2009-11-10 07:01:56 +0000
+++ b/bzrlib/osutils.py	2009-11-30 12:24:55 +0000
@@ -695,6 +695,8 @@
     return offset.days * 86400 + offset.seconds
 
 weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
+_default_format_by_weekday_num = [wd + " %Y-%m-%d %H:%M:%S" for wd in weekdays]
+
 
 def format_date(t, offset=0, timezone='original', date_fmt=None,
                 show_offset=True):
@@ -714,6 +716,32 @@
     date_str = time.strftime(date_fmt, tt)
     return date_str + offset_str
 
+
+# Cache of formatted offset strings
+_offset_cache = {}
+
+
+def format_date_with_offset_in_original_timezone(t, offset=0,
+    _cache=_offset_cache):
+    """Return a formatted date string in the original timezone.
+
+    This routine may be faster then format_date.
+
+    :param t: Seconds since the epoch.
+    :param offset: Timezone offset in seconds east of utc.
+    """
+    if offset is None:
+        offset = 0
+    tt = time.gmtime(t + offset)
+    date_fmt = _default_format_by_weekday_num[tt[6]]
+    date_str = time.strftime(date_fmt, tt)
+    offset_str = _cache.get(offset, None)
+    if offset_str is None:
+        offset_str = ' %+03d%02d' % (offset / 3600, (offset / 60) % 60)
+        _cache[offset] = offset_str
+    return date_str + offset_str
+
+
 def format_local_date(t, offset=0, timezone='original', date_fmt=None,
                       show_offset=True):
     """Return an unicode date string formatted according to the current locale.
@@ -733,6 +761,7 @@
         date_str = date_str.decode(get_user_encoding(), 'replace')
     return date_str + offset_str
 
+
 def _format_date(t, offset, timezone, date_fmt, show_offset):
     if timezone == 'utc':
         tt = time.gmtime(t)

=== modified file 'bzrlib/revision.py'
--- a/bzrlib/revision.py	2009-09-01 12:39:21 +0000
+++ b/bzrlib/revision.py	2009-11-30 12:24:55 +0000
@@ -54,8 +54,11 @@
 
     def __init__(self, revision_id, properties=None, **args):
         self.revision_id = revision_id
-        self.properties = properties or {}
-        self._check_properties()
+        if properties is None:
+            self.properties = {}
+        else:
+            self.properties = properties
+            self._check_properties()
         self.committer = None
         self.parent_ids = []
         self.parent_sha1s = []

=== modified file 'bzrlib/tests/test_osutils.py'
--- a/bzrlib/tests/test_osutils.py	2009-11-18 16:10:18 +0000
+++ b/bzrlib/tests/test_osutils.py	2009-11-30 12:24:55 +0000
@@ -378,6 +378,14 @@
         # Instead blackbox.test_locale should check for localized
         # dates once they do occur in output strings.
 
+    def test_format_date_with_offset_in_original_timezone(self):
+        self.assertEqual("Thu 1970-01-01 00:00:00 +0000",
+            osutils.format_date_with_offset_in_original_timezone(0))
+        self.assertEqual("Fri 1970-01-02 03:46:40 +0000",
+            osutils.format_date_with_offset_in_original_timezone(100000))
+        self.assertEqual("Fri 1970-01-02 05:46:40 +0200",
+            osutils.format_date_with_offset_in_original_timezone(100000, 7200))
+
     def test_local_time_offset(self):
         """Test that local_time_offset() returns a sane value."""
         offset = osutils.local_time_offset()




More information about the bazaar-commits mailing list