Skip to content

Commit 9513aa0

Browse files
committed
Added frame for configuration reader involving a meta class, decorators and tests - most of which still has to be filled out
1 parent 657a57a commit 9513aa0

File tree

6 files changed

+209
-4
lines changed

6 files changed

+209
-4
lines changed

lib/git/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
__version__ = 'git'
1111

12+
from git.config import GitConfigParser
1213
from git.objects import *
1314
from git.refs import *
1415
from git.actor import Actor

lib/git/config.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# config.py
2+
# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
3+
#
4+
# This module is part of GitPython and is released under
5+
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
6+
"""
7+
Module containing module parser implementation able to properly read and write
8+
configuration files
9+
"""
10+
11+
import re
12+
from ConfigParser import RawConfigParser
13+
14+
class _MetaParserBuilder(type):
15+
"""
16+
Utlity class wrapping methods into decorators that assure read-only properties
17+
"""
18+
19+
def _needs_values(func):
20+
"""Returns method assuring we read values (on demand) before we try to access them"""
21+
return func
22+
23+
def _ensure_writable(non_const_func):
24+
"""Return method that checks whether given non constant function may be called.
25+
If so, the instance will be set dirty"""
26+
27+
28+
29+
class GitConfigParser(RawConfigParser, object):
30+
"""
31+
Implements specifics required to read git style configuration files.
32+
33+
This variation behaves much like the git.config command such that the configuration
34+
will be read on demand based on the filepath given during initialization.
35+
36+
The changes will automatically be written once the instance goes out of scope, but
37+
can be triggered manually as well.
38+
39+
The configuration file will be locked if you intend to change values preventing other
40+
instances to write concurrently.
41+
"""
42+
__metaclass__ = _MetaParserBuilder
43+
44+
OPTCRE = re.compile(
45+
r'\s?(?P<option>[^:=\s][^:=]*)' # very permissive, incuding leading whitespace
46+
r'\s*(?P<vi>[:=])\s*' # any number of space/tab,
47+
# followed by separator
48+
# (either : or =), followed
49+
# by any # space/tab
50+
r'(?P<value>.*)$' # everything up to eol
51+
)
52+
53+
# list of RawConfigParser methods able to change the instance
54+
_mutating_methods_ = tuple()
55+
56+
57+
def __init__(self, file_or_files, read_only=True):
58+
"""
59+
Initialize a configuration reader to read the given file_or_files and to
60+
possibly allow changes to it by setting read_only False
61+
"""
62+
self._file_or_files = file_or_files
63+
self._read_only = read_only
64+
self._is_initialized = False
65+
self._is_dirty = False
66+
67+
def __del__(self):
68+
"""
69+
Write pending changes if required and release locks
70+
"""
71+
72+
def read(self):
73+
"""
74+
Read configuration information from our file or files
75+
"""
76+
if self._is_initialized:
77+
return
78+
79+
self._is_initialized = True
80+
81+
@_ensure_writable
82+
def write(self):
83+
"""
84+
Write our changes to our file
85+
86+
Raise
87+
AssertionError if this is a read-only writer instance
88+
"""
89+
if not self._is_dirty:
90+
return
91+
92+
self._is_dirty = False
93+
94+
@property
95+
def read_only(self):
96+
"""
97+
Returns
98+
True if this instance may change the configuration file
99+
"""
100+
return self._read_only

lib/git/repo.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
66

77
import os
8+
import sys
89
import re
910
import gzip
1011
import StringIO
@@ -15,7 +16,7 @@
1516
from actor import Actor
1617
from refs import *
1718
from objects import *
18-
19+
from config import GitConfigParser
1920

