Rev 3219: Clean up logging, add push/pop_log_file, trace state can now be precisely pushed and restored in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Thu Feb 7 05:25:10 GMT 2008


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

------------------------------------------------------------
revno: 3219
revision-id:pqm at pqm.ubuntu.com-20080207052502-jz28cn21r726b737
parent: pqm at pqm.ubuntu.com-20080206163804-6zyjbbfpsm8txfdm
parent: mbp at sourcefrog.net-20080207034957-cc7tlku5xfya9wei
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Thu 2008-02-07 05:25:02 +0000
message:
  Clean up logging, add push/pop_log_file, trace state can now be precisely pushed and restored
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/symbol_versioning.py    symbol_versioning.py-20060105104851-9ecf8af605d15a80
  bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
  bzrlib/tests/test_trace.py     testtrace.py-20051110225523-a21117fc7a07eeff
  bzrlib/trace.py                trace.py-20050309040759-c8ed824bdcd4748a
    ------------------------------------------------------------
    revno: 3173.1.15
    revision-id:mbp at sourcefrog.net-20080207034957-cc7tlku5xfya9wei
    parent: mbp at sourcefrog.net-20080206004104-mxtn32habuhjq6b8
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: 124153-test-kibble
    timestamp: Thu 2008-02-07 14:49:57 +1100
    message:
      Update test_push_log_file to handle there always being timestamps at the start of the trace messages
    modified:
      bzrlib/tests/test_trace.py     testtrace.py-20051110225523-a21117fc7a07eeff
    ------------------------------------------------------------
    revno: 3173.1.14
    revision-id:mbp at sourcefrog.net-20080206004104-mxtn32habuhjq6b8
    parent: mbp at sourcefrog.net-20080129070504-g8vmi3hrd3n8iirl
    parent: pqm at pqm.ubuntu.com-20080205231318-dcc9hdxcxjd7hx79
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: 124153-test-kibble
    timestamp: Wed 2008-02-06 11:41:04 +1100
    message:
      Merge trunk
    removed:
      bzrlib/plugins/multiparent.py  mpregen-20070411063203-5x9z7i73add0d6f6-1
    added:
      bzrlib/tests/repository_implementations/test_has_revisions.py test_has_revisions.p-20080111035443-xaupgdsx5fw1q54b-1
      doc/en/user-guide/revnos.txt   revnos.txt-20080111231928-pbntxea0ynh9ww1t-1
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzr                            bzr.py-20050313053754-5485f144c7006fa6
      bzrlib/__init__.py             __init__.py-20050309040759-33e65acf91bbcd5d
      bzrlib/annotate.py             annotate.py-20050922133147-7c60541d2614f022
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
      bzrlib/bzrdir.py               bzrdir.py-20060131065624-156dfea39c4387cb
      bzrlib/commands.py             bzr.py-20050309040720-d10f4714595cf8c3
      bzrlib/debug.py                debug.py-20061102062349-vdhrw9qdpck8cl35-1
      bzrlib/delta.py                delta.py-20050729221636-54cf14ef94783d0a
      bzrlib/doc/api/__init__.py     __init__.py-20051224020744-7b87d590843855bc
      bzrlib/errors.py               errors.py-20050309040759-20512168c4e14fbd
      bzrlib/fetch.py                fetch.py-20050818234941-26fea6105696365d
      bzrlib/graph.py                graph_walker.py-20070525030359-y852guab65d4wtn0-1
      bzrlib/help_topics/__init__.py help_topics.py-20060920210027-rnim90q9e0bwxvy4-1
      bzrlib/inventory.py            inventory.py-20050309040759-6648b84ca2005b37
      bzrlib/knit.py                 knit.py-20051212171256-f056ac8f0fbe1bd9
      bzrlib/merge.py                merge.py-20050513021216-953b65a438527106
      bzrlib/osutils.py              osutils.py-20050309040759-eeaff12fbf77ac86
      bzrlib/plugin.py               plugin.py-20050622060424-829b654519533d69
      bzrlib/plugins/launchpad/__init__.py __init__.py-20060315182712-2d5feebd2a1032dc
      bzrlib/plugins/launchpad/lp_indirect.py lp_indirect.py-20070126012204-de5rugwlt22c7u7e-1
      bzrlib/plugins/launchpad/lp_registration.py lp_registration.py-20060315190948-daa617eafe3a8d48
      bzrlib/plugins/launchpad/test_lp_indirect.py test_lp_indirect.py-20070126002743-oyle362tzv9cd8mi-1
      bzrlib/plugins/launchpad/test_register.py test_register.py-20060315182712-40f5dda945c829a8
      bzrlib/reconfigure.py          reconfigure.py-20070908040425-6ykgo7escxhyrg9p-1
      bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
      bzrlib/repofmt/knitrepo.py     knitrepo.py-20070206081537-pyy4a00xdas0j4pf-1
      bzrlib/repofmt/pack_repo.py    pack_repo.py-20070813041115-gjv5ma7ktfqwsjgn-1
      bzrlib/repofmt/weaverepo.py    presplitout.py-20070125045333-wfav3tsh73oxu3zk-1
      bzrlib/repository.py           rev_storage.py-20051111201905-119e9401e46257e3
      bzrlib/revisiontree.py         revisiontree.py-20060724012533-bg8xyryhxd0o0i0h-1
      bzrlib/smart/client.py         client.py-20061116014825-2k6ada6xgulslami-1
      bzrlib/smart/medium.py         medium.py-20061103051856-rgu2huy59fkz902q-1
      bzrlib/smart/protocol.py       protocol.py-20061108035435-ot0lstk2590yqhzr-1
      bzrlib/smart/repository.py     repository.py-20061128022038-vr5wy5bubyb8xttk-1
      bzrlib/smart/request.py        request.py-20061108095550-gunadhxmzkdjfeek-1
      bzrlib/status.py               status.py-20050505062338-431bfa63ec9b19e6
      bzrlib/symbol_versioning.py    symbol_versioning.py-20060105104851-9ecf8af605d15a80
      bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
      bzrlib/tests/blackbox/test_log.py test_log.py-20060112090212-78f6ea560c868e24
      bzrlib/tests/blackbox/test_merge.py test_merge.py-20060323225809-9bc0459c19917f41
      bzrlib/tests/blackbox/test_outside_wt.py test_outside_wt.py-20060116200058-98edd33e7db8bdde
      bzrlib/tests/blackbox/test_pull.py test_pull.py-20051201144907-64959364f629947f
      bzrlib/tests/blackbox/test_selftest.py test_selftest.py-20060123024542-01c5f1bbcb596d78
      bzrlib/tests/interrepository_implementations/test_interrepository.py test_interrepository.py-20060220061411-1ec13fa99e5e3eee
      bzrlib/tests/repository_implementations/__init__.py __init__.py-20060131092037-9564957a7d4a841b
      bzrlib/tests/repository_implementations/test_repository.py test_repository.py-20060131092128-ad07f494f5c9d26c
      bzrlib/tests/test_annotate.py  test_annotate.py-20061213215015-sttc9agsxomls7q0-1
      bzrlib/tests/test_diff.py      testdiff.py-20050727164403-d1a3496ebb12e339
      bzrlib/tests/test_errors.py    test_errors.py-20060210110251-41aba2deddf936a8
      bzrlib/tests/test_graph.py     test_graph_walker.py-20070525030405-enq4r60hhi9xrujc-1
      bzrlib/tests/test_http.py      testhttp.py-20051018020158-b2eef6e867c514d9
      bzrlib/tests/test_log.py       testlog.py-20050728115707-1a514809d7d49309
      bzrlib/tests/test_merge.py     testmerge.py-20050905070950-c1b5aa49ff911024
      bzrlib/tests/test_merge_core.py test_merge_core.py-20050824132511-eb99b23a0eec641b
      bzrlib/tests/test_nonascii.py  testnonascii.py-20051018022645-ea1d8b6477b058a6
      bzrlib/tests/test_osutils.py   test_osutils.py-20051201224856-e48ee24c12182989
      bzrlib/tests/test_plugins.py   plugins.py-20050622075746-32002b55e5e943e9
      bzrlib/tests/test_reconfigure.py test_reconfigure.py-20070908040425-6ykgo7escxhyrg9p-2
      bzrlib/tests/test_remote.py    test_remote.py-20060720103555-yeeg2x51vn0rbtdp-2
      bzrlib/tests/test_repository.py test_repository.py-20060131075918-65c555b881612f4d
      bzrlib/tests/test_selftest.py  test_selftest.py-20051202044319-c110a115d8c0456a
      bzrlib/tests/test_smart.py     test_smart.py-20061122024551-ol0l0o0oofsu9b3t-2
      bzrlib/tests/test_smart_transport.py test_ssh_transport.py-20060608202016-c25gvf1ob7ypbus6-2
      bzrlib/tests/test_trace.py     testtrace.py-20051110225523-a21117fc7a07eeff
      bzrlib/tests/test_transform.py test_transaction.py-20060105172520-b3ffb3946550e6c4
      bzrlib/tests/test_tsort.py     testtsort.py-20051025073946-27da871c394d5be4
      bzrlib/tests/workingtree_implementations/test_rename_one.py test_rename_one.py-20070226161242-2d8ibdedl700jgio-1
      bzrlib/trace.py                trace.py-20050309040759-c8ed824bdcd4748a
      bzrlib/transform.py            transform.py-20060105172343-dd99e54394d91687
      bzrlib/transport/__init__.py   transport.py-20050711165921-4978aa7ce1285ad5
      bzrlib/transport/remote.py     ssh.py-20060608202016-c25gvf1ob7ypbus6-1
      bzrlib/tsort.py                tsort.py-20051025073946-7808f6aaf7d07208
      doc/developers/HACKING.txt     HACKING-20050805200004-2a5dc975d870f78c
      doc/en/tutorials/using_bazaar_with_launchpad.txt using_bazaar_with_lp-20071211073140-7msh8uf9a9h4y9hb-1
      doc/en/user-guide/controlling_registration.txt controlling_registra-20071121073725-0corxykv5irjal00-3
      doc/en/user-guide/core_concepts.txt core_concepts.txt-20071114035000-q36a9h57ps06uvnl-2
      doc/en/user-guide/index.txt    index.txt-20060622101119-tgwtdci8z769bjb9-2
    ------------------------------------------------------------
    revno: 3173.1.13
    revision-id:mbp at sourcefrog.net-20080129070504-g8vmi3hrd3n8iirl
    parent: mbp at sourcefrog.net-20080129060611-clv6gh4ijuawhjvy
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: 124153-test-kibble
    timestamp: Tue 2008-01-29 18:05:04 +1100
    message:
      Describe fixes by push/pop_log_file
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
    ------------------------------------------------------------
    revno: 3173.1.12
    revision-id:mbp at sourcefrog.net-20080129060611-clv6gh4ijuawhjvy
    parent: mbp at sourcefrog.net-20080129060546-ndx1go4w0dord9d1
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: 124153-test-kibble
    timestamp: Tue 2008-01-29 17:06:11 +1100
    message:
      Add test_push_log_file
    modified:
      bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
      bzrlib/tests/test_trace.py     testtrace.py-20051110225523-a21117fc7a07eeff
    ------------------------------------------------------------
    revno: 3173.1.11
    revision-id:mbp at sourcefrog.net-20080129060546-ndx1go4w0dord9d1
    parent: mbp at sourcefrog.net-20080129060033-7t1w0rh448m3u6kr
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: 124153-test-kibble
    timestamp: Tue 2008-01-29 17:05:46 +1100
    message:
      Fix up renaming of push_log_file
    modified:
      bzrlib/trace.py                trace.py-20050309040759-c8ed824bdcd4748a
    ------------------------------------------------------------
    revno: 3173.1.10
    revision-id:mbp at sourcefrog.net-20080129060033-7t1w0rh448m3u6kr
    parent: mbp at sourcefrog.net-20080122080302-9mhey64swvky8m5j
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: 124153-test-kibble
    timestamp: Tue 2008-01-29 17:00:33 +1100
    message:
      Move assertFileEqual to TestCase base class as it's generally usable
    modified:
      bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
    ------------------------------------------------------------
    revno: 3173.1.9
    revision-id:mbp at sourcefrog.net-20080122080302-9mhey64swvky8m5j
    parent: mbp at sourcefrog.net-20080122080246-w724vmev1yt4mq1r
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: 124153-test-kibble
    timestamp: Tue 2008-01-22 19:03:02 +1100
    message:
      tests should now call push/pop_test_log
    modified:
      bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
    ------------------------------------------------------------
    revno: 3173.1.8
    revision-id:mbp at sourcefrog.net-20080122080246-w724vmev1yt4mq1r
    parent: mbp at sourcefrog.net-20080122074230-87ijmlmu1jrwer3a
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: 124153-test-kibble
    timestamp: Tue 2008-01-22 19:02:46 +1100
    message:
      doc update
    modified:
      bzrlib/trace.py                trace.py-20050309040759-c8ed824bdcd4748a
    ------------------------------------------------------------
    revno: 3173.1.7
    revision-id:mbp at sourcefrog.net-20080122074230-87ijmlmu1jrwer3a
    parent: mbp at sourcefrog.net-20080122072845-1ct7zakuje6w34j4
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: 124153-test-kibble
    timestamp: Tue 2008-01-22 18:42:30 +1100
    message:
      Rename push/pop_log_file, and deprecate enable/disable_test_log
    modified:
      bzrlib/trace.py                trace.py-20050309040759-c8ed824bdcd4748a
    ------------------------------------------------------------
    revno: 3173.1.6
    revision-id:mbp at sourcefrog.net-20080122072845-1ct7zakuje6w34j4
    parent: mbp at sourcefrog.net-20080122070957-qmmxkkv090oapj88
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: 124153-test-kibble
    timestamp: Tue 2008-01-22 18:28:45 +1100
    message:
      Remove _stderr_handler global
    modified:
      bzrlib/trace.py                trace.py-20050309040759-c8ed824bdcd4748a
    ------------------------------------------------------------
    revno: 3173.1.5
    revision-id:mbp at sourcefrog.net-20080122070957-qmmxkkv090oapj88
    parent: mbp at sourcefrog.net-20080122065434-4rtkpg2asj03hxcw
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: 124153-test-kibble
    timestamp: Tue 2008-01-22 18:09:57 +1100
    message:
      Remove global _bzr_log_file
    modified:
      bzrlib/trace.py                trace.py-20050309040759-c8ed824bdcd4748a
    ------------------------------------------------------------
    revno: 3173.1.4
    revision-id:mbp at sourcefrog.net-20080122065434-4rtkpg2asj03hxcw
    parent: mbp at sourcefrog.net-20080122053400-2wbr56mi9byq9mez
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: 124153-test-kibble
    timestamp: Tue 2008-01-22 17:54:34 +1100
    message:
      Rearrange and clean up bzr trace/logging
    modified:
      bzrlib/trace.py                trace.py-20050309040759-c8ed824bdcd4748a
    ------------------------------------------------------------
    revno: 3173.1.3
    revision-id:mbp at sourcefrog.net-20080122053400-2wbr56mi9byq9mez
    parent: mbp at sourcefrog.net-20080122042118-w3menysoh7wrtz8n
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: 124153-test-kibble
    timestamp: Tue 2008-01-22 16:34:00 +1100
    message:
      Deprecate disable_default_logging and add 1.2 deprecation marker
    modified:
      bzrlib/symbol_versioning.py    symbol_versioning.py-20060105104851-9ecf8af605d15a80
      bzrlib/trace.py                trace.py-20050309040759-c8ed824bdcd4748a
    ------------------------------------------------------------
    revno: 3173.1.2
    revision-id:mbp at sourcefrog.net-20080122042118-w3menysoh7wrtz8n
    parent: mbp at sourcefrog.net-20080122040252-48p03j2u4y24d0u3
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: 124153-test-kibble
    timestamp: Tue 2008-01-22 15:21:18 +1100
    message:
      bzrlib.trace docs and cleanups
    modified:
      bzrlib/trace.py                trace.py-20050309040759-c8ed824bdcd4748a
    ------------------------------------------------------------
    revno: 3173.1.1
    revision-id:mbp at sourcefrog.net-20080122040252-48p03j2u4y24d0u3
    parent: pqm at pqm.ubuntu.com-20080111025153-mqrn9i5o3hqezw75
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: 124153-test-kibble
    timestamp: Tue 2008-01-22 15:02:52 +1100
    message:
      New _push_log_file and _pop_log_file as a cleaner stack, towards a fix for #124153
    modified:
      bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
      bzrlib/trace.py                trace.py-20050309040759-c8ed824bdcd4748a
