From 690828ce2e03ce32c5a66186c543d7c5050287e4 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 6 Jul 2011 22:11:48 +0200 Subject: Added basis for initial dulwich integration. Many basic issues should surface while integrating this --- git/db/dulwich/__init__.py | 13 +++++++++++++ git/db/dulwich/base.py | 6 ++++++ git/test/db/dulwich/__init__.py | 4 ++++ git/test/db/dulwich/test_base.py | 22 ++++++++++++++++++++++ git/test/db/lib.py | 3 ++- git/test/lib/helper.py | 6 +++++- 6 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 git/db/dulwich/__init__.py create mode 100644 git/db/dulwich/base.py create mode 100644 git/test/db/dulwich/__init__.py create mode 100644 git/test/db/dulwich/test_base.py (limited to 'git') diff --git a/git/db/dulwich/__init__.py b/git/db/dulwich/__init__.py new file mode 100644 index 00000000..92d30941 --- /dev/null +++ b/git/db/dulwich/__init__.py @@ -0,0 +1,13 @@ +"""Dulwich module initialization""" + +def init_dulwich(): + """:raise ImportError: if dulwich is not present""" + try: + import dulwich + except ImportError: + raise ImportError("Could not find 'dulwich' in the PYTHONPATH - dulwich functionality is not available") + #END handle dulwich import + + + +init_dulwich() diff --git a/git/db/dulwich/base.py b/git/db/dulwich/base.py new file mode 100644 index 00000000..cd1d71c8 --- /dev/null +++ b/git/db/dulwich/base.py @@ -0,0 +1,6 @@ +"""Module with some basic database implementations""" + + +__all__ = [] + + diff --git a/git/test/db/dulwich/__init__.py b/git/test/db/dulwich/__init__.py new file mode 100644 index 00000000..8a681e42 --- /dev/null +++ b/git/test/db/dulwich/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors +# +# This module is part of GitDB and is released under +# the New BSD License: http://www.opensource.org/licenses/bsd-license.php diff --git a/git/test/db/dulwich/test_base.py b/git/test/db/dulwich/test_base.py new file mode 100644 index 00000000..f3489014 --- /dev/null +++ b/git/test/db/dulwich/test_base.py @@ -0,0 +1,22 @@ +# Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors +# +# This module is part of GitDB and is released under +# the New BSD License: http://www.opensource.org/licenses/bsd-license.php +from git.test.db.base import RepoBase +from git.db.complex import PureCompatibilityGitDB + +try: + import git.db.dulwich # import test + + class TestPyDBBase(RepoBase): + + RepoCls = PureCompatibilityGitDB + + # def test_basics(self): + # pass + +except ImportError: + del(RepoBase) + import warnings + warnings.warn("Skipped all dulwich tests as they are not in the path") +#END handle import diff --git a/git/test/db/lib.py b/git/test/db/lib.py index 499ca252..2b3ddde5 100644 --- a/git/test/db/lib.py +++ b/git/test/db/lib.py @@ -70,7 +70,8 @@ class TestDBBase(TestBase): each test type has its own repository """ if cls.needs_ro_repo: - assert cls.RepoCls is not None, "RepoCls class member must be set" + if cls is not TestDBBase: + assert cls.RepoCls is not None, "RepoCls class member must be set in %s" % cls cls.rorepo = cls.RepoCls(rorepo_dir()) #END handle rorepo diff --git a/git/test/lib/helper.py b/git/test/lib/helper.py index d9a92a52..ef2d3280 100644 --- a/git/test/lib/helper.py +++ b/git/test/lib/helper.py @@ -207,7 +207,11 @@ class GlobalsItemDeletorMetaCls(type): new_type = super(GlobalsItemDeletorMetaCls, metacls).__new__(metacls, name, bases, clsdict) if name != metacls.ModuleToDelete: mod = __import__(new_type.__module__, globals(), locals(), new_type.__module__) - delattr(mod, metacls.ModuleToDelete) + try: + delattr(mod, metacls.ModuleToDelete) + except AttributeError: + pass + #END skip case that people import our base without actually using it #END handle deletion return new_type -- cgit v1.2.1 From 80aa40537a3596f24593b5a67adb6d635fe4fa22 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Jul 2011 12:55:03 +0200 Subject: Added auto-skip mixin metacls, some serious brainfuck, if the required module was not found. Its actually a nice mix between decorators which are class types, and a mixin as a metaclass, which applies said decorator. The InstanceDecorator wouldn't actually be needed, but it adds flexibility. Maybe it should be removed ... --- git/test/db/dulwich/lib.py | 18 ++++++++ git/test/db/dulwich/test_base.py | 25 ++++++----- git/test/lib/helper.py | 94 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 123 insertions(+), 14 deletions(-) create mode 100644 git/test/db/dulwich/lib.py (limited to 'git') diff --git a/git/test/db/dulwich/lib.py b/git/test/db/dulwich/lib.py new file mode 100644 index 00000000..a1110ffa --- /dev/null +++ b/git/test/db/dulwich/lib.py @@ -0,0 +1,18 @@ +"""dulwich specific utilities, as well as all the default ones""" + +from git.test.lib import * + +#{ Decoorators + +def needs_dulwich_or_skip(func): + """Skip this test if we have no dulwich - print warning""" + return needs_module_or_skip('dulwich')(func) + +#}END decorators + +#{ MetaClasses + +class DulwichRequiredMetaMixin(InheritedTestMethodsOverrideWrapperMetaClsAutoMixin): + decorator = [needs_dulwich_or_skip] + +#} END metaclasses diff --git a/git/test/db/dulwich/test_base.py b/git/test/db/dulwich/test_base.py index f3489014..9bc9c394 100644 --- a/git/test/db/dulwich/test_base.py +++ b/git/test/db/dulwich/test_base.py @@ -2,21 +2,22 @@ # # This module is part of GitDB and is released under # the New BSD License: http://www.opensource.org/licenses/bsd-license.php +from lib import * from git.test.db.base import RepoBase from git.db.complex import PureCompatibilityGitDB try: - import git.db.dulwich # import test + import dulwich +except ImportError: + # om this case, all other dulwich tests will be skipped + pass - class TestPyDBBase(RepoBase): - - RepoCls = PureCompatibilityGitDB +class TestPyDBBase(RepoBase): + __metaclass__ = DulwichRequiredMetaMixin + RepoCls = PureCompatibilityGitDB + + @needs_dulwich_or_skip + def test_basics(self): + import dulwich + pass - # def test_basics(self): - # pass - -except ImportError: - del(RepoBase) - import warnings - warnings.warn("Skipped all dulwich tests as they are not in the path") -#END handle import diff --git a/git/test/lib/helper.py b/git/test/lib/helper.py index ef2d3280..87600489 100644 --- a/git/test/lib/helper.py +++ b/git/test/lib/helper.py @@ -12,6 +12,9 @@ import tempfile import shutil import cStringIO +import warnings +from nose import SkipTest + from base import ( maketemp, rorepo_dir @@ -19,8 +22,9 @@ from base import ( __all__ = ( - 'StringProcessAdapter', 'GlobalsItemDeletorMetaCls', - 'with_rw_repo', 'with_rw_and_rw_remote_repo', 'TestBase', 'TestCase', + 'StringProcessAdapter', 'GlobalsItemDeletorMetaCls', 'InheritedTestMethodsOverrideWrapperInstanceDecorator', + 'InheritedTestMethodsOverrideWrapperMetaClsAutoMixin', + 'with_rw_repo', 'with_rw_and_rw_remote_repo', 'TestBase', 'TestCase', 'needs_module_or_skip' ) @@ -191,6 +195,27 @@ def with_rw_and_rw_remote_repo(working_tree_ref): return argument_passer +def needs_module_or_skip(module): + """Decorator to be used for test cases only. + Print a warning if the given module could not be imported, and skip the test. + Otherwise run the test as usual + :param module: the name of the module to skip""" + def argpasser(func): + def wrapper(self, *args, **kwargs): + try: + __import__(module) + except ImportError: + msg = "Module %r is required to run this test - skipping" % module + warnings.warn(msg) + raise SkipTest(msg) + #END check import + return func(self, *args, **kwargs) + #END wrapper + wrapper.__name__ = func.__name__ + return wrapper + #END argpasser + return argpasser + #} END decorators #{ Meta Classes @@ -214,6 +239,71 @@ class GlobalsItemDeletorMetaCls(type): #END skip case that people import our base without actually using it #END handle deletion return new_type + + +class InheritedTestMethodsOverrideWrapperInstanceDecorator(object): + """Utility to wrap all inherited methods into a given decorator and set up new + overridden methods on our actual type. This allows to adjust tests which are inherited + by our parent type, automatically. The decorator set in a derived type should + do what it has to do, possibly skipping the test if some prerequesites are not met. + + To use it, instatiate it and use it as a wrapper for the __new__ function of your metacls, as in + + __new__ = @InheritedTestMethodsOverrideWrapperInstanceDecorator(mydecorator)(MyMetaclsBase.__new__)""" + + + def __init__(self, decorator): + self.decorator = decorator + + def _patch_methods_recursive(self, bases, clsdict): + """depth-first patching of methods""" + for base in bases: + self._patch_methods_recursive(base.__bases__, clsdict) + for name, item in base.__dict__.iteritems(): + if not name.startswith('test_'): + continue + #END skip non-tests + clsdict[name] = self.decorator(item) + #END for each item + #END for each base + + def __call__(self, func): + def wrapper(metacls, name, bases, clsdict): + self._patch_methods_recursive(bases, clsdict) + return func(metacls, name, bases, clsdict) + #END wrapper + assert func.__name__ == '__new__', "Can only wrap __new__ function of metaclasses" + wrapper.__name__ = func.__name__ + return wrapper + + + +class InheritedTestMethodsOverrideWrapperMetaClsAutoMixin(object): + """Automatically picks up the actual metaclass of the the type to be created, + that is the one inherited by one of the bases, and patch up its __new__ to use + the InheritedTestMethodsOverrideWrapperInstanceDecorator with our configured decorator""" + + #{ Configuration + # decorator function to use when wrapping the inherited methods. Put it into a list as first member + # to hide it from being created as class method + decorator = [] + #}END configuration + + @classmethod + def _find_metacls(metacls, bases): + """emulate pythons lookup""" + mcls_attr = '__metaclass__' + for base in bases: + if hasattr(base, mcls_attr): + return getattr(base, mcls_attr) + return metacls._find_metacls(base.__bases__) + #END for each base + raise AssertionError("base class had not metaclass attached") + + def __new__(metacls, name, bases, clsdict): + assert metacls.decorator, "'decorator' member needs to be set in subclass" + base_metacls = metacls._find_metacls(bases) + return InheritedTestMethodsOverrideWrapperInstanceDecorator(metacls.decorator[0])(base_metacls.__new__)(base_metacls, name, bases, clsdict) #} END meta classes -- cgit v1.2.1 From d5038ebadc190753c67c02c9f5930a14ca2dc1e7 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Jul 2011 12:57:26 +0200 Subject: removed now superfluous InstanceDecorator, as it was just complicating things after all --- git/test/lib/helper.py | 58 +++++++++++++++----------------------------------- 1 file changed, 17 insertions(+), 41 deletions(-) (limited to 'git') diff --git a/git/test/lib/helper.py b/git/test/lib/helper.py index 87600489..2045f9d3 100644 --- a/git/test/lib/helper.py +++ b/git/test/lib/helper.py @@ -22,8 +22,7 @@ from base import ( __all__ = ( - 'StringProcessAdapter', 'GlobalsItemDeletorMetaCls', 'InheritedTestMethodsOverrideWrapperInstanceDecorator', - 'InheritedTestMethodsOverrideWrapperMetaClsAutoMixin', + 'StringProcessAdapter', 'GlobalsItemDeletorMetaCls', 'InheritedTestMethodsOverrideWrapperMetaClsAutoMixin', 'with_rw_repo', 'with_rw_and_rw_remote_repo', 'TestBase', 'TestCase', 'needs_module_or_skip' ) @@ -239,44 +238,7 @@ class GlobalsItemDeletorMetaCls(type): #END skip case that people import our base without actually using it #END handle deletion return new_type - - -class InheritedTestMethodsOverrideWrapperInstanceDecorator(object): - """Utility to wrap all inherited methods into a given decorator and set up new - overridden methods on our actual type. This allows to adjust tests which are inherited - by our parent type, automatically. The decorator set in a derived type should - do what it has to do, possibly skipping the test if some prerequesites are not met. - - To use it, instatiate it and use it as a wrapper for the __new__ function of your metacls, as in - - __new__ = @InheritedTestMethodsOverrideWrapperInstanceDecorator(mydecorator)(MyMetaclsBase.__new__)""" - - def __init__(self, decorator): - self.decorator = decorator - - def _patch_methods_recursive(self, bases, clsdict): - """depth-first patching of methods""" - for base in bases: - self._patch_methods_recursive(base.__bases__, clsdict) - for name, item in base.__dict__.iteritems(): - if not name.startswith('test_'): - continue - #END skip non-tests - clsdict[name] = self.decorator(item) - #END for each item - #END for each base - - def __call__(self, func): - def wrapper(metacls, name, bases, clsdict): - self._patch_methods_recursive(bases, clsdict) - return func(metacls, name, bases, clsdict) - #END wrapper - assert func.__name__ == '__new__', "Can only wrap __new__ function of metaclasses" - wrapper.__name__ = func.__name__ - return wrapper - - class InheritedTestMethodsOverrideWrapperMetaClsAutoMixin(object): """Automatically picks up the actual metaclass of the the type to be created, @@ -299,11 +261,25 @@ class InheritedTestMethodsOverrideWrapperMetaClsAutoMixin(object): return metacls._find_metacls(base.__bases__) #END for each base raise AssertionError("base class had not metaclass attached") - + + @classmethod + def _patch_methods_recursive(metacls, bases, clsdict): + """depth-first patching of methods""" + for base in bases: + metacls._patch_methods_recursive(base.__bases__, clsdict) + for name, item in base.__dict__.iteritems(): + if not name.startswith('test_'): + continue + #END skip non-tests + clsdict[name] = metacls.decorator[0](item) + #END for each item + #END for each base + def __new__(metacls, name, bases, clsdict): assert metacls.decorator, "'decorator' member needs to be set in subclass" base_metacls = metacls._find_metacls(bases) - return InheritedTestMethodsOverrideWrapperInstanceDecorator(metacls.decorator[0])(base_metacls.__new__)(base_metacls, name, bases, clsdict) + metacls._patch_methods_recursive(bases, clsdict) + return base_metacls.__new__(base_metacls, name, bases, clsdict) #} END meta classes -- cgit v1.2.1 From 4bb5107cff6f205f5c6e73a6f8bd22fc56f48cf4 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Jul 2011 14:53:37 +0200 Subject: Initial version of the DulwichType inheritance. For now, it inherits everything from the existing implementation, but one by one things can be reimplmented to use dulwich. It also shows that py 2.6 is quite plagued from its new feature, which is actually a bug, as objects inability to accept any args makes mixins hard to use ... --- git/db/compat.py | 24 ++++++++++++---- git/db/dulwich/base.py | 6 ---- git/db/dulwich/complex.py | 59 ++++++++++++++++++++++++++++++++++++++++ git/db/interface.py | 12 ++------ git/db/py/base.py | 19 +++++++++---- git/db/py/complex.py | 16 ++--------- git/db/py/resolve.py | 4 --- git/test/db/base.py | 6 ++++ git/test/db/dulwich/lib.py | 7 ++++- git/test/db/dulwich/test_base.py | 22 +++++++++++---- git/test/test_remote.py | 8 ++++++ 11 files changed, 133 insertions(+), 50 deletions(-) delete mode 100644 git/db/dulwich/base.py create mode 100644 git/db/dulwich/complex.py (limited to 'git') diff --git a/git/db/compat.py b/git/db/compat.py index 767ab5e0..771a1e77 100644 --- a/git/db/compat.py +++ b/git/db/compat.py @@ -4,14 +4,10 @@ # the New BSD License: http://www.opensource.org/licenses/bsd-license.php """Module providing adaptors to maintain backwards compatability""" -class RepoCompatibilityInterface(object): +class RepoCompatibilityInterfaceNoBare(object): """Interface to install backwards compatability of the new complex repository types with the previous, all in one, repository.""" - @property - def bare(self): - return self.is_bare - def rev_parse(self, *args, **kwargs): return self.resolve_object(*args, **kwargs) @@ -28,4 +24,22 @@ class RepoCompatibilityInterface(object): return self.head.reference def __repr__(self): + """Return the representation of the repository, the way it used to be""" return '' % self.git_dir + + @property + def branches(self): + return self.heads + + +class RepoCompatibilityInterface(RepoCompatibilityInterfaceNoBare): + """Interface to install backwards compatability of the new complex repository + types with the previous, all in one, repository.""" + + @property + def bare(self): + return self.is_bare + + @property + def refs(self): + return self.references diff --git a/git/db/dulwich/base.py b/git/db/dulwich/base.py deleted file mode 100644 index cd1d71c8..00000000 --- a/git/db/dulwich/base.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Module with some basic database implementations""" - - -__all__ = [] - - diff --git a/git/db/dulwich/complex.py b/git/db/dulwich/complex.py new file mode 100644 index 00000000..6c3645a4 --- /dev/null +++ b/git/db/dulwich/complex.py @@ -0,0 +1,59 @@ + +__all__ = ['DulwichGitODB', 'DulwichGitDB', 'DulwichCompatibilityGitDB'] + +from git.db.py.complex import PureGitODB +from git.db.py.base import ( + PureRepositoryPathsMixin, + PureConfigurationMixin, + PureIndexDB, + ) +from git.db.py.resolve import PureReferencesMixin +from git.db.py.transport import PureTransportDB +from git.db.py.submodule import PureSubmoduleDB + +from git.db.cmd.complex import CmdHighLevelRepository, GitCommandMixin +from git.db.compat import RepoCompatibilityInterfaceNoBare + +#from git.db.interface import ObjectDBW, ObjectDBR +from dulwich.repo import Repo as DulwichRepo + +import os + + +class DulwichGitODB(PureGitODB): + """A full fledged database to read and write object files from all kinds of sources.""" + + def __init__(self, objects_root): + """Initalize this instance""" + PureGitODB.__init__(self, objects_root) + self._dw_repo = DulwichRepo(self.working_dir) + + def __getattr__(self, attr): + try: + # supply LazyMixin with this call first + return super(DulwichGitODB, self).__getattr__(attr) + except AttributeError: + # now assume its on the dulwich repository ... for now + return getattr(self._dw_repo, attr) + #END handle attr + + +class DulwichGitDB( PureRepositoryPathsMixin, PureConfigurationMixin, + PureReferencesMixin, PureSubmoduleDB, + PureIndexDB, + PureTransportDB, # not fully implemented + GitCommandMixin, + CmdHighLevelRepository, + DulwichGitODB): # must come last, as it doesn't pass on __init__ with super + + + def __init__(self, root_path): + """Initialize ourselves on the .git directory, or the .git/objects directory.""" + PureRepositoryPathsMixin._initialize(self, root_path) + super(DulwichGitDB, self).__init__(self.objects_dir) + + +class DulwichCompatibilityGitDB(RepoCompatibilityInterfaceNoBare, DulwichGitDB): + """Basic dulwich compatibility database""" + pass + diff --git a/git/db/interface.py b/git/db/interface.py index 803f7769..9ad74cc1 100644 --- a/git/db/interface.py +++ b/git/db/interface.py @@ -561,16 +561,8 @@ class ReferencesMixin(object): raise NotImplementedError() #}END edit methods - - #{ Backward Compatability - # These aliases need to be provided by the implementing interface as well - refs = references - branches = heads - #} END backward compatability - - - - + + class RepositoryPathsMixin(object): """Represents basic functionality of a full git repository. This involves an optional working tree, a git directory with references and an object directory. diff --git a/git/db/py/base.py b/git/db/py/base.py index 2c21c136..fb6e2f4a 100644 --- a/git/db/py/base.py +++ b/git/db/py/base.py @@ -104,7 +104,6 @@ class PureRootPathDB(RootPathDB): super(PureRootPathDB, self).__init__(root_path) - #{ Interface def root_path(self): return self._root_path @@ -233,7 +232,7 @@ class PureCompoundDB(CompoundDB, PureObjectDBR, LazyMixin, CachingDB): class PureRepositoryPathsMixin(RepositoryPathsMixin): # slots has no effect here, its just to keep track of used attrs - __slots__ = ("_git_path", '_bare') + __slots__ = ("_git_path", '_bare', '_working_tree_dir') #{ Configuration repo_dir = '.git' @@ -272,14 +271,16 @@ class PureRepositoryPathsMixin(RepositoryPathsMixin): raise InvalidGitRepositoryError(epath) # END path not found - self._bare = self._git_path.endswith(self.repo_dir) + self._bare = self._working_tree_dir is None if hasattr(self, 'config_reader'): try: self._bare = self.config_reader("repository").getboolean('core','bare') except Exception: # lets not assume the option exists, although it should pass + #END handle exception #END check bare flag + self._working_tree_dir = self._bare and None or self._working_tree_dir #} end subclass interface @@ -313,7 +314,7 @@ class PureRepositoryPathsMixin(RepositoryPathsMixin): @property def working_tree_dir(self): - if self.is_bare: + if self._working_tree_dir is None: raise AssertionError("Repository at %s is bare and does not have a working tree directory" % self.git_dir) #END assertion return dirname(self.git_dir) @@ -354,6 +355,10 @@ class PureConfigurationMixin(ConfigurationMixin): repo_config_file_name = "config" #} END + def __new__(cls, *args, **kwargs): + """This is just a stupid workaround for the evil py2.6 change which makes mixins quite impossible""" + return super(PureConfigurationMixin, cls).__new__(cls, *args, **kwargs) + def __init__(self, *args, **kwargs): """Verify prereqs""" try: @@ -421,7 +426,11 @@ class PureAlternatesFileMixin(object): #} END configuration def __init__(self, *args, **kwargs): - super(PureAlternatesFileMixin, self).__init__(*args, **kwargs) + try: + super(PureAlternatesFileMixin, self).__init__(*args, **kwargs) + except TypeError: + pass + #END handle py2.6 code breaking changes self._alternates_path() # throws on incompatible type #{ Interface diff --git a/git/db/py/complex.py b/git/db/py/complex.py index d5c185f3..5f4e81e0 100644 --- a/git/db/py/complex.py +++ b/git/db/py/complex.py @@ -22,17 +22,7 @@ from submodule import PureSubmoduleDB from git.db.compat import RepoCompatibilityInterface -from git.util import ( - LazyMixin, - normpath, - join, - dirname - ) -from git.exc import ( - InvalidDBRoot, - BadObject, - AmbiguousObjectName - ) +from git.exc import InvalidDBRoot import os __all__ = ('PureGitODB', 'PurePartialGitDB', 'PureCompatibilityGitDB') @@ -106,7 +96,8 @@ class PureGitODB(PureRootPathDB, PureObjectDBW, PureCompoundDB, PureAlternatesFi class PurePartialGitDB(PureGitODB, PureRepositoryPathsMixin, PureConfigurationMixin, PureReferencesMixin, PureSubmoduleDB, - PureIndexDB, PureTransportDB + PureIndexDB, + PureTransportDB # not fully implemented # HighLevelRepository Currently not implemented ! ): """Git like database with support for object lookup as well as reference resolution. @@ -122,7 +113,6 @@ class PurePartialGitDB(PureGitODB, super(PurePartialGitDB, self).__init__(self.objects_dir) - class PureCompatibilityGitDB(PurePartialGitDB, RepoCompatibilityInterface): """Pure git database with a compatability layer required by 0.3x code""" diff --git a/git/db/py/resolve.py b/git/db/py/resolve.py index 7bea779e..9a31fbd8 100644 --- a/git/db/py/resolve.py +++ b/git/db/py/resolve.py @@ -361,7 +361,3 @@ class PureReferencesMixin(ReferencesMixin): def delete_tag(self, *tags): return self.TagReferenceCls.delete(self, *tags) - - # compat - branches = heads - refs = references diff --git a/git/test/db/base.py b/git/test/db/base.py index 5291ba03..7016afb7 100644 --- a/git/test/db/base.py +++ b/git/test/db/base.py @@ -613,8 +613,14 @@ class RepoBase(TestDBBase): def test_submodule_update(self, rwrepo): # fails in bare mode rwrepo._bare = True + # special handling: there are repo implementations which have a bare attribute. IN that case, set it directly + if not rwrepo.bare: + rwrepo.bare = True self.failUnlessRaises(InvalidGitRepositoryError, rwrepo.submodule_update) rwrepo._bare = False + if rwrepo.bare: + rwrepo.bare = False + #END special repo handling # test create submodule sm = rwrepo.submodules[0] diff --git a/git/test/db/dulwich/lib.py b/git/test/db/dulwich/lib.py index a1110ffa..56734064 100644 --- a/git/test/db/dulwich/lib.py +++ b/git/test/db/dulwich/lib.py @@ -1,6 +1,11 @@ """dulwich specific utilities, as well as all the default ones""" -from git.test.lib import * +from git.test.lib import ( + InheritedTestMethodsOverrideWrapperMetaClsAutoMixin, + needs_module_or_skip + ) + +__all__ = ['needs_dulwich_or_skip', 'DulwichRequiredMetaMixin'] #{ Decoorators diff --git a/git/test/db/dulwich/test_base.py b/git/test/db/dulwich/test_base.py index 9bc9c394..50e64131 100644 --- a/git/test/db/dulwich/test_base.py +++ b/git/test/db/dulwich/test_base.py @@ -3,21 +3,31 @@ # This module is part of GitDB and is released under # the New BSD License: http://www.opensource.org/licenses/bsd-license.php from lib import * +from git.test.lib import TestBase, with_rw_repo from git.test.db.base import RepoBase -from git.db.complex import PureCompatibilityGitDB + + try: import dulwich except ImportError: # om this case, all other dulwich tests will be skipped - pass + # Need to properly initialize the class though, otherwise it would fail + from git.db.complex import PureCompatibilityGitDB as DulwichDB +else: + # now we know dulwich is available, to do futher imports + from git.db.dulwich.complex import DulwichCompatibilityGitDB as DulwichDB + +#END handle imports class TestPyDBBase(RepoBase): __metaclass__ = DulwichRequiredMetaMixin - RepoCls = PureCompatibilityGitDB + RepoCls = DulwichDB @needs_dulwich_or_skip - def test_basics(self): - import dulwich - pass + @with_rw_repo('HEAD', bare=False) + def test_basics(self, rw_repo): + db = DulwichDB(rw_repo.working_tree_dir) + print db.git_dir + diff --git a/git/test/test_remote.py b/git/test/test_remote.py index cef8687b..30bd1232 100644 --- a/git/test/test_remote.py +++ b/git/test/test_remote.py @@ -21,6 +21,8 @@ from git.refs import ( SymbolicReference ) +from nose import SkipTest + import tempfile import shutil import os @@ -352,7 +354,13 @@ class TestRemote(TestBase): # the same repository TagReference.delete(rw_repo, new_tag, other_tag) remote.push(":%s" % other_tag.path) + + def test_todo(self): + # If you see this, plesase remind yourself, that all this needs to be run + # per repository type ! + raise SkipTest("todo") + @with_rw_and_rw_remote_repo('0.1.6') def test_base(self, rw_repo, remote_repo): num_remotes = 0 -- cgit v1.2.1 From 8b3b222565199eab67196a5ab840b9e2770bfc53 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Jul 2011 19:34:25 +0200 Subject: Added default performance tests - these should help to measure something at least, which implicitly includes pack handling. For the pack specific tests to work, one would need a pack interface though, which is currently not planned to be specifically exposed --- git/db/dulwich/complex.py | 7 ++++++- git/test/performance/db/test_looseodb_dulwich.py | 6 ++++++ git/test/performance/db/test_odb_dulwich.py | 6 ++++++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 git/test/performance/db/test_looseodb_dulwich.py create mode 100644 git/test/performance/db/test_odb_dulwich.py (limited to 'git') diff --git a/git/db/dulwich/complex.py b/git/db/dulwich/complex.py index 6c3645a4..3fa7c1cd 100644 --- a/git/db/dulwich/complex.py +++ b/git/db/dulwich/complex.py @@ -26,7 +26,12 @@ class DulwichGitODB(PureGitODB): def __init__(self, objects_root): """Initalize this instance""" PureGitODB.__init__(self, objects_root) - self._dw_repo = DulwichRepo(self.working_dir) + if hasattr(self, 'working_dir'): + wd = self.working_dir + else: + wd = os.path.dirname(os.path.dirname(objects_root)) + #END try to figure out good entry for dulwich, which doesn't do an extensive search + self._dw_repo = DulwichRepo(wd) def __getattr__(self, attr): try: diff --git a/git/test/performance/db/test_looseodb_dulwich.py b/git/test/performance/db/test_looseodb_dulwich.py new file mode 100644 index 00000000..cf27a528 --- /dev/null +++ b/git/test/performance/db/test_looseodb_dulwich.py @@ -0,0 +1,6 @@ +from git.db.dulwich.complex import DulwichGitODB +from looseodb_impl import TestLooseDBWPerformanceBase + +class TestPureLooseDB(TestLooseDBWPerformanceBase): + LooseODBCls = DulwichGitODB + diff --git a/git/test/performance/db/test_odb_dulwich.py b/git/test/performance/db/test_odb_dulwich.py new file mode 100644 index 00000000..069c5b43 --- /dev/null +++ b/git/test/performance/db/test_odb_dulwich.py @@ -0,0 +1,6 @@ +from git.db.dulwich.complex import DulwichCompatibilityGitDB +from odb_impl import TestObjDBPerformanceBase + +class TestPureDB(TestObjDBPerformanceBase): + RepoCls = DulwichCompatibilityGitDB + -- cgit v1.2.1 From b56764b2dbe8845d476e41c8659fc0543ffb3433 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Jul 2011 21:01:12 +0200 Subject: Fixed submodule related issues once again - is it ever going to end ? --- git/test/db/base.py | 8 ++++---- git/test/objects/test_submodule.py | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'git') diff --git a/git/test/db/base.py b/git/test/db/base.py index 7016afb7..2f8f50e2 100644 --- a/git/test/db/base.py +++ b/git/test/db/base.py @@ -601,12 +601,12 @@ class RepoBase(TestDBBase): self.failUnlessRaises(NotImplementedError, rev_parse, "@{1 week ago}") def test_submodules(self): - assert len(self.rorepo.submodules) == 1 # non-recursive + assert len(self.rorepo.submodules) == 2 # non-recursive # in previous configurations, we had recursive repositories so this would compare to 2 - # now there is only one left, as gitdb was merged - assert len(list(self.rorepo.iter_submodules())) == 1 + # now there is only one left, as gitdb was merged, but we have smmap instead + assert len(list(self.rorepo.iter_submodules())) == 2 - assert isinstance(self.rorepo.submodule("git/ext/async"), Submodule) + assert isinstance(self.rorepo.submodule("async"), Submodule) self.failUnlessRaises(ValueError, self.rorepo.submodule, "doesn't exist") @with_rw_repo('HEAD', bare=False) diff --git a/git/test/objects/test_submodule.py b/git/test/objects/test_submodule.py index e5adedbc..2b7c7f40 100644 --- a/git/test/objects/test_submodule.py +++ b/git/test/objects/test_submodule.py @@ -284,7 +284,9 @@ class TestSubmodule(TestObjectBase): # make sure sub-submodule is not modified by forcing it to update # to the revision it is supposed to point to. - csm.update() + for subitem in sm.traverse(): + subitem.update() + #END checkout to right commit # this would work assert sm.remove(dry_run=True) is sm -- cgit v1.2.1 From 09064504e52a5ec8bfc4825a3176239b731380d2 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Jul 2011 21:28:08 +0200 Subject: Added trivial implementation for info and stream methods - info is very inefficient, but can't help it. Basic repo tests don't work as dulwich ignores alternate files --- git/db/dulwich/complex.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'git') diff --git a/git/db/dulwich/complex.py b/git/db/dulwich/complex.py index 3fa7c1cd..e1dad01d 100644 --- a/git/db/dulwich/complex.py +++ b/git/db/dulwich/complex.py @@ -17,6 +17,10 @@ from git.db.compat import RepoCompatibilityInterfaceNoBare #from git.db.interface import ObjectDBW, ObjectDBR from dulwich.repo import Repo as DulwichRepo +from git.base import OInfo, OStream +from git.fun import type_id_to_type_map + +from cStringIO import StringIO import os @@ -42,6 +46,18 @@ class DulwichGitODB(PureGitODB): return getattr(self._dw_repo, attr) #END handle attr + #{ Object DBR + + def info(self, binsha): + type_id, uncomp_data = self._dw_repo.object_store.get_raw(binsha) + return OInfo(binsha, type_id_to_type_map[type_id], len(uncomp_data)) + + def stream(self, binsha): + type_id, uncomp_data = self._dw_repo.object_store.get_raw(binsha) + return OStream(binsha, type_id_to_type_map[type_id], len(uncomp_data), StringIO(uncomp_data)) + + #}END object dbr + class DulwichGitDB( PureRepositoryPathsMixin, PureConfigurationMixin, PureReferencesMixin, PureSubmoduleDB, -- cgit v1.2.1 From 6a10c8a82adcbe1f18c2e79a9135c7f5c753b826 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Jul 2011 21:47:02 +0200 Subject: Removed cache in PureCompoundDB as it had the tendency to slow things down actually --- git/db/py/base.py | 46 +++++++++++++++++----------------------------- 1 file changed, 17 insertions(+), 29 deletions(-) (limited to 'git') diff --git a/git/db/py/base.py b/git/db/py/base.py index fb6e2f4a..49f28a8d 100644 --- a/git/db/py/base.py +++ b/git/db/py/base.py @@ -131,44 +131,33 @@ class PureCompoundDB(CompoundDB, PureObjectDBR, LazyMixin, CachingDB): def _set_cache_(self, attr): if attr == '_dbs': self._dbs = list() - elif attr == '_obj_cache': - self._obj_cache = dict() else: super(PureCompoundDB, self)._set_cache_(attr) - def _db_query(self, sha): - """:return: database containing the given 20 byte sha - :raise BadObject:""" - # most databases use binary representations, prevent converting - # it everytime a database is being queried - try: - return self._obj_cache[sha] - except KeyError: - pass - # END first level cache - - for db in self._dbs: - if db.has_object(sha): - self._obj_cache[sha] = db - return db - # END for each database - raise BadObject(sha) - #{ PureObjectDBR interface def has_object(self, sha): - try: - self._db_query(sha) - return True - except BadObject: - return False - # END handle exceptions + for db in self._dbs: + if db.has_object(sha): + return True + #END for each db + return False def info(self, sha): - return self._db_query(sha).info(sha) + for db in self._dbs: + try: + return db.info(sha) + except BadObject: + pass + #END for each db def stream(self, sha): - return self._db_query(sha).stream(sha) + for db in self._dbs: + try: + return db.stream(sha) + except BadObject: + pass + #END for each db def size(self): return reduce(lambda x,y: x+y, (db.size() for db in self._dbs), 0) @@ -185,7 +174,6 @@ class PureCompoundDB(CompoundDB, PureObjectDBR, LazyMixin, CachingDB): def update_cache(self, force=False): # something might have changed, clear everything - self._obj_cache.clear() stat = False for db in self._dbs: if isinstance(db, CachingDB): -- cgit v1.2.1 From f4f330f8588dacd43af6513e1e1e1a50237da1e7 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Jul 2011 23:37:04 +0200 Subject: Added store support. Now the basic object IO is implemented, which shall be enough for the first batch of work --- git/db/dulwich/complex.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'git') diff --git a/git/db/dulwich/complex.py b/git/db/dulwich/complex.py index e1dad01d..ad5b97a4 100644 --- a/git/db/dulwich/complex.py +++ b/git/db/dulwich/complex.py @@ -16,9 +16,10 @@ from git.db.compat import RepoCompatibilityInterfaceNoBare #from git.db.interface import ObjectDBW, ObjectDBR from dulwich.repo import Repo as DulwichRepo +from dulwich.objects import ShaFile from git.base import OInfo, OStream -from git.fun import type_id_to_type_map +from git.fun import type_id_to_type_map, type_to_type_id_map from cStringIO import StringIO import os @@ -57,7 +58,16 @@ class DulwichGitODB(PureGitODB): return OStream(binsha, type_id_to_type_map[type_id], len(uncomp_data), StringIO(uncomp_data)) #}END object dbr + + #{ Object DBW + + def store(self, istream): + obj = ShaFile.from_raw_string(type_to_type_id_map[istream.type], istream.read()) + self._dw_repo.object_store.add_object(obj) + istream.binsha = obj.sha().digest() + return istream + #}END object dbw class DulwichGitDB( PureRepositoryPathsMixin, PureConfigurationMixin, PureReferencesMixin, PureSubmoduleDB, -- cgit v1.2.1