| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent a1d1d2c commit a1e2f63
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -15,7 +15,7 @@ | |||
| 15 | 15 | from git.odict import OrderedDict | |
| 16 | 16 | from git.util import LockFile | |
| 17 | 17 | ||
| 18 | - __all__ = ('GitConfigParser', ) | ||
| 18 | + __all__ = ('GitConfigParser', 'SectionConstraint') | ||
| 19 | 19 | ||
| 20 | 20 | class MetaParserBuilder(type): | |
| 21 | 21 | """Utlity class wrapping base-class methods into decorators that assure read-only properties""" | |
@@ -63,7 +63,29 @@ def flush_changes(self, *args, **kwargs): | |||
| 63 | 63 | flush_changes.__name__ = non_const_func.__name__ | |
| 64 | 64 | return flush_changes | |
| 65 | 65 | ||
| 66 | + | ||
| 67 | + class SectionConstraint(object): | ||
| 68 | + """Constrains a ConfigParser to only option commands which are constrained to | ||
| 69 | + always use the section we have been initialized with. | ||
| 70 | + | ||
| 71 | + It supports all ConfigParser methods that operate on an option""" | ||
| 72 | + __slots__ = ("_config", "_section_name") | ||
| 73 | + _valid_attrs_ = ("get_value", "set_value", "get", "set", "getint", "getfloat", "getboolean", "has_option") | ||
| 66 | 74 | ||
| 75 | + def __init__(self, config, section): | ||
| 76 | + self._config = config | ||
| 77 | + self._section_name = section | ||
| 78 | + | ||
| 79 | + def __getattr__(self, attr): | ||
| 80 | + if attr in self._valid_attrs_: | ||
| 81 | + return lambda *args, **kwargs: self._call_config(attr, *args, **kwargs) | ||
| 82 | + return super(SectionConstraint,self).__getattribute__(attr) | ||
| 83 | + | ||
| 84 | + def _call_config(self, method, *args, **kwargs): | ||
| 85 | + """Call the configuration at the given method which must take a section name | ||
| 86 | + as first argument""" | ||
| 87 | + return getattr(self._config, method)(self._section_name, *args, **kwargs) | ||
| 88 | + | ||
| 67 | 89 | ||
| 68 | 90 | class GitConfigParser(cp.RawConfigParser, object): | |
| 69 | 91 | """Implements specifics required to read git style configuration files. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,8 @@ | |||
| 1 | 1 | import base | |
| 2 | + from cStringIO import StringIO | ||
| 3 | + from git.config import GitConfigParser | ||
| 4 | + from git.util import join_path_native | ||
| 5 | + from git.exc import InvalidGitRepositoryError, NoSuchPathError | ||
| 2 | 6 | ||
| 3 | 7 | __all__ = ("Submodule", ) | |
| 4 | 8 | ||
@@ -7,10 +11,115 @@ class Submodule(base.IndexObject): | |||
| 7 | 11 | represents a commit in the submodule's repository which is to be checked out | |
| 8 | 12 | at the path of this instance. | |
| 9 | 13 | The submodule type does not have a string type associated with it, as it exists | |
| 10 | - solely as a marker in the tree and index""" | ||
| 14 | + solely as a marker in the tree and index. | ||
| 15 | + | ||
| 16 | + All methods work in bare and non-bare repositories.""" | ||
| 17 | + | ||
| 18 | + kModulesFile = '.gitmodules' | ||
| 11 | 19 | ||
| 12 | 20 | # this is a bogus type for base class compatability | |
| 13 | 21 | type = 'submodule' | |
| 14 | 22 | ||
| 15 | - # TODO: Add functions to retrieve a repo for the submodule, to allow | ||
| 16 | - # its initiailization and handling | ||
| 23 | + __slots__ = ('_root_tree', '_url', '_ref') | ||
| 24 | + | ||
| 25 | + def _set_cache_(self, attr): | ||
| 26 | + if attr == 'size': | ||
| 27 | + raise ValueError("Submodules do not have a size as they do not refer to anything in this repository") | ||
| 28 | + elif attr == '_root_tree': | ||
| 29 | + # set a default value, which is the root tree of the current head | ||
| 30 | + self._root_tree = self.repo.tree() | ||
| 31 | + elif attr in ('path', '_url', '_ref'): | ||
| 32 | + reader = self.config_reader() | ||
| 33 | + # default submodule values | ||
| 34 | + self._path = reader.get_value('path') | ||
| 35 | + self._url = reader.get_value('url') | ||
| 36 | + # git-python extension values - optional | ||
| 37 | + self._ref = reader.get_value('ref', 'master') | ||
| 38 | + else: | ||
| 39 | + super(Submodule, self)._set_cache_(attr) | ||
| 40 | + # END handle attribute name | ||
| 41 | + | ||
| 42 | + def _fp_config(self): | ||
| 43 | + """:return: Configuration file as StringIO - we only access it through the respective blob's data""" | ||
| 44 | + return StringIO(self._root_tree[self.kModulesFile].datastream.read()) | ||
| 45 | + | ||
| 46 | + def _config_parser(self, read_only): | ||
| 47 | + """:return: Config Parser constrained to our submodule in read or write mode""" | ||
| 48 | + parser = GitConfigParser(self._fp_config(), read_only = read_only) | ||
| 49 | + return SectionConstraint(parser, 'submodule "%s"' % self.path) | ||
| 50 | + | ||
| 51 | + #{ Edit Interface | ||
| 52 | + | ||
| 53 | + @classmethod | ||
| 54 | + def add(cls, repo, path, url, skip_init=False): | ||
| 55 | + """Add a new submodule to the given repository. This will alter the index | ||
| 56 | + as well as the .gitmodules file, but will not create a new commit. | ||
| 57 | + :param repo: Repository instance which should receive the submodule | ||
| 58 | + :param path: repository-relative path at which the submodule should be located | ||
| 59 | + It will be created as required during the repository initialization. | ||
| 60 | + :param url: git-clone compatible URL, see git-clone reference for more information | ||
| 61 | + :param skip_init: if True, the new repository will not be cloned to its location. | ||
| 62 | + :return: The newly created submodule instance""" | ||
| 63 | + | ||
| 64 | + def set_root_tree(self, root_tree): | ||
| 65 | + """Set this instance to use the given tree which is supposed to contain the | ||
| 66 | + .gitmodules blob. | ||
| 67 | + :param root_tree: Tree'ish reference pointing at the root_tree | ||
| 68 | + :raise ValueError: if the root_tree didn't contain the .gitmodules blob.""" | ||
| 69 | + tree = self.repo.tree(root_tree) | ||
| 70 | + if self.kModulesFile not in tree: | ||
| 71 | + raise ValueError("Tree %s did not contain the %s file" % (root_tree, self.kModulesFile)) | ||
| 72 | + # END handle exceptions | ||
| 73 | + self._root_tree = tree | ||
| 74 | + | ||
| 75 | + # clear the possibly changing values | ||
| 76 | + del(self.path) | ||
| 77 | + del(self._ref) | ||
| 78 | + del(self._url) | ||
| 79 | + | ||
| 80 | + def config_writer(self): | ||
| 81 | + """:return: a config writer instance allowing you to read and write the data | ||
| 82 | + belonging to this submodule into the .gitmodules file.""" | ||
| 83 | + return self._config_parser(read_only=False) | ||
| 84 | + | ||
| 85 | + #} END edit interface | ||
| 86 | + | ||
| 87 | + #{ Query Interface | ||
| 88 | + | ||
| 89 | + def module(self): | ||
| 90 | + """:return: Repo instance initialized from the repository at our submodule path | ||
| 91 | + :raise InvalidGitRepositoryError: if a repository was not available""" | ||
| 92 | + if self.repo.bare: | ||
| 93 | + raise InvalidGitRepositoryError("Cannot retrieve module repository in bare parent repositories") | ||
| 94 | + # END handle bare mode | ||
| 95 | + | ||
| 96 | + repo_path = join_path_native(self.repo.working_tree_dir, self.path) | ||
| 97 | + try: | ||
| 98 | + return Repo(repo_path) | ||
| 99 | + except (InvalidGitRepositoryError, NoSuchPathError): | ||
| 100 | + raise InvalidGitRepositoryError("No valid repository at %s" % self.path) | ||
| 101 | + # END handle exceptions | ||
| 102 | + | ||
| 103 | + def ref(self): | ||
| 104 | + """:return: The reference's name that we are to checkout""" | ||
| 105 | + return self._ref | ||
| 106 | + | ||
| 107 | + def url(self): | ||
| 108 | + """:return: The url to the repository which our module-repository refers to""" | ||
| 109 | + return self._url | ||
| 110 | + | ||
| 111 | + def root_tree(self): | ||
| 112 | + """:return: Tree instance referring to the tree which contains the .gitmodules file | ||
| 113 | + we are to use | ||
| 114 | + :note: will always point to the current head's root tree if it was not set explicitly""" | ||
| 115 | + return self._root_tree | ||
| 116 | + | ||
| 117 | + def config_reader(self): | ||
| 118 | + """:return: ConfigReader instance which allows you to qurey the configuration values | ||
| 119 | + of this submodule, as provided by the .gitmodules file | ||
| 120 | + :note: The config reader will actually read the data directly from the repository | ||
| 121 | + and thus does not need nor care about your working tree. | ||
| 122 | + :note: Should be cached by the caller and only kept as long as needed""" | ||
| 123 | + return self._config_parser.read_only(read_only=True) | ||
| 124 | + | ||
| 125 | + #} END query interface | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,7 +7,8 @@ | |||
| 7 | 7 | ||
| 8 | 8 | from exc import GitCommandError | |
| 9 | 9 | from objects import Commit | |
| 10 | - from ConfigParser import NoOptionError | ||
| 10 | + from ConfigParser import NoOptionError | ||
| 11 | + from config import SectionConstraint | ||
| 11 | 12 | ||
| 12 | 13 | from git.util import ( | |
| 13 | 14 | LazyMixin, | |
@@ -30,29 +31,6 @@ | |||
| 30 | 31 | ||
| 31 | 32 | __all__ = ('RemoteProgress', 'PushInfo', 'FetchInfo', 'Remote') | |
| 32 | 33 | ||
| 33 | - class _SectionConstraint(object): | ||
| 34 | - """Constrains a ConfigParser to only option commands which are constrained to | ||
| 35 | - always use the section we have been initialized with. | ||
| 36 | - | ||
| 37 | - It supports all ConfigParser methods that operate on an option""" | ||
| 38 | - __slots__ = ("_config", "_section_name") | ||
| 39 | - _valid_attrs_ = ("get_value", "set_value", "get", "set", "getint", "getfloat", "getboolean", "has_option") | ||
| 40 | - | ||
| 41 | - def __init__(self, config, section): | ||
| 42 | - self._config = config | ||
| 43 | - self._section_name = section | ||
| 44 | - | ||
| 45 | - def __getattr__(self, attr): | ||
| 46 | - if attr in self._valid_attrs_: | ||
| 47 | - return lambda *args, **kwargs: self._call_config(attr, *args, **kwargs) | ||
| 48 | - return super(_SectionConstraint,self).__getattribute__(attr) | ||
| 49 | - | ||
| 50 | - def _call_config(self, method, *args, **kwargs): | ||
| 51 | - """Call the configuration at the given method which must take a section name | ||
| 52 | - as first argument""" | ||
| 53 | - return getattr(self._config, method)(self._section_name, *args, **kwargs) | ||
| 54 | - | ||
| 55 | - | ||
| 56 | 34 | class RemoteProgress(object): | |
| 57 | 35 | """ | |
| 58 | 36 | Handler providing an interface to parse progress information emitted by git-push | |
@@ -449,7 +427,7 @@ def _config_section_name(self): | |||
| 449 | 427 | ||
| 450 | 428 | def _set_cache_(self, attr): | |
| 451 | 429 | if attr == "_config_reader": | |
| 452 | - self._config_reader = _SectionConstraint(self.repo.config_reader(), self._config_section_name()) | ||
| 430 | + self._config_reader = SectionConstraint(self.repo.config_reader(), self._config_section_name()) | ||
| 453 | 431 | else: | |
| 454 | 432 | super(Remote, self)._set_cache_(attr) | |
| 455 | 433 | ||
@@ -735,4 +713,4 @@ def config_writer(self): | |||
| 735 | 713 | ||
| 736 | 714 | # clear our cache to assure we re-read the possibly changed configuration | |
| 737 | 715 | del(self._config_reader) | |
| 738 | - return _SectionConstraint(writer, self._config_section_name()) | ||
| 716 | + return SectionConstraint(writer, self._config_section_name()) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,7 +6,35 @@ | |||
| 6 | 6 | ||
| 7 | 7 | class TestSubmodule(TestBase): | |
| 8 | 8 | ||
| 9 | - def test_base(self): | ||
| 10 | - # TODO | ||
| 11 | - pass | ||
| 9 | + kCOTag = '0.1.6' | ||
| 10 | + | ||
| 11 | + def _do_base_tests(self, rwrepo): | ||
| 12 | + """Perform all tests in the given repository, it may be bare or nonbare""" | ||
| 13 | + | ||
| 14 | + # uncached path/url - retrieves information from .gitmodules file | ||
| 15 | + | ||
| 16 | + # changing the root_tree yields new values when querying them (i.e. cache is cleared) | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + # size is invalid | ||
| 20 | + self.failUnlessRaises(ValueError, getattr, sm, 'size') | ||
| 21 | + | ||
| 22 | + # fails if tree has no gitmodule file | ||
| 23 | + | ||
| 24 | + if rwrepo.bare: | ||
| 25 | + # module fails | ||
| 26 | + pass | ||
| 27 | + else: | ||
| 28 | + # get the module repository | ||
| 29 | + pass | ||
| 30 | + # END bare handling | ||
| 31 | + | ||
| 32 | + @with_rw_repo(kCOTag) | ||
| 33 | + def test_base_rw(self, rwrepo): | ||
| 34 | + self._do_base_tests(rwrepo) | ||
| 35 | + | ||
| 36 | + @with_bare_rw_repo | ||
| 37 | + def test_base_bare(self, rwrepo): | ||
| 38 | + self._do_base_tests(rwrepo) | ||
| 39 | + | ||
| 12 | 40 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments