Skip to content

Commit

Permalink
Added version_info property to git command. Its cached and efficient,…
Browse files Browse the repository at this point in the history
… including test
  • Loading branch information
Byron committed Jun 7, 2011
1 parent e11f12a commit e583a9e
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 71 deletions.
29 changes: 24 additions & 5 deletions git/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
# the BSD License: http://www.opensource.org/licenses/bsd-license.php

import os, sys
from util import *
from util import (
LazyMixin,
stream_copy
)
from exc import GitCommandError

from subprocess import (
Expand All @@ -26,7 +29,7 @@
def dashify(string):
return string.replace('_', '-')

class Git(object):
class Git(LazyMixin):
"""
The Git class manages communication with the Git binary.
Expand All @@ -41,7 +44,7 @@ class Git(object):
of the command to stdout.
Set its value to 'full' to see details about the returned values.
"""
__slots__ = ("_working_dir", "cat_file_all", "cat_file_header")
__slots__ = ("_working_dir", "cat_file_all", "cat_file_header", "_version_info")

# CONFIGURATION
# The size in bytes read from stdout when copying git's output to another stream
Expand Down Expand Up @@ -214,14 +217,30 @@ def __getattr__(self, name):
"""A convenience method as it allows to call the command as if it was
an object.
:return: Callable object that will execute call _call_process with your arguments."""
if name[:1] == '_':
raise AttributeError(name)
if name[0] == '_':
return LazyMixin.__getattr__(self, name)
return lambda *args, **kwargs: self._call_process(name, *args, **kwargs)

def _set_cache_(self, attr):
if attr == '_version_info':
version_numbers = self._call_process('version').rpartition(' ')[2]
self._version_info = tuple(int(n) for n in version_numbers.split('.'))
else:
super(Git, self)._set_cache_(attr)
#END handle version info


@property
def working_dir(self):
""":return: Git directory we are working on"""
return self._working_dir

@property
def version_info(self):
""":return: tuple(int, ...) tuple with integers representing the major, minor
and additional version numbers as parsed from git version.
This value is generated on demand and is cached"""
return self._version_info

def execute(self, command,
istream=None,
Expand Down
148 changes: 82 additions & 66 deletions git/test/test_git.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,80 +5,96 @@
# the BSD License: http://www.opensource.org/licenses/bsd-license.php

import os, sys
from git.test.lib import *
from git.test.lib import (
TestBase,
patch_object,
raises,
assert_equal,
assert_true,
assert_match,
fixture_path
)
from git import Git, GitCommandError

class TestGit(TestCase):

@classmethod
def setUpAll(cls):
cls.git = Git(GIT_REPO)
class TestGit(TestBase):

@classmethod
def setUpAll(cls):
super(TestGit, cls).setUpAll()
cls.git = Git(cls.rorepo.working_dir)

@patch_object(Git, 'execute')
def test_call_process_calls_execute(self, git):
git.return_value = ''
self.git.version()
assert_true(git.called)
assert_equal(git.call_args, ((['git', 'version'],), {}))
@patch_object(Git, 'execute')
def test_call_process_calls_execute(self, git):
git.return_value = ''
self.git.version()
assert_true(git.called)
assert_equal(git.call_args, ((['git', 'version'],), {}))

@raises(GitCommandError)
def test_it_raises_errors(self):
self.git.this_does_not_exist()
@raises(GitCommandError)
def test_it_raises_errors(self):
self.git.this_does_not_exist()


def test_it_transforms_kwargs_into_git_command_arguments(self):
assert_equal(["-s"], self.git.transform_kwargs(**{'s': True}))
assert_equal(["-s5"], self.git.transform_kwargs(**{'s': 5}))
def test_it_transforms_kwargs_into_git_command_arguments(self):
assert_equal(["-s"], self.git.transform_kwargs(**{'s': True}))
assert_equal(["-s5"], self.git.transform_kwargs(**{'s': 5}))

assert_equal(["--max-count"], self.git.transform_kwargs(**{'max_count': True}))
assert_equal(["--max-count=5"], self.git.transform_kwargs(**{'max_count': 5}))
assert_equal(["--max-count"], self.git.transform_kwargs(**{'max_count': True}))
assert_equal(["--max-count=5"], self.git.transform_kwargs(**{'max_count': 5}))

assert_equal(["-s", "-t"], self.git.transform_kwargs(**{'s': True, 't': True}))
assert_equal(["-s", "-t"], self.git.transform_kwargs(**{'s': True, 't': True}))

def test_it_executes_git_to_shell_and_returns_result(self):
assert_match('^git version [\d\.]{2}.*$', self.git.execute(["git","version"]))
def test_it_executes_git_to_shell_and_returns_result(self):
assert_match('^git version [\d\.]{2}.*$', self.git.execute(["git","version"]))

def test_it_accepts_stdin(self):
filename = fixture_path("cat_file_blob")
fh = open(filename, 'r')
assert_equal("70c379b63ffa0795fdbfbc128e5a2818397b7ef8",
self.git.hash_object(istream=fh, stdin=True))
fh.close()
def test_it_accepts_stdin(self):
filename = fixture_path("cat_file_blob")
fh = open(filename, 'r')
assert_equal("70c379b63ffa0795fdbfbc128e5a2818397b7ef8",
self.git.hash_object(istream=fh, stdin=True))
fh.close()

@patch_object(Git, 'execute')
def test_it_ignores_false_kwargs(self, git):
# this_should_not_be_ignored=False implies it *should* be ignored
output = self.git.version(pass_this_kwarg=False)
assert_true("pass_this_kwarg" not in git.call_args[1])

def test_persistent_cat_file_command(self):
# read header only
import subprocess as sp
hexsha = "b2339455342180c7cc1e9bba3e9f181f7baa5167"
g = self.git.cat_file(batch_check=True, istream=sp.PIPE,as_process=True)
g.stdin.write("b2339455342180c7cc1e9bba3e9f181f7baa5167\n")
g.stdin.flush()
obj_info = g.stdout.readline()

# read header + data
g = self.git.cat_file(batch=True, istream=sp.PIPE,as_process=True)
g.stdin.write("b2339455342180c7cc1e9bba3e9f181f7baa5167\n")
g.stdin.flush()
obj_info_two = g.stdout.readline()
assert obj_info == obj_info_two

# read data - have to read it in one large chunk
size = int(obj_info.split()[2])
data = g.stdout.read(size)
terminating_newline = g.stdout.read(1)

# now we should be able to read a new object
g.stdin.write("b2339455342180c7cc1e9bba3e9f181f7baa5167\n")
g.stdin.flush()
assert g.stdout.readline() == obj_info


# same can be achived using the respective command functions
hexsha, typename, size = self.git.get_object_header(hexsha)
hexsha, typename_two, size_two, data = self.git.get_object_data(hexsha)
assert typename == typename_two and size == size_two
@patch_object(Git, 'execute')
def test_it_ignores_false_kwargs(self, git):
# this_should_not_be_ignored=False implies it *should* be ignored
output = self.git.version(pass_this_kwarg=False)
assert_true("pass_this_kwarg" not in git.call_args[1])

def test_persistent_cat_file_command(self):
# read header only
import subprocess as sp
hexsha = "b2339455342180c7cc1e9bba3e9f181f7baa5167"
g = self.git.cat_file(batch_check=True, istream=sp.PIPE,as_process=True)
g.stdin.write("b2339455342180c7cc1e9bba3e9f181f7baa5167\n")
g.stdin.flush()
obj_info = g.stdout.readline()

# read header + data
g = self.git.cat_file(batch=True, istream=sp.PIPE,as_process=True)
g.stdin.write("b2339455342180c7cc1e9bba3e9f181f7baa5167\n")
g.stdin.flush()
obj_info_two = g.stdout.readline()
assert obj_info == obj_info_two

# read data - have to read it in one large chunk
size = int(obj_info.split()[2])
data = g.stdout.read(size)
terminating_newline = g.stdout.read(1)

# now we should be able to read a new object
g.stdin.write("b2339455342180c7cc1e9bba3e9f181f7baa5167\n")
g.stdin.flush()
assert g.stdout.readline() == obj_info


# same can be achived using the respective command functions
hexsha, typename, size = self.git.get_object_header(hexsha)
hexsha, typename_two, size_two, data = self.git.get_object_data(hexsha)
assert typename == typename_two and size == size_two

def test_version(self):
v = self.git.version_info
assert isinstance(v, tuple)
for n in v:
assert isinstance(n, int)
#END verify number types

0 comments on commit e583a9e

Please sign in to comment.