=== modified file 'NEWS'
--- a/NEWS	2008-02-06 16:38:04 +0000
+++ b/NEWS	2008-02-07 05:25:02 +0000
@@ -82,7 +82,15 @@
     * List possible values for BZR_SSH environment variable in env-variables
       help topic. (Alexander Belchenko, #181842)
 
-    * pull --quiet is now more quiet, in particular a message is no longer
+   * New methods ``push_log_file`` and ``pop_log_file`` to intercept messages:
+     popping the log redirection now precisely restores the previous state,
+     which makes it easier to use bzr log output from other programs.
+     TestCaseInTempDir no longer depends on a log redirection being established
+     by the test framework, which lets bzr tests cleanly run from a normal
+     unittest runner.
+     (#124153, #124849, Martin Pool, Jonathan Lange)
+
+    * ``pull --quiet`` is now more quiet, in particular a message is no longer
       printed when the remembered pull location is used. (James Westby,
       #185907)
 

=== modified file 'bzrlib/symbol_versioning.py'
--- a/bzrlib/symbol_versioning.py	2008-01-17 03:14:20 +0000
+++ b/bzrlib/symbol_versioning.py	2008-02-06 00:41:04 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006, 2007 Canonical Ltd
+# Copyright (C) 2006, 2007, 2008 Canonical Ltd
 #   Authors: Robert Collins <robert.collins at canonical.com> and others
 #
 # This program is free software; you can redistribute it and/or modify

=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2008-01-29 08:21:19 +0000
+++ b/bzrlib/tests/__init__.py	2008-02-06 00:41:04 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2005, 2006, 2007 Canonical Ltd
+# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -797,7 +797,6 @@
     def setUp(self):
         unittest.TestCase.setUp(self)
         self._cleanEnvironment()
-        bzrlib.trace.disable_default_logging()
         self._silenceUI()
         self._startLogFile()
         self._benchcalls = []
@@ -1041,6 +1040,32 @@
         else:
             self.fail('Unexpected success.  Should have failed: %s' % reason)
 
+    def assertFileEqual(self, content, path):
+        """Fail if path does not contain 'content'."""
+        self.failUnlessExists(path)
+        f = file(path, 'rb')
+        try:
+            s = f.read()
+        finally:
+            f.close()
+        self.assertEqualDiff(content, s)
+
+    def failUnlessExists(self, path):
+        """Fail unless path or paths, which may be abs or relative, exist."""
+        if not isinstance(path, basestring):
+            for p in path:
+                self.failUnlessExists(p)
+        else:
+            self.failUnless(osutils.lexists(path),path+" does not exist")
+
+    def failIfExists(self, path):
+        """Fail if path or paths, which may be abs or relative, exist."""
+        if not isinstance(path, basestring):
+            for p in path:
+                self.failIfExists(p)
+        else:
+            self.failIf(osutils.lexists(path),path+" exists")
+
     def _capture_deprecation_warnings(self, a_callable, *args, **kwargs):
         """A helper for callDeprecated and applyDeprecated.
 
@@ -1159,7 +1184,7 @@
         """
         fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
         self._log_file = os.fdopen(fileno, 'w+')
-        self._log_nonce = bzrlib.trace.enable_test_log(self._log_file)
+        self._log_memento = bzrlib.trace.push_log_file(self._log_file)
         self._log_file_name = name
         self.addCleanup(self._finishLogFile)
 
@@ -1170,7 +1195,7 @@
         """
         if self._log_file is None:
             return
-        bzrlib.trace.disable_test_log(self._log_nonce)
+        bzrlib.trace.pop_log_file(self._log_memento)
         self._log_file.close()
         self._log_file = None
         if not self._keep_log_file:
@@ -2088,32 +2113,6 @@
     def build_tree_contents(self, shape):
         build_tree_contents(shape)
 
-    def assertFileEqual(self, content, path):
-        """Fail if path does not contain 'content'."""
-        self.failUnlessExists(path)
-        f = file(path, 'rb')
-        try:
-            s = f.read()
-        finally:
-            f.close()
-        self.assertEqualDiff(content, s)
-
-    def failUnlessExists(self, path):
-        """Fail unless path or paths, which may be abs or relative, exist."""
-        if not isinstance(path, basestring):
-            for p in path:
-                self.failUnlessExists(p)
-        else:
-            self.failUnless(osutils.lexists(path),path+" does not exist")
-
-    def failIfExists(self, path):
-        """Fail if path or paths, which may be abs or relative, exist."""
-        if not isinstance(path, basestring):
-            for p in path:
-                self.failIfExists(p)
-        else:
-            self.failIf(osutils.lexists(path),path+" exists")
-
     def assertInWorkingTree(self, path, root_path='.', tree=None):
         """Assert whether path or paths are in the WorkingTree"""
         if tree is None:

=== modified file 'bzrlib/tests/test_trace.py'
--- a/bzrlib/tests/test_trace.py	2008-01-22 05:00:42 +0000
+++ b/bzrlib/tests/test_trace.py	2008-02-07 03:49:57 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2005, 2006, 2007 Canonical Ltd
+# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -23,6 +23,7 @@
 import os
 import re
 import sys
+import tempfile
 
 from bzrlib import (
     errors,
@@ -31,6 +32,8 @@
 from bzrlib.trace import (
     mutter, mutter_callsite, report_exception,
     set_verbosity_level, get_verbosity_level, is_quiet, is_verbose, be_quiet,
+    pop_log_file,
+    push_log_file,
     _rollover_trace_maybe,
     )
 
@@ -158,6 +161,39 @@
         self.assertContainsRe(log, "But fails in an ascii string")
         self.assertContainsRe(log, u"ascii argument: \xb5")
 
+    def test_push_log_file(self):
+        """Can push and pop log file, and this catches mutter messages.
+
+        This is primarily for use in the test framework. 
+        """
+        tmp1 = tempfile.NamedTemporaryFile()
+        tmp2 = tempfile.NamedTemporaryFile()
+        try:
+            memento1 = push_log_file(tmp1)
+            mutter("comment to file1")
+            try:
+                memento2 = push_log_file(tmp2)
+                try:
+                    mutter("comment to file2")
+                finally:
+                    pop_log_file(memento2)
+                mutter("again to file1")
+            finally:
+                pop_log_file(memento1)
+            # the files were opened in binary mode, so should have exactly
+            # these bytes.  and removing the file as the log target should
+            # have caused them to be flushed out.  need to match using regexps
+            # as there's a timestamp at the front.
+            tmp1.seek(0)
+            self.assertContainsRe(tmp1.read(),
+                r"\d+\.\d+  comment to file1\n\d+\.\d+  again to file1\n")
+            tmp2.seek(0)
+            self.assertContainsRe(tmp2.read(),
+                r"\d+\.\d+  comment to file2\n")
+        finally:
+            tmp1.close()
+            tmp2.close()
+
 
 class TestVerbosityLevel(TestCase):
 

=== modified file 'bzrlib/trace.py'
--- a/bzrlib/trace.py	2008-01-22 05:00:42 +0000
+++ b/bzrlib/trace.py	2008-02-06 00:41:04 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2005, 2006, 2007 Canonical Ltd
+# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -49,6 +49,11 @@
 # FIXME: Unfortunately it turns out that python's logging module
 # is quite expensive, even when the message is not printed by any handlers.
 # We should perhaps change back to just simply doing it here.
+#
+# On the other hand, as of 1.2 we generally only call the mutter() statement
+# if (according to debug_flags) we actually intend to write it.  So the
+# increased cost of logging.py is not so bad, and we could standardize on
+# that.
 
 import codecs
 import logging
@@ -73,35 +78,54 @@
     errors,
     osutils,
     plugin,
+    symbol_versioning,
     )
 """)
 
-_file_handler = None
-_stderr_handler = None
+
+# global verbosity for bzrlib; controls the log level for stderr; 0=normal; <0
+# is quiet; >0 is verbose.
 _verbosity_level = 0
+
+# File-like object where mutter/debug output is currently sent.  Can be
+# changed by _push_log_file etc.  This is directly manipulated by some
+# external code; maybe there should be functions to do that more precisely
+# than push/pop_log_file.
 _trace_file = None
-_trace_depth = 0
-_bzr_log_file = None
+
+# Absolute path for ~/.bzr.log.  Not changed even if the log/trace output is
+# redirected elsewhere.  Used to show the location in --version.
 _bzr_log_filename = None
-_start_time = bzrlib._start_time
-
-
-# configure convenient aliases for output routines
-
+
+# The time the first message was written to the trace file, so that we can
+# show relative times since startup.
+_bzr_log_start_time = bzrlib._start_time
+
+
+# held in a global for quick reference
 _bzr_logger = logging.getLogger('bzr')
 
 
 def note(*args, **kwargs):
     # FIXME note always emits utf-8, regardless of the terminal encoding
+    #
+    # FIXME: clearing the ui and then going through the abstract logging
+    # framework is whack; we should probably have a logging Handler that
+    # deals with terminal output if needed.
     import bzrlib.ui
     bzrlib.ui.ui_factory.clear_term()
     _bzr_logger.info(*args, **kwargs)
 
+
 def warning(*args, **kwargs):
     import bzrlib.ui
     bzrlib.ui.ui_factory.clear_term()
     _bzr_logger.warning(*args, **kwargs)
 
+
+# configure convenient aliases for output routines
+#
+# TODO: deprecate them, have one name for each.
 info = note
 log_error = _bzr_logger.error
 error =     _bzr_logger.error
@@ -128,11 +152,11 @@
         out = fmt % tuple(real_args)
     else:
         out = fmt
-    timestamp = '%0.3f  ' % (time.time() - _start_time,)
+    timestamp = '%0.3f  ' % (time.time() - _bzr_log_start_time,)
     out = timestamp + out + '\n'
     _trace_file.write(out)
-    # TODO: jam 20051227 Consider flushing the trace file to help debugging
-    #_trace_file.flush()
+    # no need to flush here, the trace file is now linebuffered when it's
+    # opened.
 
 
 def mutter_callsite(stacklevel, fmt, *args):
@@ -162,44 +186,136 @@
         return
 
 
-def open_tracefile(tracefilename=None):
-    # Messages are always written to here, so that we have some
-    # information if something goes wrong.  In a future version this
-    # file will be removed on successful completion.
-    global _file_handler, _bzr_log_file, _bzr_log_filename
-    import codecs
-
-    if tracefilename is None:
-        if sys.platform == 'win32':
-            from bzrlib import win32utils
-            home = win32utils.get_home_location()
-        else:
-            home = os.path.expanduser('~')
-        _bzr_log_filename = os.path.join(home, '.bzr.log')
+def _get_bzr_log_filename():
+    # TODO: should this be overridden by $BZR_HOME?
+    if sys.platform == 'win32':
+        from bzrlib import win32utils
+        home = win32utils.get_home_location()
     else:
-        _bzr_log_filename = tracefilename
-
-    _bzr_log_filename = os.path.expanduser(_bzr_log_filename)
+        home = os.path.expanduser('~')
+    return os.path.join(home, '.bzr.log')
+
+
+def _open_bzr_log():
+    """Open the .bzr.log trace file.  
+
+    If the log is more than a particular length, the old file is renamed to
+    .bzr.log.old and a new file is started.  Otherwise, we append to the
+    existing file.
+
+    This sets the global _bzr_log_filename.
+    """
+    global _bzr_log_filename
+    _bzr_log_filename = _get_bzr_log_filename()
     _rollover_trace_maybe(_bzr_log_filename)
     try:
-        LINE_BUFFERED = 1
-        #tf = codecs.open(trace_fname, 'at', 'utf8', buffering=LINE_BUFFERED)
-        tf = open(_bzr_log_filename, 'at', LINE_BUFFERED)
-        _bzr_log_file = tf
-        # tf.tell() on windows always return 0 until some writing done
-        tf.write('\n')
-        if tf.tell() <= 2:
-            tf.write("this is a debug log for diagnosing/reporting problems in bzr\n")
-            tf.write("you can delete or truncate this file, or include sections in\n")
-            tf.write("bug reports to https://bugs.launchpad.net/bzr/+filebug\n\n")
-        _file_handler = logging.StreamHandler(tf)
-        fmt = r'[%(process)5d] %(asctime)s.%(msecs)03d %(levelname)s: %(message)s'
-        datefmt = r'%Y-%m-%d %H:%M:%S'
-        _file_handler.setFormatter(logging.Formatter(fmt, datefmt))
-        _file_handler.setLevel(logging.DEBUG)
-        logging.getLogger('').addHandler(_file_handler)
+        bzr_log_file = open(_bzr_log_filename, 'at', 1) # line buffered
+        # bzr_log_file.tell() on windows always return 0 until some writing done
+        bzr_log_file.write('\n')
+        if bzr_log_file.tell() <= 2:
+            bzr_log_file.write("this is a debug log for diagnosing/reporting problems in bzr\n")
+            bzr_log_file.write("you can delete or truncate this file, or include sections in\n")
+            bzr_log_file.write("bug reports to https://bugs.launchpad.net/bzr/+filebug\n\n")
+        return bzr_log_file
     except IOError, e:
         warning("failed to open trace file: %s" % (e))
+    # TODO: What should happen if we fail to open the trace file?  Maybe the
+    # objects should be pointed at /dev/null or the equivalent?  Currently
+    # returns None which will cause failures later.
+
+
+def enable_default_logging():
+    """Configure default logging: messages to stderr and debug to .bzr.log
+    
+    This should only be called once per process.
+
+    Non-command-line programs embedding bzrlib do not need to call this.  They
+    can instead either pass a file to _push_log_file, or act directly on
+    logging.getLogger("bzr").
+    
+    Output can be redirected away by calling _push_log_file.
+    """
+    # create encoded wrapper around stderr
+    bzr_log_file = _open_bzr_log()
+    push_log_file(bzr_log_file,
+        r'[%(process)5d] %(asctime)s.%(msecs)03d %(levelname)s: %(message)s',
+        r'%Y-%m-%d %H:%M:%S')
+    # after hooking output into bzr_log, we also need to attach a stderr
+    # handler, writing only at level info and with encoding
+    writer_factory = codecs.getwriter(osutils.get_terminal_encoding())
+    encoded_stderr = writer_factory(sys.stderr, errors='replace')
+    stderr_handler = logging.StreamHandler(encoded_stderr)
+    stderr_handler.setLevel(logging.INFO)
+    logging.getLogger('bzr').addHandler(stderr_handler)
+
+
+def push_log_file(to_file, log_format=None, date_format=None):
+    """Intercept log and trace messages and send them to a file.
+
+    :param to_file: A file-like object to which messages will be sent.
+
+    :returns: A memento that should be passed to _pop_log_file to restore the 
+    previously active logging.
+    """
+    global _trace_file
+    # make a new handler
+    new_handler = logging.StreamHandler(to_file)
+    new_handler.setLevel(logging.DEBUG)
+    if log_format is None:
+        log_format = '%(levelname)8s  %(message)s'
+    new_handler.setFormatter(logging.Formatter(log_format, date_format))
+    # save and remove any existing log handlers
+    bzr_logger = logging.getLogger('bzr')
+    old_handlers = bzr_logger.handlers[:]
+    del bzr_logger.handlers[:]
+    # set that as the default logger
+    bzr_logger.addHandler(new_handler)
+    bzr_logger.setLevel(logging.DEBUG)
+    # TODO: check if any changes are needed to the root logger
+    #
+    # TODO: also probably need to save and restore the level on bzr_logger.
+    # but maybe we can avoid setting the logger level altogether, and just set
+    # the level on the handler?
+    #
+    # save the old trace file
+    old_trace_file = _trace_file
+    # send traces to the new one
+    _trace_file = to_file
+    result = new_handler, _trace_file
+    return ('log_memento', old_handlers, new_handler, old_trace_file, to_file)
+
+
+def pop_log_file((magic, old_handlers, new_handler, old_trace_file, new_trace_file)):
+    """Undo changes to logging/tracing done by _push_log_file.
+
+    This flushes, but does not close the trace file.
+    
+    Takes the memento returned from _push_log_file."""
+    assert magic == 'log_memento'
+    global _trace_file
+    _trace_file = old_trace_file
+    bzr_logger = logging.getLogger('bzr')
+    bzr_logger.removeHandler(new_handler)
+    # must be closed, otherwise logging will try to close it atexit, and the
+    # file will likely already be closed underneath.
+    new_handler.close()
+    bzr_logger.handlers = old_handlers
+    new_trace_file.flush()
+
+
+ at symbol_versioning.deprecated_function(symbol_versioning.one_two)
+def enable_test_log(to_file):
+    """Redirect logging to a temporary file for a test
+    
+    :returns: an opaque reference that should be passed to disable_test_log
+    after the test completes.
+    """
+    return push_log_file(to_file)
+
+
+ at symbol_versioning.deprecated_function(symbol_versioning.one_two)
+def disable_test_log(memento):
+    return pop_log_file(memento)
 
 
 def log_exception_quietly():
@@ -209,28 +325,9 @@
     interesting to developers but not to users.  For example, 
     errors loading plugins.
     """
-    import traceback
     mutter(traceback.format_exc())
 
 
-def enable_default_logging():
-    """Configure default logging to stderr and .bzr.log"""
-    # FIXME: if this is run twice, things get confused
-    global _stderr_handler, _file_handler, _trace_file, _bzr_log_file
-    # create encoded wrapper around stderr
-    stderr = codecs.getwriter(osutils.get_terminal_encoding())(sys.stderr,
-        errors='replace')
-    _stderr_handler = logging.StreamHandler(stderr)
-    logging.getLogger('').addHandler(_stderr_handler)
-    _stderr_handler.setLevel(logging.INFO)
-    if not _file_handler:
-        open_tracefile()
-    _trace_file = _bzr_log_file
-    if _file_handler:
-        _file_handler.setLevel(logging.DEBUG)
-    _bzr_logger.setLevel(logging.DEBUG)
-
-
 def set_verbosity_level(level):
     """Set the verbosity level.
 
@@ -260,9 +357,9 @@
 def _update_logging_level(quiet=True):
     """Hide INFO messages if quiet."""
     if quiet:
-        _stderr_handler.setLevel(logging.WARNING)
+        _bzr_logger.setLevel(logging.WARNING)
     else:
-        _stderr_handler.setLevel(logging.INFO)
+        _bzr_logger.setLevel(logging.INFO)
 
 
 def is_quiet():
@@ -275,49 +372,13 @@
     return _verbosity_level > 0
 
 
+ at symbol_versioning.deprecated_function(symbol_versioning.one_two)
 def disable_default_logging():
     """Turn off default log handlers.
 
-    This is intended to be used by the test framework, which doesn't
-    want leakage from the code-under-test into the main logs.
-    """
-
-    l = logging.getLogger('')
-    l.removeHandler(_stderr_handler)
-    if _file_handler:
-        l.removeHandler(_file_handler)
-    _trace_file = None
-
-
-def enable_test_log(to_file):
-    """Redirect logging to a temporary file for a test
-    
-    returns an opaque reference that should be passed to disable_test_log
-    after the test completes.
-    """
-    disable_default_logging()
-    global _trace_file
-    global _trace_depth
-    hdlr = logging.StreamHandler(to_file)
-    hdlr.setLevel(logging.DEBUG)
-    hdlr.setFormatter(logging.Formatter('%(levelname)8s  %(message)s'))
-    _bzr_logger.addHandler(hdlr)
-    _bzr_logger.setLevel(logging.DEBUG)
-    result = hdlr, _trace_file, _trace_depth
-    _trace_file = to_file
-    _trace_depth += 1
-    return result
-
-
-def disable_test_log((test_log_hdlr, old_trace_file, old_trace_depth)):
-    _bzr_logger.removeHandler(test_log_hdlr)
-    test_log_hdlr.close()
-    global _trace_file
-    global _trace_depth
-    _trace_file = old_trace_file
-    _trace_depth = old_trace_depth
-    if not _trace_depth:
-        enable_default_logging()
+    Don't call this method, use _push_log_file and _pop_log_file instead.
+    """
+    pass
 
 
 def report_exception(exc_info, err_file):
@@ -364,7 +425,6 @@
 
 def report_bug(exc_info, err_file):
     """Report an exception that probably indicates a bug in bzr"""
-    import traceback
     exc_type, exc_object, exc_tb = exc_info
     err_file.write("bzr: ERROR: %s.%s: %s\n" % (
         exc_type.__module__, exc_type.__name__, exc_object))




More information about the bazaar-commits mailing list