From c70d3b38b65153fceeec6b745673cef02ac9fca1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 25 Feb 2013 22:56:22 +0530 Subject: [PATCH] Add bzr support --- powerline/lib/vcs/__init__.py | 7 ++-- powerline/lib/vcs/bzr.py | 62 +++++++++++++++++++++++++++++++++++ tests/install.sh | 2 +- tests/test_lib.py | 30 +++++++++++++++-- 4 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 powerline/lib/vcs/bzr.py diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index 8e583da4..14c5cc72 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -3,8 +3,11 @@ import os from powerline.lib.memoize import memoize -vcs_props = (('git', '.git', os.path.exists), - ('mercurial', '.hg', os.path.isdir)) +vcs_props = ( + ('git', '.git', os.path.exists), + ('mercurial', '.hg', os.path.isdir), + ('bzr', '.bzr', os.path.isdir), +) def generate_directories(path): diff --git a/powerline/lib/vcs/bzr.py b/powerline/lib/vcs/bzr.py new file mode 100644 index 00000000..1843001f --- /dev/null +++ b/powerline/lib/vcs/bzr.py @@ -0,0 +1,62 @@ +from __future__ import absolute_import, unicode_literals, division, print_function + +import sys +from io import StringIO + +from bzrlib import (branch, workingtree, status, library_state, trace, ui) + + +class CoerceIO(StringIO): + def write(self, arg): + if isinstance(arg, bytes): + arg = arg.decode('utf-8', 'replace') + return super(CoerceIO, self).write(arg) + + +class Repository(object): + def __init__(self, directory): + if isinstance(directory, bytes): + directory = directory.decode(sys.getfilesystemencoding() or sys.getdefaultencoding() or 'utf-8') + self.directory = directory + self.state = library_state.BzrLibraryState(ui=ui.SilentUIFactory, trace=trace.DefaultConfig()) + + def status(self, path=None): + '''Return status of repository or file. + + Without file argument: returns status of the repository: + + :"D?": dirty (tracked modified files: added, removed, deleted, modified), + :"?U": untracked-dirty (added, but not tracked files) + :None: clean (status is empty) + + With file argument: returns status of this file: The status codes are + those returned by bzr status -S + ''' + try: + return self._status(path) + except: + pass + + def _status(self, path): + buf = CoerceIO() + w = workingtree.WorkingTree.open(self.directory) + status.show_tree_status(w, specific_files=[path] if path else None, to_file=buf, short=True) + raw = buf.getvalue() + if not raw.strip(): + return + if path: + return raw[:2] + dirtied = untracked = ' ' + for line in raw.splitlines(): + if len(line) > 1 and line[1] in 'ACDMRIN': + dirtied = 'D' + elif line and line[0] == '?': + untracked = 'U' + return dirtied + untracked + + def branch(self): + try: + b = branch.Branch.open(self.directory) + return b.nick or None + except: + pass diff --git a/tests/install.sh b/tests/install.sh index ddce7e74..9a5b1cd2 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -2,7 +2,7 @@ pip install . if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then # Python 2 - pip install mercurial + pip install mercurial bzr if python -c 'import sys; sys.exit(1 * (sys.version_info[1] >= 7))' ; then # Python 2.6 pip install unittest2 diff --git a/tests/test_lib.py b/tests/test_lib.py index 46dd03b3..c475808b 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -39,7 +39,7 @@ class TestLib(TestCase): self.assertEqual(humanize_bytes(1000000000, si_prefix=False), '953.7 MiB') -use_mercurial = sys.version_info < (3, 0) +use_mercurial = use_bzr = sys.version_info < (3, 0) class TestVCS(TestCase): @@ -78,6 +78,27 @@ class TestVCS(TestCase): self.assertEqual(repo.status('file'), 'A') os.remove(os.path.join('hg_repo', 'file')) + if use_bzr: + def test_bzr(self): + repo = guess(path='bzr_repo') + self.assertNotEqual(repo, None, 'No bzr repo found. Do you have bzr installed?') + self.assertEqual(repo.branch(), 'test_powerline') + self.assertEqual(repo.status(), None) + with open(os.path.join('bzr_repo', 'file'), 'w') as f: + f.write('abc') + self.assertEqual(repo.status(), ' U') + self.assertEqual(repo.status('file'), '? ') + call(['bzr', 'add', '.'], cwd='bzr_repo', stdout=PIPE) + self.assertEqual(repo.status(), 'D ') + self.assertEqual(repo.status('file'), '+N') + call(['bzr', 'commit', '-m', 'initial commit'], cwd='bzr_repo', stdout=PIPE, stderr=PIPE) + self.assertEqual(repo.status(), None) + with open(os.path.join('bzr_repo', 'file'), 'w') as f: + f.write('def') + self.assertEqual(repo.status(), 'D ') + self.assertEqual(repo.status('file'), ' M') + self.assertEqual(repo.status('notexist'), None) + os.remove(os.path.join('bzr_repo', 'file')) old_HGRCPATH = None old_cwd = None @@ -99,12 +120,17 @@ def setUpModule(): with open(os.path.join('hg_repo', '.hg', 'hgrc'), 'w') as hgrc: hgrc.write('[ui]\n') hgrc.write('username = Foo \n') + if use_bzr: + call(['bzr', 'init', '--quiet', 'bzr_repo']) + call(['bzr', 'config', 'email=Foo '], cwd='bzr_repo') + call(['bzr', 'config', 'nickname=test_powerline'], cwd='bzr_repo') + call(['bzr', 'config', 'create_signatures=0'], cwd='bzr_repo') def tearDownModule(): global old_cwd global old_HGRCPATH - for repo_dir in ['git_repo'] + (['hg_repo'] if use_mercurial else []): + for repo_dir in (['git_repo'] + (['hg_repo'] if use_mercurial else []) + (['bzr_repo'] if use_bzr else [])): for root, dirs, files in list(os.walk(repo_dir, topdown=False)): for file in files: os.remove(os.path.join(root, file))