Rev 5310: (lifeless) Aaron's output fix for bzr from the UDS sprint, in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Mon Jun 21 06:45:09 BST 2010


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

------------------------------------------------------------
revno: 5310 [merge]
revision-id: pqm at pqm.ubuntu.com-20100621054505-7b6lnkos9fcy3d4r
parent: pqm at pqm.ubuntu.com-20100621003342-mhnno5gywszhofxt
parent: robertc at robertcollins.net-20100621041616-w3unifc8ua57kppc
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Mon 2010-06-21 06:45:05 +0100
message:
  (lifeless) Aaron's output fix for bzr from the UDS sprint,
   tweaked to use a context manager for the whole library. (Aaron Bentley)
   (Robert Collins)
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzr                            bzr.py-20050313053754-5485f144c7006fa6
  bzrlib/__init__.py             __init__.py-20050309040759-33e65acf91bbcd5d
  bzrlib/smart/medium.py         medium.py-20061103051856-rgu2huy59fkz902q-1
  bzrlib/trace.py                trace.py-20050309040759-c8ed824bdcd4748a
  bzrlib/ui/__init__.py          ui.py-20050824083933-8cf663c763ba53a9
  doc/developers/overview.txt    overview.txt-20080904022501-ww2ggomrs5elxfm0-1
=== modified file 'NEWS'
--- a/NEWS	2010-06-20 22:55:07 +0000
+++ b/NEWS	2010-06-21 04:16:16 +0000
@@ -14,11 +14,25 @@
 Compatibility Breaks
 ********************
 
+* bzrlib library users now need to call ``__enter__`` and ``__exit__`` on
+  the result of ``bzrlib.initialize``. This change was made when fixing
+  the bad habit recent bzr versions have had of leaving progress bars 
+  behind on the screen. That required calling another function before
+  exiting the program, and it made sense to provide a full context
+  manager at the same time. (Robert Collins)
+
+* The ``bzr`` front end now requires a ``bzrlib.ui.ui_factory`` which is a
+  context manager in the Python 2.5 and above sense. The bzrlib base class
+  is such a manager, but third party UI factories which do not derive from
+  ``bzrlib.ui.UIFactory`` will be incompatible with the command line front
+  end.
+  
 * URLs like ``foo:bar/baz`` are now always parsed as a URL with scheme "foo"
   and path "bar/baz", even if bzr does not recognize "foo" as a known URL
   scheme.  Previously these URLs would be treated as local paths.
   (Gordon Tyler)
 
+
 New Features
 ************
 
@@ -56,6 +70,8 @@
   test that all commands available to the test suite have help.
   (Robert Collins, #177500)
 
+* Progress output is cleaned up when exiting.  (Aaron Bentley)
+
 * Raise ValueError instead of a string exception.
   (John Arbash Meinel, #586926)
 
@@ -181,8 +197,7 @@
 
 * ``bzr`` does not try to guess the username as ``username at hostname``
   and requires it to be explictly set. This can be set using ``bzr
-  whoami``.
-  (Parth Malwankar, #549310)
+  whoami``. (Parth Malwankar, #549310)
 
 * ``bzrlib.commands.Command`` will now raise ValueError during
   construction if there is no __doc__ set. (Note, this will be reverted in

=== modified file 'bzr'
--- a/bzr	2010-06-16 12:47:51 +0000
+++ b/bzr	2010-06-21 03:55:08 +0000
@@ -135,11 +135,14 @@
 
 
 if __name__ == '__main__':
-    bzrlib.initialize()
-    exit_val = bzrlib.commands.main()
-
-    if profiling:
-        profile_imports.log_stack_info(sys.stderr)
+    library_state = bzrlib.initialize()
+    library_state.__enter__()
+    try:
+        exit_val = bzrlib.commands.main()
+        if profiling:
+            profile_imports.log_stack_info(sys.stderr)
+    finally:
+        library_state.__exit__(None, None, None)
 
     # By this point we really have completed everything we want to do, and
     # there's no point doing any additional cleanup.  Abruptly exiting here

=== modified file 'bzrlib/__init__.py'
--- a/bzrlib/__init__.py	2010-06-16 12:47:51 +0000
+++ b/bzrlib/__init__.py	2010-06-21 04:16:16 +0000
@@ -14,7 +14,22 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
-"""bzr library"""
+"""All of bzr.
+
+Developer documentation is available at
+http://doc.bazaar.canonical.com/bzr.dev/developers/
+
+The project website is at http://bazaar.canonical.com/
+
+Some particularly interesting things in bzrlib are:
+
+ * bzrlib.initialize -- setup the library for use
+ * bzrlib.plugin.load_plugins -- load all installed plugins
+ * bzrlib.branch.Branch.open -- open a branch
+ * bzrlib.workingtree.WorkingTree.open -- open a working tree
+
+We hope you enjoy this library.
+"""
 
 import time
 
@@ -114,15 +129,96 @@
 __version__ = _format_version_tuple(version_info)
 version_string = __version__
 
-
-def test_suite():
-    import tests
-    return tests.test_suite()
-
-
-def initialize(
-    setup_ui=True,
-    stdin=None, stdout=None, stderr=None):
+# bzr has various bits of global state that are slowly being eliminated.
+# This variable is intended to permit any new state-like things to be attached
+# to a BzrLibraryState object rather than getting new global variables that
+# need to be hunted down. Accessing the current BzrLibraryState through this
+# variable is not encouraged: it is better to pass it around as part of the
+# context of an operation than to look it up directly, but when that is too
+# hard, it is better to use this variable than to make a branch new global
+# variable.
+# If using this variable by looking it up (because it can't be easily obtained)
+# it is important to store the reference you get, rather than looking it up
+# repeatedly; that way your code will behave properly in the bzrlib test suite
+# and from programs that do use multiple library contexts.
+global_state = None
+
+
+class BzrLibraryState(object):
+    """The state about how bzrlib has been configured.
+    
+    :ivar saved_state: The bzrlib.global_state at the time __enter__ was
+        called.
+    :ivar cleanups: An ObjectWithCleanups which can be used for cleanups that
+        should occur when the use of bzrlib is completed. This is initialised
+        in __enter__ and executed in __exit__.
+    """
+
+    def __init__(self, setup_ui=True, stdin=None, stdout=None, stderr=None):
+        """Create library start for normal use of bzrlib.
+
+        Most applications that embed bzrlib, including bzr itself, should just
+        call bzrlib.initialize(), but it is possible to use the state class
+        directly.
+
+        More options may be added in future so callers should use named
+        arguments.
+
+        BzrLibraryState implements the Python 2.5 Context Manager protocol, and
+        can be used with the with statement. Upon __enter__ the global
+        variables in use by bzr are set, and they are cleared on __exit__.
+
+        :param setup_ui: If true (default) use a terminal UI; otherwise 
+            some other ui_factory must be assigned to `bzrlib.ui.ui_factory` by
+            the caller.
+        :param stdin, stdout, stderr: If provided, use these for terminal IO;
+            otherwise use the files in `sys`.
+        """
+        self.setup_ui = setup_ui
+        self.stdin = stdin
+        self.stdout = stdout
+        self.stderr = stderr
+
+    def __enter__(self):
+        # NB: This function tweaks so much global state it's hard to test it in
+        # isolation within the same interpreter.  It's not reached on normal
+        # in-process run_bzr calls.  If it's broken, we expect that
+        # TestRunBzrSubprocess may fail.
+        if version_info[3] == 'final':
+            from bzrlib.symbol_versioning import suppress_deprecation_warnings
+            suppress_deprecation_warnings(override=True)
+
+        import bzrlib.cleanup
+        import bzrlib.trace
+        self.cleanups = bzrlib.cleanup.ObjectWithCleanups()
+        bzrlib.trace.enable_default_logging()
+
+        if self.setup_ui:
+            import bzrlib.ui
+            stdin = self.stdin or sys.stdin
+            stdout = self.stdout or sys.stdout
+            stderr = self.stderr or sys.stderr
+            bzrlib.ui.ui_factory = bzrlib.ui.make_ui_for_terminal(
+                stdin, stdout, stderr)
+        global global_state
+        self.saved_state = global_state
+        global_state = self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.cleanups.cleanup_now()
+        import bzrlib.ui
+        bzrlib.trace._flush_stdout_stderr()
+        bzrlib.trace._flush_trace()
+        import bzrlib.osutils
+        bzrlib.osutils.report_extension_load_failures()
+        bzrlib.ui.ui_factory.__exit__(None, None, None)
+        bzrlib.ui.ui_factory = None
+        global global_state
+        global_state = self.saved_state
+        return False # propogate exceptions.
+
+
+def initialize(setup_ui=True, stdin=None, stdout=None, stderr=None):
     """Set up everything needed for normal use of bzrlib.
 
     Most applications that embed bzrlib, including bzr itself, should call
@@ -131,39 +227,20 @@
     More options may be added in future so callers should use named arguments.
 
     :param setup_ui: If true (default) use a terminal UI; otherwise 
-        something else must be put into `bzrlib.ui.ui_factory`.
+        some other ui_factory must be assigned to `bzrlib.ui.ui_factory` by
+        the caller.
     :param stdin, stdout, stderr: If provided, use these for terminal IO;
         otherwise use the files in `sys`.
+    :return: A context manager for the use of bzrlib. The __enter__ method of
+        this context needs to be called before it takes effect, and the __exit__
+        should be called by the caller before exiting their process or
+        otherwise stopping use of bzrlib. Advanced callers can use
+        BzrLibraryState directly.
     """
-    # TODO: mention this in a guide to embedding bzrlib
-    #
-    # NB: This function tweaks so much global state it's hard to test it in
-    # isolation within the same interpreter.  It's not reached on normal
-    # in-process run_bzr calls.  If it's broken, we expect that
-    # TestRunBzrSubprocess may fail.
-    
-    import atexit
-    import bzrlib.trace
-
-    bzrlib.trace.enable_default_logging()
-    atexit.register(bzrlib.trace._flush_stdout_stderr)
-    atexit.register(bzrlib.trace._flush_trace)
-
-    import bzrlib.ui
-    if stdin is None:
-        stdin = sys.stdin
-    if stdout is None:
-        stdout = sys.stdout
-    if stderr is None:
-        stderr = sys.stderr
-
-    if setup_ui:
-        bzrlib.ui.ui_factory = bzrlib.ui.make_ui_for_terminal(
-            stdin, stdout, stderr)
-
-    if bzrlib.version_info[3] == 'final':
-        from bzrlib.symbol_versioning import suppress_deprecation_warnings
-        suppress_deprecation_warnings(override=True)
-
-    import bzrlib.osutils
-    atexit.register(osutils.report_extension_load_failures)
+    return BzrLibraryState(setup_ui=setup_ui, stdin=stdin,
+        stdout=stdout, stderr=stderr)
+
+
+def test_suite():
+    import tests
+    return tests.test_suite()

=== modified file 'bzrlib/smart/medium.py'
--- a/bzrlib/smart/medium.py	2010-06-16 07:45:53 +0000
+++ b/bzrlib/smart/medium.py	2010-06-21 03:55:08 +0000
@@ -28,9 +28,9 @@
 import sys
 import urllib
 
+import bzrlib
 from bzrlib.lazy_import import lazy_import
 lazy_import(globals(), """
-import atexit
 import socket
 import thread
 import weakref
@@ -494,16 +494,16 @@
 class _DebugCounter(object):
     """An object that counts the HPSS calls made to each client medium.
 
-    When a medium is garbage-collected, or failing that when atexit functions
-    are run, the total number of calls made on that medium are reported via
-    trace.note.
+    When a medium is garbage-collected, or failing that when
+    bzrlib.global_state exits, the total number of calls made on that medium
+    are reported via trace.note.
     """
 
     def __init__(self):
         self.counts = weakref.WeakKeyDictionary()
         client._SmartClient.hooks.install_named_hook(
             'call', self.increment_call_count, 'hpss call counter')
-        atexit.register(self.flush_all)
+        bzrlib.global_state.cleanups.addCleanup(self.flush_all)
 
     def track(self, medium):
         """Start tracking calls made to a medium.

=== modified file 'bzrlib/trace.py'
--- a/bzrlib/trace.py	2010-05-21 06:14:50 +0000
+++ b/bzrlib/trace.py	2010-06-21 03:15:55 +0000
@@ -374,7 +374,7 @@
     _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
+    # must be closed, otherwise logging will try to close it at exit, and the
     # file will likely already be closed underneath.
     new_handler.close()
     bzr_logger.handlers = old_handlers
@@ -540,7 +540,7 @@
 
 
 def _flush_stdout_stderr():
-    # installed into an atexit hook by bzrlib.initialize()
+    # called from the bzrlib library finalizer returned by bzrlib.initialize()
     try:
         sys.stdout.flush()
         sys.stderr.flush()
@@ -553,7 +553,7 @@
 
 
 def _flush_trace():
-    # run from atexit hook
+    # called from the bzrlib library finalizer returned by bzrlib.initialize()
     global _trace_file
     if _trace_file:
         _trace_file.flush()

=== modified file 'bzrlib/ui/__init__.py'
--- a/bzrlib/ui/__init__.py	2010-03-25 07:34:15 +0000
+++ b/bzrlib/ui/__init__.py	2010-06-21 03:15:55 +0000
@@ -106,6 +106,9 @@
     This tells the library how to display things to the user.  Through this
     layer different applications can choose the style of UI.
 
+    UI Factories are also context managers, for some syntactic sugar some users
+    need.
+
     :ivar suppressed_warnings: Identifiers for user warnings that should 
         no be emitted.
     """
@@ -123,6 +126,22 @@
         self.suppressed_warnings = set()
         self._quiet = False
 
+    def __enter__(self):
+        """Context manager entry support.
+
+        Override in a concrete factory class if initialisation before use is
+        needed.
+        """
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        """Context manager exit support.
+
+        Override in a concrete factory class if more cleanup than a simple
+        self.clear_term() is needed when the UIFactory is finished with.
+        """
+        self.clear_term()
+        return False # propogate exceptions.
+
     def be_quiet(self, state):
         """Tell the UI to be more quiet, or not.
 
@@ -352,7 +371,6 @@
                 "without an upgrade path.\n" % (inter.target._format,))
 
 
-
 class SilentUIFactory(UIFactory):
     """A UI Factory which never prints anything.
 

=== modified file 'doc/developers/overview.txt'
--- a/doc/developers/overview.txt	2010-05-23 20:44:49 +0000
+++ b/doc/developers/overview.txt	2010-06-21 03:55:08 +0000
@@ -13,6 +13,38 @@
 to the Bazaar mailing list.  
 
 
+Using bzrlib
+############
+
+Within bzr
+==========
+
+When using bzrlib within the ``bzr`` program (for instance as a bzr
+plugin), bzrlib's global state is already available for use.
+
+From outside bzr
+================
+
+To use bzrlib outside of ``bzr`` some global state needs to be setup.
+bzrlib needs ways to handle user input, passwords, a place to emit
+progress bars, logging setup appropriately for your program. The easiest
+way to set all this up in the same fashion ``bzr`` does is to call
+``bzrlib.initialize``. This returns a context manager within which bzrlib
+functions will work correctly. See the pydoc for ``bzrlib.initialize`` for
+more information. In Python 2.4 the ``with`` keyword is not supported and
+so you need to use the context manager manually::
+
+  # This sets up your ~/.bzr.log, ui factory and so on and so forth. It is
+  # not safe to use as a doctest.
+  library_state = bzrlib.initialize()
+  library_state.__enter__()
+  try:
+      pass
+      # do stuff here
+  finally:
+      library_state.__exit__(None, None, None)
+
+
 Core classes
 ############
 




More information about the bazaar-commits mailing list