2021
class Repo(object):
2122
"""
@@ -30,6 +31,10 @@ class Repo(object):
3031
re_hexsha_only = re.compile('^[0-9A-Fa-f]{40}$')
3132
re_author_committer_start = re.compile(r'^(author|committer)')
3233
re_tab_full_line = re.compile(r'^\t(.*)$')
34+
35+
# invariants
36+
# represents the configuration level of a configuration file
37+
config_level = ("system", "global", "repository")
3338

3439
def __init__(self, path=None):
3540
"""
@@ -125,6 +130,52 @@ def tags(self):
125130
"""
126131
return Tag.list_items(self)
127132

133+
def _get_config_path(self, config_level ):
134+
# we do not support an absolute path of the gitconfig on windows ,
135+
# use the global config instead
136+
if sys.platform == "win32" and config_level == "system":
137+
config_level = "global"
138+
139+
if config_level == "system":
140+
return "/etc/gitconfig"
141+
elif config_level == "global":
142+
return os.path.expanduser("~/.gitconfig")
143+
elif config_level == "repository":
144+
return "%s/config" % self.git.git_dir
145+
146+
raise ValueError( "Invalid configuration level: %r" % config_level )
147+
148+
@property
149+
def config_reader(self):
150+
"""
151+
Returns
152+
GitConfigParser allowing to read the full git configuration, but not to write it
153+
154+
The configuration will include values from the system, user and repository
155+
configuration files.
156+
157+
NOTE: On windows, system configuration cannot currently be read as the path is
158+
unknown, instead the global path will be used.
159+
"""
160+
files = [ self._get_config_path(f) for f in self.config_level ]
161+
return GitConfigParser(files, read_only=True)
162+
163+
def config_writer(self, config_level="repository"):
164+
"""
165+
Returns
166+
GitConfigParser allowing to write values of the specified configuration file level.
167+
Config writers should be retrieved, used to change the configuration ,and written
168+
right away as they will lock the configuration file in question and prevent other's
169+
to write it.
170+
171+
``config_level``
172+
One of the following values
173+
system = sytem wide configuration file
174+
global = user level configuration file
175+
repository = configuration file for this repostory only
176+
"""
177+
return GitConfigParser(self._get_config_path(config_level), read_only = False)
178+
128179
def commit(self, rev=None):
129180
"""
130181
The Commit object for the specified revision

test/fixtures/git_config

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[core]
2+
repositoryformatversion = 0
3+
filemode = true
4+
bare = false
5+
logallrefupdates = true
6+
[remote "origin"]
7+
fetch = +refs/heads/*:refs/remotes/origin/*
8+
url = git://gitorious.org/~byron/git-python/byrons-clone.git
9+
pushurl = git@gitorious.org:~byron/git-python/byrons-clone.git
10+
[branch "master"]
11+
remote = origin
12+
merge = refs/heads/master
13+
[remote "mainline"]
14+
url = git://gitorious.org/git-python/mainline.git
15+
fetch = +refs/heads/*:refs/remotes/mainline/*
16+
[remote "MartinMarcher"]
17+
url = git://gitorious.org/~martin.marcher/git-python/serverhorror.git
18+
fetch = +refs/heads/*:refs/remotes/MartinMarcher/*
19+
[gui]
20+
geometry = 1316x820+219+243 207 192
21+
[branch "mainline_performance"]
22+
remote = mainline
23+
merge = refs/heads/master

test/git/test_config.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# test_config.py
2+
# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
3+
#
4+
# This module is part of GitPython and is released under
5+
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
6+
7+
from test.testlib import *
8+
from git import *
9+
10+
class TestBase(TestCase):
11+
12+
@classmethod
13+
def setUpAll(cls):
14+
cls.repo = Repo(GIT_REPO)
15+
16+
def test_base(self):
17+
path = fixture_path("git_config")
18+
self.fail("TODO: Base config writer testing")

test/git/test_repo.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
from test.testlib import *
99
from git import *
1010

11-
class TestRepo(object):
12-
def setup(self):
13-
self.repo = Repo(GIT_REPO)
11+
class TestRepo(TestCase):
12+
13+
@classmethod
14+
def setUpAll(cls):
15+
cls.repo = Repo(GIT_REPO)
1416

1517
@raises(InvalidGitRepositoryError)
1618
def test_new_should_raise_on_invalid_repo_location(self):
@@ -219,3 +221,13 @@ def test_untracked_files(self):
219221
# END handle files
220222

221223
assert len(self.repo.untracked_files) == (num_recently_untracked - len(files))
224+
225+
def test_config_reader(self):
226+
reader = self.repo.config_reader
227+
assert reader.read_only
228+
229+
def test_config_writer(self):
230+
for config_level in self.repo.config_level:
231+
writer = self.repo.config_writer(config_level)
232+
assert not writer.read_only
233+
# END for each config level

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy