Skip to content

Replaced exception handling logic #998

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions pygit2/__init__.py
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@
from .blame import Blame, BlameHunk
from .config import Config
from .credentials import *
from .errors import check_error, Passthrough
from .errors import GitException, GitPassthroughError, Passthrough
from .ffi import ffi, C
from .index import Index, IndexEntry
from .remote import Remote, RemoteCallbacks, get_credentials
@@ -145,8 +145,7 @@ def init_repository(path, bare=False,

# Call
crepository = ffi.new('git_repository **')
err = C.git_repository_init_ext(crepository, to_bytes(path), options)
check_error(err)
GitException.check_result(C.git_repository_init_ext)(crepository, to_bytes(path), options)

# Ok
return Repository(to_str(path))
@@ -250,17 +249,11 @@ def clone_repository(
callbacks = RemoteCallbacks()

callbacks._fill_fetch_options(opts.fetch_opts)

err = C.git_clone(crepo, to_bytes(url), to_bytes(path), opts)

GitException.check_result(C.git_clone)(crepo, to_bytes(url), to_bytes(path), opts)
# Error handling
exc = d.get('exception', callbacks._stored_exception)
if exc:
raise exc

check_error(err)

# Ok
return Repository._from_c(crepo[0], owned=True)


88 changes: 26 additions & 62 deletions pygit2/config.py
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@
from cached_property import cached_property

# Import from pygit2
from .errors import check_error
from .errors import GitException, GitIOError
from .ffi import ffi, C
from .utils import to_bytes

@@ -50,9 +50,7 @@ def __iter__(self):

def _next_entry(self):
centry = ffi.new('git_config_entry **')
err = C.git_config_next(centry, self._iter)
check_error(err)

GitException.check_result(C.git_config_next)(centry, self._iter)
return ConfigEntry._from_c(centry[0], self)

def next(self):
@@ -75,12 +73,10 @@ def __init__(self, path=None):
cconfig = ffi.new('git_config **')

if not path:
err = C.git_config_new(cconfig)
GitIOError.check_result(C.git_config_new)(cconfig)
else:
assert_string(path, "path")
err = C.git_config_open_ondisk(cconfig, to_bytes(path))

check_error(err, io=True)
GitIOError.check_result(C.git_config_open_ondisk)(cconfig, to_bytes(path))
self._config = cconfig[0]

@classmethod
@@ -101,27 +97,19 @@ def _get(self, key):
assert_string(key, "key")

entry = ffi.new('git_config_entry **')
err = C.git_config_get_entry(entry, self._config, to_bytes(key))
GitException.check_result(C.git_config_get_entry)(entry, self._config, to_bytes(key))

return err, ConfigEntry._from_c(entry[0])
return ConfigEntry._from_c(entry[0])

def _get_entry(self, key):
err, entry = self._get(key)

if err == C.GIT_ENOTFOUND:
raise KeyError(key)

check_error(err)
entry = self._get(key)
return entry

def __contains__(self, key):
err, cstr = self._get(key)

if err == C.GIT_ENOTFOUND:
try:
self._get(key)
except KeyError:
return False

check_error(err)

return True

def __getitem__(self, key):
@@ -131,29 +119,21 @@ def __getitem__(self, key):

def __setitem__(self, key, value):
assert_string(key, "key")

err = 0
if isinstance(value, bool):
err = C.git_config_set_bool(self._config, to_bytes(key), value)
GitException.check_result(C.git_config_set_bool)(self._config, to_bytes(key), value)
elif isinstance(value, int):
err = C.git_config_set_int64(self._config, to_bytes(key), value)
GitException.check_result(C.git_config_set_int64)(self._config, to_bytes(key), value)
else:
err = C.git_config_set_string(self._config, to_bytes(key),
to_bytes(value))

check_error(err)
GitException.check_result(C.git_config_set_string)(self._config, to_bytes(key),to_bytes(value))

def __delitem__(self, key):
assert_string(key, "key")
GitException.check_result(C.git_config_delete_entry)(self._config, to_bytes(key))

err = C.git_config_delete_entry(self._config, to_bytes(key))
check_error(err)

def __iter__(self):
citer = ffi.new('git_config_iterator **')
err = C.git_config_iterator_new(citer, self._config)
check_error(err)

GitException.check_result(C.git_config_iterator_new)(citer, self._config)
return ConfigIterator(self, citer[0])

def get_multivar(self, name, regex=None):
@@ -165,11 +145,7 @@ def get_multivar(self, name, regex=None):
assert_string(name, "name")

citer = ffi.new('git_config_iterator **')
err = C.git_config_multivar_iterator_new(citer, self._config,
to_bytes(name),
to_bytes(regex))
check_error(err)

GitException.check_result(C.git_config_multivar_iterator_new)(citer, self._config, to_bytes(name), to_bytes(regex))
return ConfigMultivarIterator(self, citer[0])

def set_multivar(self, name, regex, value):
@@ -180,9 +156,8 @@ def set_multivar(self, name, regex, value):
assert_string(regex, "regex")
assert_string(value, "value")

err = C.git_config_set_multivar(self._config, to_bytes(name),
to_bytes(regex), to_bytes(value))
check_error(err)
GitException.check_result(C.git_config_set_multivar)(self._config, to_bytes(name),to_bytes(regex), to_bytes(value))


def get_bool(self, key):
"""Look up *key* and parse its value as a boolean as per the git-config
@@ -191,12 +166,9 @@ def get_bool(self, key):
Truthy values are: 'true', 1, 'on' or 'yes'. Falsy values are: 'false',
0, 'off' and 'no'
"""

entry = self._get_entry(key)
res = ffi.new('int *')
err = C.git_config_parse_bool(res, entry.c_value)
check_error(err)

GitException.check_result(C.git_config_parse_bool)(res, entry.c_value)
return res[0] != 0

def get_int(self, key):
@@ -209,17 +181,14 @@ def get_int(self, key):

entry = self._get_entry(key)
res = ffi.new('int64_t *')
err = C.git_config_parse_int64(res, entry.c_value)
check_error(err)
GitException.check_result(C.git_config_parse_int64)(res, entry.c_value)

return res[0]

def add_file(self, path, level=0, force=0):
"""Add a config file instance to an existing config."""
GitException.check_result(C.git_config_add_file_ondisk)(self._config, to_bytes(path), level, ffi.NULL, force)

err = C.git_config_add_file_ondisk(self._config, to_bytes(path), level,
ffi.NULL, force)
check_error(err)

def snapshot(self):
"""Create a snapshot from this Config object.
@@ -228,9 +197,7 @@ def snapshot(self):
of the configuration files.
"""
ccfg = ffi.new('git_config **')
err = C.git_config_snapshot(ccfg, self._config)
check_error(err)

GitException.check_result(C.git_config_snapshot)(ccfg, self._config)
return Config.from_c(self._repo, ccfg[0])

#
@@ -240,17 +207,15 @@ def snapshot(self):
@staticmethod
def parse_bool(text):
res = ffi.new('int *')
err = C.git_config_parse_bool(res, to_bytes(text))
check_error(err)
GitException.check_result(C.git_config_parse_bool)(res, to_bytes(text))


return res[0] != 0

@staticmethod
def parse_int(text):
res = ffi.new('int64_t *')
err = C.git_config_parse_int64(res, to_bytes(text))
check_error(err)

GitException.check_result(C.git_config_parse_int64)(res, to_bytes(text))
return res[0]

#
@@ -260,8 +225,7 @@ def parse_int(text):
@staticmethod
def _from_found_config(fn):
buf = ffi.new('git_buf *', (ffi.NULL, 0))
err = fn(buf)
check_error(err, io=True)
GitIOError.check_result(fn)(buf)
cpath = ffi.string(buf.ptr).decode('utf-8')
C.git_buf_dispose(buf)

291 changes: 264 additions & 27 deletions pygit2/errors.py
Original file line number Diff line number Diff line change
@@ -26,41 +26,278 @@
# Import from pygit2
from .ffi import ffi, C
from ._pygit2 import GitError
from typing import Callable, Type, Any


value_errors = set([C.GIT_EEXISTS, C.GIT_EINVALIDSPEC, C.GIT_EAMBIGUOUS])
class Passthrough(Exception):
"""
Indicate that we want libgit2 to pretend a function was not set.
def check_error(err, io=False):
if err >= 0:
return
This class is here for backward compatibility.
For new code, GitPassthroughError should be used.
"""
# TODO drop legacy support
def __new__(cls, *args, **kwargs):
return super().__new__(cls, *args, **kwargs)

# Error message
giterr = C.git_error_last()
if giterr != ffi.NULL:
message = ffi.string(giterr.message).decode('utf8')
else:
message = "err %d (no message provided)" % err
def __init__(self, *args, **kwargs):
super().__init__("The function asked for pass-through.")

# Translate to Python errors
if err in value_errors:
raise ValueError(message)

if err == C.GIT_ENOTFOUND:
if io:
raise IOError(message)
class BaseGitException(GitError):
"""
The base exception class of PyGit2. Inherits from the pygit.c GitError exception for backward compatibility.
Do not directly inherit from this class but rather use the GitException subclass for custom exceptions.
"""

raise KeyError(message)
def __new__(cls, exc: Type["BaseGitException"] = None, *args, **kwargs):
if BaseGitException._is_subclass(cl=exc):
# If a more explicit subclass of BaseGitException was provided, use that.
return exc(exc=None, *args, **kwargs)

if err == C.GIT_EINVALIDSPEC:
raise ValueError(message)
try:
# Any subclasses that inherit from BaseGitException are fine with this
return super().__new__(cls, exc=None, *args, **kwargs)
except TypeError:
# Some of the exception classes one might inherit from do only
# accept *args. This could be explicitly handled but would require
# any class that inherits from BaseGitException to define __new__ and
# probably handle such cases there. To be more pythonic let's just
# try and except it here.
return super().__new__(cls, *args)

if err == C.GIT_ITEROVER:
raise StopIteration()
def __init__(self, message: str = None, exitcode: int = None, *args, **kwargs):
"""
Provides the most explicit exception based on it's input.
# Generic Git error
raise GitError(message)
If a message was provided, it will be used as exception message.
Else the message will be generated from the libgit2 error stack, if any.
If no proper error is found in the Git error stack, a generic message using the provided exitcode
as hint will be used. If no exitcode was provided a generic message without an exitcode is raised.
# Indicate that we want libgit2 to pretend a function was not set
class Passthrough(Exception):
def __init__(self):
super().__init__( "The function asked for pass-through")
If a BaseGitException subclass was provided, will raise that more explicit class. See __new__() for details.
Args:
message: the message to use for the generated exception
exitcode: the exit code to use when constructing a generic message
"""
if not message:
# If no message was provided, generate one on best effort
message = BaseGitException._git_error_last(exitcode=exitcode)

super().__init__(message)

@staticmethod
def _is_subclass(cl: Type[Any]):
try:
return issubclass(cl, BaseGitException)
except TypeError:
return False

@staticmethod
def _translate(exitcode: int, *args, **kwargs) -> "BaseGitException":
"""
Translates C based libgit2 exceptions into Python exceptions, if possible.
If no matching Python exception is found a generic BaseGitException will be returned.
Args:
exitcode: the exit code that's known to be an error as returned from libgit2
Returns:
a matching Python exception that inherits from BaseGitException
"""
"""
TODO implement not yet explicitly implemented libgit2 error codes as exceptions and handle them here
GIT_ERROR = -1
GIT_EBUFS = -6
GIT_EBAREREPO = -8
GIT_EUNBORNBRANCH = -9
GIT_EUNMERGED = -10
GIT_ENONFASTFORWARD = -11
GIT_ECONFLICT = -13
GIT_ELOCKED = -14
GIT_EMODIFIED = -15
GIT_EAUTH = -16
GIT_ECERTIFICATE = -17
GIT_EAPPLIED = -18
GIT_EPEEL = -19
GIT_EEOF = -20
GIT_EINVALID = -21
GIT_EUNCOMMITTED = -22
GIT_EDIRECTORY = -23
GIT_EMERGECONFLICT = -24
GIT_PASSTHROUGH = -30
GIT_RETRY = -32
GIT_EMISMATCH = -33
GIT_EINDEXDIRTY = -34
GIT_EAPPLYFAIL = -35
"""
if exitcode == C.GIT_EEXISTS:
return GitExistsError(exitcode=exitcode, *args, **kwargs)
elif exitcode == C.GIT_EINVALIDSPEC:
return GitInvalidSpecError(exitcode=exitcode, *args, **kwargs)
elif exitcode == C.GIT_EAMBIGUOUS:
return GitAmbiguousError(exitcode=exitcode, *args, **kwargs)
elif exitcode == C.GIT_ENOTFOUND:
return GitNotFoundError(exitcode=exitcode, *args, **kwargs)
elif exitcode == C.GIT_ITEROVER:
return GitIterOverError(exitcode=exitcode, *args, **kwargs)
elif exitcode == C.GIT_EUSER:
return GitUserError(exitcode=exitcode, *args, **kwargs)
else:
return GitException(exitcode=exitcode, *args, **kwargs)

@staticmethod
def _git_error_last(exitcode:int = None) -> str:
"""
Get the error message of the last known error from Git.
If no last known error exists, a generic message including the provided exitcode will be returned.
If no exitcode was provided, a generic error message will be returned.
Returns:
the most explicit error message.
"""
git_error = C.git_error_last()
if git_error != ffi.NULL:
return ffi.string(git_error.message).decode("utf8")
if isinstance(exitcode, int):
return f"Git exited with exit code '{exitcode}'. No message was provided."
return "A unknown Git error occurred."

@staticmethod
def check_result(fn: Callable, message: str = None, exc: Type["BaseGitException"] = None, *oargs, **okwargs):
"""
Wraps any libgit2 call that returns a exit code. Returns the result on success (exit code >= 0).
If an error occures (exit code < 0), a best-matching Python exception will be raised.
If a specific message or exception should be raised in case of error they can be provided.
Args:
fn: the callable to wrap.
message: the message to use in the exception in case of error.
exception: the exception to use in case of error.
Returns:
The result of the function call.
Raises:
BaseGitException or a subclass of it.
"""
def wrapped(*iargs, **ikwargs):
# If exceptions occure while calling fn, they'll raise natively
result = fn(*iargs, **ikwargs)
try:
if result and result < C.GIT_OK:
# Translate the exit code returned from Libgit2 to a BaseGitException
# If a more specific BaseGitException subclass and/or a more specific
# message was provided, use it.
raise BaseGitException._translate(exitcode=result, message=message, exc=exc, *oargs, **okwargs)
return result
except Exception as ex:
# Raise all exceptions
raise ex
return wrapped

class GitException(BaseGitException):
"""
Subclass of BaseGitException that should be used to inherit from when implementing new exception subclasses in PyGit2.
"""
def __new__(cls, *args, **kwargs):
# Just leave this here for easier debugging
return super().__new__(cls, *args, **kwargs)

def __init__(self, *args, **kwargs):
# Just leave this here for easier debugging
super().__init__(*args, **kwargs)

@classmethod
def check_result(cls, fn: Callable, message: str = None, exc: Type["BaseGitException"] = None, *oargs, **okwargs):
# Override the check_result behavior from BaseGitException so the calling subclass is used by default
if not exc and cls is not GitException:
# Only auto-set for subclasses of GitException
exc = cls
return BaseGitException.check_result(fn=fn, message=message, exc=exc, *oargs, **okwargs)


class GitExistsError(GitException, ValueError):
def __new__(cls, *args, **kwargs):
# Just leave this here for easier debugging
return super().__new__(cls, *args, **kwargs)

def __init__(self, *args, **kwargs):
# Just leave this here for easier debugging
super().__init__(*args, **kwargs)


class GitInvalidSpecError(GitException, ValueError):
def __new__(cls, *args, **kwargs):
# Just leave this here for easier debugging
return super().__new__(cls, *args, **kwargs)

def __init__(self, *args, **kwargs):
# Just leave this here for easier debugging
super().__init__(*args, **kwargs)


class GitAmbiguousError(GitException, ValueError):
def __new__(cls, *args, **kwargs):
# Just leave this here for easier debugging
return super().__new__(cls, *args, **kwargs)

def __init__(self, *args, **kwargs):
# Just leave this here for easier debugging
super().__init__(*args, **kwargs)


class GitNotFoundError(GitException,KeyError):
def __new__(cls, *args, **kwargs):
# Just leave this here for easier debugging
return super().__new__(cls, *args, **kwargs)

def __init__(self, *args, **kwargs):
# Just leave this here for easier debugging
super().__init__(*args, **kwargs)


class GitIterOverError(GitException,StopIteration):
def __new__(cls, *args, **kwargs):
# Just leave this here for easier debugging
return super().__new__(cls, *args, **kwargs)

def __init__(self, *args, **kwargs):
# Just leave this here for easier debugging
super().__init__(*args, **kwargs)


class GitUserError(GitException,TypeError):
def __new__(cls, *args, **kwargs):
# Just leave this here for easier debugging
return super().__new__(cls, *args, **kwargs)

def __init__(self, *args, **kwargs):
# Just leave this here for easier debugging
super().__init__(*args, **kwargs)

class GitPassthroughError(GitException, Passthrough):
# Inherits from Passthrough purely for backward compatibility
def __new__(cls, *args, **kwargs):
# Just leave this here for easier debugging
return super().__new__(cls, *args, **kwargs)

def __init__(self, *args, **kwargs):
# Just leave this here for easier debugging
super().__init__(*args, **kwargs)

class GitIOError(GitException, IOError):
"""
This class should be replaced by better matching error definitions from libgit2
It's here as a temporary replacement to support explicitly throwing IOErrors in some
of the PyGit2 classes and should not be used for new code.
"""
# TODO drop legacy support
def __new__(cls, *args, **kwargs):
# Just leave this here for easier debugging
return super().__new__(cls, *args, **kwargs)

def __init__(self, *args, **kwargs):
# Just leave this here for easier debugging
super().__init__(*args, **kwargs)
78 changes: 24 additions & 54 deletions pygit2/index.py
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@

# Import from pygit2
from ._pygit2 import Oid, Tree, Diff
from .errors import check_error
from .errors import GitException, GitIOError
from .ffi import ffi, C
from .utils import to_bytes, to_str
from .utils import GenericIterator, StrArray
@@ -42,9 +42,7 @@ def __init__(self, path=None):
to read from and write to.
"""
cindex = ffi.new('git_index **')
err = C.git_index_open(cindex, to_bytes(path))
check_error(err)

GitException.check_result(C.git_index_open)(cindex, to_bytes(path))
self._repo = None
self._index = cindex[0]
self._cindex = cindex
@@ -69,11 +67,10 @@ def __len__(self):
return C.git_index_entrycount(self._index)

def __contains__(self, path):
err = C.git_index_find(ffi.NULL, self._index, to_bytes(path))
if err == C.GIT_ENOTFOUND:
try:
GitException.check_result(C.git_index_find)(ffi.NULL, self._index, to_bytes(path))
except KeyError:
return False

check_error(err)
return True

def __getitem__(self, key):
@@ -107,17 +104,14 @@ def read(self, force=True):
has changed.
"""

err = C.git_index_read(self._index, force)
check_error(err, io=True)
GitIOError.check_result(C.git_index_read)(self._index, force)

def write(self):
"""Write the contents of the Index to disk."""
err = C.git_index_write(self._index)
check_error(err, io=True)
GitIOError.check_result(C.git_index_write)(self._index)

def clear(self):
err = C.git_index_clear(self._index)
check_error(err)
GitException.check_result(C.git_index_clear)(self._index)

def read_tree(self, tree):
"""Replace the contents of the Index with those of the given tree,
@@ -140,8 +134,7 @@ def read_tree(self, tree):

tree_cptr = ffi.new('git_tree **')
ffi.buffer(tree_cptr)[:] = tree._pointer[:]
err = C.git_index_read_tree(self._index, tree_cptr[0])
check_error(err)
GitException.check_result(C.git_index_read_tree)(self._index, tree_cptr[0])

def write_tree(self, repo=None):
"""Create a tree out of the Index. Return the <Oid> object of the
@@ -159,25 +152,22 @@ def write_tree(self, repo=None):
repo = repo or self._repo

if repo:
err = C.git_index_write_tree_to(coid, self._index, repo._repo)
GitException.check_result(C.git_index_write_tree_to)(coid, self._index, repo._repo)
else:
err = C.git_index_write_tree(coid, self._index)
GitException.check_result(C.git_index_write_tree)(coid, self._index)

check_error(err)
return Oid(raw=bytes(ffi.buffer(coid)[:]))

def remove(self, path, level=0):
"""Remove an entry from the Index.
"""
err = C.git_index_remove(self._index, to_bytes(path), level)
check_error(err, io=True)
GitIOError.check_result(C.git_index_remove)(self._index, to_bytes(path), level)

def remove_all(self, pathspecs):
"""Remove all index entries matching pathspecs.
"""
with StrArray(pathspecs) as arr:
err = C.git_index_remove_all(self._index, arr, ffi.NULL, ffi.NULL)
check_error(err, io=True)
GitIOError.check_result(C.git_index_remove_all)(self._index, arr, ffi.NULL, ffi.NULL)

def add_all(self, pathspecs=[]):
"""Add or update index entries matching files in the working directory.
@@ -186,8 +176,7 @@ def add_all(self, pathspecs=[]):
be added.
"""
with StrArray(pathspecs) as arr:
err = C.git_index_add_all(self._index, arr, 0, ffi.NULL, ffi.NULL)
check_error(err, io=True)
GitIOError.check_result(C.git_index_add_all)(self._index, arr, 0, ffi.NULL, ffi.NULL)

def add(self, path_or_entry):
"""Add or update an entry in the Index.
@@ -203,14 +192,13 @@ def add(self, path_or_entry):
if isinstance(path_or_entry, IndexEntry):
entry = path_or_entry
centry, str_ref = entry._to_c()
err = C.git_index_add(self._index, centry)
GitIOError.check_result(C.git_index_add)(self._index, centry)
elif isinstance(path_or_entry, str) or hasattr(path_or_entry, '__fspath__'):
path = path_or_entry
err = C.git_index_add_bypath(self._index, to_bytes(path))
GitIOError.check_result(C.git_index_add_bypath)(self._index, to_bytes(path))
else:
raise AttributeError('argument must be string or IndexEntry')

check_error(err, io=True)

def diff_to_workdir(self, flags=0, context_lines=3, interhunk_lines=0):
"""
@@ -235,18 +223,12 @@ def diff_to_workdir(self, flags=0, context_lines=3, interhunk_lines=0):
raise ValueError('diff needs an associated repository')

copts = ffi.new('git_diff_options *')
err = C.git_diff_init_options(copts, 1)
check_error(err)

GitException.check_result(C.git_diff_init_options)(copts, 1)
copts.flags = flags
copts.context_lines = context_lines
copts.interhunk_lines = interhunk_lines

cdiff = ffi.new('git_diff **')
err = C.git_diff_index_to_workdir(cdiff, repo._repo, self._index,
copts)
check_error(err)

GitException.check_result(C.git_diff_index_to_workdir)(cdiff, repo._repo, self._index,copts)
return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), repo)

def diff_to_tree(self, tree, flags=0, context_lines=3, interhunk_lines=0):
@@ -278,8 +260,7 @@ def diff_to_tree(self, tree, flags=0, context_lines=3, interhunk_lines=0):
raise TypeError('tree must be a Tree')

copts = ffi.new('git_diff_options *')
err = C.git_diff_init_options(copts, 1)
check_error(err)
GitException.check_result(C.git_diff_init_options)(copts, 1)

copts.flags = flags
copts.context_lines = context_lines
@@ -289,9 +270,7 @@ def diff_to_tree(self, tree, flags=0, context_lines=3, interhunk_lines=0):
ffi.buffer(ctree)[:] = tree._pointer[:]

cdiff = ffi.new('git_diff **')
err = C.git_diff_tree_to_index(cdiff, repo._repo, ctree[0],
self._index, copts)
check_error(err)
GitException.check_result(C.git_diff_tree_to_index)(cdiff, repo._repo, ctree[0],self._index, copts)

return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), repo)

@@ -390,10 +369,7 @@ def __getitem__(self, path):
cancestor = ffi.new('git_index_entry **')
cours = ffi.new('git_index_entry **')
ctheirs = ffi.new('git_index_entry **')

err = C.git_index_conflict_get(cancestor, cours, ctheirs,
self._index._index, to_bytes(path))
check_error(err)
GitException.check_result(C.git_index_conflict_get)(cancestor, cours, ctheirs, self._index._index, to_bytes(path))

ancestor = IndexEntry._from_c(cancestor[0])
ours = IndexEntry._from_c(cours[0])
@@ -402,8 +378,7 @@ def __getitem__(self, path):
return ancestor, ours, theirs

def __delitem__(self, path):
err = C.git_index_conflict_remove(self._index._index, to_bytes(path))
check_error(err)
GitException.check_result(C.git_index_conflict_remove)(self._index._index, to_bytes(path))

def __iter__(self):
return ConflictIterator(self._index)
@@ -413,8 +388,7 @@ class ConflictIterator(object):

def __init__(self, index):
citer = ffi.new('git_index_conflict_iterator **')
err = C.git_index_conflict_iterator_new(citer, index._index)
check_error(err)
GitException.check_result(C.git_index_conflict_iterator_new)(citer, index._index)
self._index = index
self._iter = citer[0]

@@ -429,11 +403,7 @@ def __next__(self):
cours = ffi.new('git_index_entry **')
ctheirs = ffi.new('git_index_entry **')

err = C.git_index_conflict_next(cancestor, cours, ctheirs, self._iter)
if err == C.GIT_ITEROVER:
raise StopIteration

check_error(err)
GitException.check_result(C.git_index_conflict_next)(cancestor, cours, ctheirs, self._iter)

ancestor = IndexEntry._from_c(cancestor[0])
ours = IndexEntry._from_c(cours[0])
6 changes: 2 additions & 4 deletions pygit2/refspec.py
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@
# Boston, MA 02110-1301, USA.

# Import from pygit2
from .errors import check_error
from .errors import GitException
from .ffi import ffi, C
from .utils import to_bytes

@@ -73,9 +73,7 @@ def dst_matches(self, ref):

def _transform(self, ref, fn):
buf = ffi.new('git_buf *', (ffi.NULL, 0))
err = fn(buf, self._refspec, to_bytes(ref))
check_error(err)

GitException.check_result(fn)(buf, self._refspec, to_bytes(ref))
try:
return ffi.string(buf.ptr).decode('utf-8')
finally:
106 changes: 36 additions & 70 deletions pygit2/remote.py
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@

# Import from pygit2
from ._pygit2 import Oid
from .errors import check_error, Passthrough
from .errors import GitException, GitPassthroughError
from .ffi import ffi, C
from .refspec import Refspec
from .utils import to_bytes, strarray_to_strings, StrArray
@@ -118,7 +118,7 @@ def credentials(self, url, username_from_url, allowed_types):
allowed_types : int
Credential types supported by the remote.
"""
raise Passthrough
raise GitPassthroughError

def certificate_check(self, certificate, valid, host):
"""
@@ -140,7 +140,7 @@ def certificate_check(self, certificate, valid, host):
The hostname we want to connect to.
"""

raise Passthrough
raise GitPassthroughError

def transfer_progress(self, stats):
"""
@@ -267,7 +267,6 @@ def _update_tips_cb(refname, a, b, data):
s = maybe_string(refname)
a = Oid(raw=bytes(ffi.buffer(a)[:]))
b = Oid(raw=bytes(ffi.buffer(b)[:]))

update_tips(s, a, b)
except Exception as e:
self._stored_exception = e
@@ -304,7 +303,7 @@ def _credentials_cb(cred_out, url, username, allowed, data):
try:
ccred = get_credentials(credentials, url, username, allowed)
cred_out[0] = ccred[0]
except Passthrough:
except GitPassthroughError:
return C.GIT_PASSTHROUGH
except Exception as e:
self._stored_exception = e
@@ -325,13 +324,13 @@ def _certificate_cb(cert_i, valid, host, data):

certificate_check = getattr(self, 'certificate_check', None)
if not certificate_check:
raise Passthrough
raise GitPassthroughError

# python's parsing is deep in the libraries and assumes an OpenSSL-owned cert
val = certificate_check(None, bool(valid), ffi.string(host))
if not val:
return C.GIT_ECERTIFICATE
except Passthrough:
except GitPassthroughError:
if is_ssh:
return 0
elif valid:
@@ -344,6 +343,7 @@ def _certificate_cb(cert_i, valid, host, data):

return 0


class Remote(object):
def __init__(self, repo, ptr):
"""The constructor is for internal use only"""
@@ -383,14 +383,12 @@ def connect(self, callbacks=None, direction=C.GIT_DIRECTION_FETCH):
callbacks = RemoteCallbacks()

callbacks._fill_connect_callbacks(remote_callbacks)
err = C.git_remote_connect(self._remote, direction, remote_callbacks, ffi.NULL, ffi.NULL);
check_error(err)
GitException.check_result(C.git_remote_connect)(self._remote, direction, remote_callbacks, ffi.NULL, ffi.NULL);

def save(self):
"""Save a remote to its repository's configuration."""

err = C.git_remote_save(self._remote)
check_error(err)
GitException.check_result(C.git_remote_save)(self._remote)

def fetch(self, refspecs=None, message=None, callbacks=None, prune=C.GIT_FETCH_PRUNE_UNSPECIFIED):
"""Perform a fetch against this remote. Returns a <TransferProgress>
@@ -407,7 +405,7 @@ def fetch(self, refspecs=None, message=None, callbacks=None, prune=C.GIT_FETCH_P
"""

fetch_opts = ffi.new('git_fetch_options *')
err = C.git_fetch_init_options(fetch_opts, C.GIT_FETCH_OPTIONS_VERSION)
GitException.check_result(C.git_fetch_init_options)(fetch_opts, C.GIT_FETCH_OPTIONS_VERSION)

if callbacks is None:
callbacks = RemoteCallbacks()
@@ -417,10 +415,11 @@ def fetch(self, refspecs=None, message=None, callbacks=None, prune=C.GIT_FETCH_P

try:
with StrArray(refspecs) as arr:
err = C.git_remote_fetch(self._remote, arr, fetch_opts, to_bytes(message))
GitException.check_result(C.git_remote_fetch)(self._remote, arr, fetch_opts, to_bytes(message))
# TODO check if _stored_exceptions is still needed for anything or if it can be replaced with the new GitException logic
if callbacks._stored_exception:
raise callbacks._stored_exception
check_error(err)

finally:
callbacks._self_handle = None

@@ -437,8 +436,7 @@ def ls_remotes(self, callbacks=None):
refs = ffi.new('git_remote_head ***')
refs_len = ffi.new('size_t *')

err = C.git_remote_ls(refs, refs_len, self._remote)
check_error(err)
GitException.check_result(C.git_remote_ls)(refs, refs_len, self._remote)

results = []

@@ -471,8 +469,7 @@ def prune(self, callbacks=None):
if callbacks is None:
callbacks = RemoteCallbacks()
callbacks._fill_prune_callbacks(remote_callbacks)
err = C.git_remote_prune(self._remote, remote_callbacks)
check_error(err)
GitException.check_result(C.git_remote_prune)(self._remote, remote_callbacks)

@property
def refspec_count(self):
@@ -490,8 +487,7 @@ def fetch_refspecs(self):
"""Refspecs that will be used for fetching"""

specs = ffi.new('git_strarray *')
err = C.git_remote_get_fetch_refspecs(specs, self._remote)
check_error(err)
GitException.check_result(C.git_remote_get_fetch_refspecs)(specs, self._remote)

return strarray_to_strings(specs)

@@ -500,8 +496,7 @@ def push_refspecs(self):
"""Refspecs that will be used for pushing"""

specs = ffi.new('git_strarray *')
err = C.git_remote_get_push_refspecs(specs, self._remote)
check_error(err)
GitException.check_result(C.git_remote_get_push_refspecs)(specs, self._remote)

return strarray_to_strings(specs)

@@ -522,7 +517,7 @@ def push(self, specs, callbacks=None):
Push refspecs to use.
"""
push_opts = ffi.new('git_push_options *')
err = C.git_push_init_options(push_opts, C.GIT_PUSH_OPTIONS_VERSION)
GitException.check_result(C.git_push_init_options)(push_opts, C.GIT_PUSH_OPTIONS_VERSION)

if callbacks is None:
callbacks = RemoteCallbacks()
@@ -532,8 +527,7 @@ def push(self, specs, callbacks=None):

try:
with StrArray(specs) as refspecs:
err = C.git_remote_push(self._remote, refspecs, push_opts)
check_error(err)
GitException.check_result(C.git_remote_push)(self._remote, refspecs, push_opts)
finally:
callbacks._self_handle = None

@@ -558,34 +552,27 @@ def get_credentials(fn, url, username, allowed):
ccred = ffi.new('git_cred **')
if cred_type == C.GIT_CREDTYPE_USERPASS_PLAINTEXT:
name, passwd = credential_tuple
err = C.git_cred_userpass_plaintext_new(ccred, to_bytes(name),
to_bytes(passwd))
GitException.check_result(C.git_cred_userpass_plaintext_new)(ccred, to_bytes(name),to_bytes(passwd))

elif cred_type == C.GIT_CREDTYPE_SSH_KEY:
name, pubkey, privkey, passphrase = credential_tuple
if pubkey is None and privkey is None:
err = C.git_cred_ssh_key_from_agent(ccred, to_bytes(name))
GitException.check_result(C.git_cred_ssh_key_from_agent)(ccred, to_bytes(name))
else:
err = C.git_cred_ssh_key_new(ccred, to_bytes(name),
to_bytes(pubkey), to_bytes(privkey),
to_bytes(passphrase))
GitException.check_result(C.git_cred_ssh_key_new)(ccred, to_bytes(name),to_bytes(pubkey), to_bytes(privkey),to_bytes(passphrase))

elif cred_type == C.GIT_CREDTYPE_USERNAME:
name, = credential_tuple
err = C.git_cred_username_new(ccred, to_bytes(name))
GitException.check_result(C.git_cred_username_new)(ccred, to_bytes(name))

elif cred_type == C.GIT_CREDTYPE_SSH_MEMORY:
name, pubkey, privkey, passphrase = credential_tuple
if pubkey is None and privkey is None:
raise TypeError("SSH keys from memory are empty")
err = C.git_cred_ssh_key_memory_new(ccred, to_bytes(name),
to_bytes(pubkey), to_bytes(privkey),
to_bytes(passphrase))
GitException.check_result(C.git_cred_ssh_key_memory_new)(ccred, to_bytes(name),to_bytes(pubkey), to_bytes(privkey),to_bytes(passphrase))
else:
raise TypeError("unsupported credential type")

check_error(err)

return ccred

class RemoteCollection(object):
@@ -605,9 +592,7 @@ def __len__(self):
names = ffi.new('git_strarray *')

try:
err = C.git_remote_list(names, self._repo._repo)
check_error(err)

GitException.check_result(C.git_remote_list)(names, self._repo._repo)
return names.count
finally:
C.git_strarray_free(names)
@@ -616,26 +601,19 @@ def __iter__(self):
names = ffi.new('git_strarray *')

try:
err = C.git_remote_list(names, self._repo._repo)
check_error(err)

GitException.check_result(C.git_remote_list)(names, self._repo._repo)
cremote = ffi.new('git_remote **')
for i in range(names.count):
err = C.git_remote_lookup(cremote, self._repo._repo, names.strings[i])
check_error(err)

GitException.check_result(C.git_remote_lookup)(cremote, self._repo._repo, names.strings[i])
yield Remote(self._repo, cremote[0])
finally:
C.git_strarray_free(names)

def __getitem__(self, name):
if isinstance(name, int):
return list(self)[name]

cremote = ffi.new('git_remote **')
err = C.git_remote_lookup(cremote, self._repo._repo, to_bytes(name))
check_error(err)

GitException.check_result(C.git_remote_lookup)(cremote, self._repo._repo, to_bytes(name))
return Remote(self._repo, cremote[0])

def create(self, name, url, fetch=None):
@@ -644,15 +622,12 @@ def create(self, name, url, fetch=None):
If 'fetch' is provided, this fetch refspec will be used instead of the default
"""

cremote = ffi.new('git_remote **')

if fetch:
err = C.git_remote_create_with_fetchspec(cremote, self._repo._repo, to_bytes(name), to_bytes(url), to_bytes(fetch))
GitException.check_result(C.git_remote_create_with_fetchspec)(cremote, self._repo._repo, to_bytes(name), to_bytes(url), to_bytes(fetch))
else:
err = C.git_remote_create(cremote, self._repo._repo, to_bytes(name), to_bytes(url))

check_error(err)
GitException.check_result(C.git_remote_create)(cremote, self._repo._repo, to_bytes(name), to_bytes(url))

return Remote(self._repo, cremote[0])

@@ -671,9 +646,7 @@ def rename(self, name, new_name):
raise ValueError("New remote name must be a non-empty string")

problems = ffi.new('git_strarray *')
err = C.git_remote_rename(problems, self._repo._repo, to_bytes(name), to_bytes(new_name))
check_error(err)

GitException.check_result(C.git_remote_rename)(problems, self._repo._repo, to_bytes(name), to_bytes(new_name))
ret = strarray_to_strings(problems)
C.git_strarray_free(problems)

@@ -684,31 +657,24 @@ def delete(self, name):
All remote-tracking branches and configuration settings for the remote will be removed.
"""
err = C.git_remote_delete(self._repo._repo, to_bytes(name))
check_error(err)
GitException.check_result(C.git_remote_delete)(self._repo._repo, to_bytes(name))

def set_url(self, name, url):
""" Set the URL for a remote
"""
err = C.git_remote_set_url(self._repo._repo, to_bytes(name), to_bytes(url))
check_error(err)
GitException.check_result(C.git_remote_set_url)(self._repo._repo, to_bytes(name), to_bytes(url))

def set_push_url(self, name, url):
"""Set the push-URL for a remote
"""
err = C.git_remote_set_pushurl(self._repo._repo, to_bytes(name), to_bytes(url))
check_error(err)
GitException.check_result(C.git_remote_set_pushurl)(self._repo._repo, to_bytes(name), to_bytes(url))

def add_fetch(self, name, refspec):
"""Add a fetch refspec (str) to the remote
"""

err = C.git_remote_add_fetch(self._repo._repo, to_bytes(name), to_bytes(refspec))
check_error(err)
GitException.check_result(C.git_remote_add_fetch)(self._repo._repo, to_bytes(name), to_bytes(refspec))

def add_push(self, name, refspec):
"""Add a push refspec (str) to the remote
"""

err = C.git_remote_add_push(self._repo._repo, to_bytes(name), to_bytes(refspec))
check_error(err)
GitException.check_result(C.git_remote_add_push)(self._repo._repo, to_bytes(name), to_bytes(refspec))
104 changes: 31 additions & 73 deletions pygit2/repository.py
Original file line number Diff line number Diff line change
@@ -40,7 +40,7 @@
from ._pygit2 import InvalidSpecError

from .config import Config
from .errors import check_error
from .errors import GitException, GitIOError
from .ffi import ffi, C
from .index import Index
from .remote import RemoteCollection
@@ -88,8 +88,7 @@ def lookup_submodule(self, path):
csub = ffi.new('git_submodule **')
cpath = ffi.new('char[]', to_bytes(path))

err = C.git_submodule_lookup(csub, self._repo, cpath)
check_error(err)
GitException.check_result(C.git_submodule_lookup)(csub, self._repo, cpath)
return Submodule._from_c(self, csub[0])

def update_submodules(self, submodules=None, init=False, callbacks=None):
@@ -110,12 +109,7 @@ def update_submodules(self, submodules=None, init=False, callbacks=None):

for submodule in submodules:
submodule_instance = self.lookup_submodule(submodule)
err = C.git_submodule_update(
submodule_instance._subm,
i,
opts)
check_error(err)

GitException.check_result(C.git_submodule_update)(submodule_instance._subm,i,opts)
return None

#
@@ -159,8 +153,7 @@ def config(self):
(if they are available).
"""
cconfig = ffi.new('git_config **')
err = C.git_repository_config(cconfig, self._repo)
check_error(err)
GitException.check_result(C.git_repository_config)(cconfig, self._repo)

return Config.from_c(self, cconfig[0])

@@ -172,9 +165,7 @@ def config_snapshot(self):
of the configuration files.
"""
cconfig = ffi.new('git_config **')
err = C.git_repository_config_snapshot(cconfig, self._repo)
check_error(err)

GitException.check_result(C.git_repository_config_snapshot)(cconfig, self._repo)
return Config.from_c(self, cconfig[0])

#
@@ -241,7 +232,7 @@ def resolve_refish(self, refish):
def _checkout_args_to_options(strategy=None, directory=None, paths=None):
# Create the options struct to pass
copts = ffi.new('git_checkout_options *')
check_error(C.git_checkout_init_options(copts, 1))
GitException.check_result(C.git_checkout_init_options)(copts, 1)

# References we need to keep to strings and so forth
refs = []
@@ -270,15 +261,15 @@ def checkout_head(self, **kwargs):
For arguments, see Repository.checkout().
"""
copts, refs = Repository._checkout_args_to_options(**kwargs)
check_error(C.git_checkout_head(self._repo, copts))
GitException.check_result(C.git_checkout_head)(self._repo, copts)

def checkout_index(self, index=None, **kwargs):
"""Checkout the given index or the repository's index
For arguments, see Repository.checkout().
"""
copts, refs = Repository._checkout_args_to_options(**kwargs)
check_error(C.git_checkout_index(self._repo, index._index if index else ffi.NULL, copts))
GitException.check_result(C.git_checkout_index)(self._repo, index._index if index else ffi.NULL, copts)

def checkout_tree(self, treeish, **kwargs):
"""Checkout the given treeish
@@ -288,8 +279,7 @@ def checkout_tree(self, treeish, **kwargs):
copts, refs = Repository._checkout_args_to_options(**kwargs)
cptr = ffi.new('git_object **')
ffi.buffer(cptr)[:] = treeish._pointer[:]

check_error(C.git_checkout_tree(self._repo, cptr[0], copts))
GitException.check_result(C.git_checkout_tree)(self._repo, cptr[0], copts)

def checkout(self, refname=None, **kwargs):
"""
@@ -363,13 +353,11 @@ def set_head(self, target):
if isinstance(target, Oid):
oid = ffi.new('git_oid *')
ffi.buffer(oid)[:] = target.raw[:]
err = C.git_repository_set_head_detached(self._repo, oid)
check_error(err)
GitException.check_result(C.git_repository_set_head_detached)(self._repo, oid)
return

# if it's a string, then it's a reference name
err = C.git_repository_set_head(self._repo, to_bytes(target))
check_error(err)
GitException.check_result(C.git_repository_set_head)(self._repo, to_bytes(target))

#
# Diff
@@ -549,9 +537,7 @@ def blame(self, path, flags=None, min_match_characters=None,
options.max_line = max_line

cblame = ffi.new('git_blame **')
err = C.git_blame_file(cblame, self._repo, to_bytes(path), options)
check_error(err)

GitException.check_result(C.git_blame_file)(cblame, self._repo, to_bytes(path), options)
return Blame._from_c(self, cblame[0])

#
@@ -561,9 +547,7 @@ def blame(self, path, flags=None, min_match_characters=None,
def index(self):
"""Index representing the repository's index file."""
cindex = ffi.new('git_index **')
err = C.git_repository_index(cindex, self._repo)
check_error(err, io=True)

GitIOError.check_result(C.git_repository_index)(cindex, self._repo)
return Index.from_c(self, cindex)

#
@@ -591,9 +575,7 @@ def favor_to_enum(favor):
raise ValueError("unkown favor value %s" % favor)

opts = ffi.new('git_merge_options *')
err = C.git_merge_init_options(opts, C.GIT_MERGE_OPTIONS_VERSION)
check_error(err)

GitException.check_result(C.git_merge_init_options)(opts, C.GIT_MERGE_OPTIONS_VERSION)
opts.file_favor = favor_val

return opts
@@ -619,12 +601,7 @@ def merge_file_from_index(self, ancestor, ours, theirs):
ctheirs, theirs_str_ref = (
theirs._to_c() if theirs is not None else (ffi.NULL, ffi.NULL))

err = C.git_merge_file_from_index(
cmergeresult, self._repo,
cancestor, cours, ctheirs,
ffi.NULL);
check_error(err)

GitException.check_result(C.git_merge_file_from_index)(cmergeresult, self._repo,cancestor, cours, ctheirs,ffi.NULL);
ret = ffi.string(cmergeresult.ptr,
cmergeresult.len).decode('utf-8')
C.git_merge_file_result_free(cmergeresult)
@@ -676,9 +653,7 @@ def merge_commits(self, ours, theirs, favor='normal'):
ffi.buffer(ours_ptr)[:] = ours._pointer[:]
ffi.buffer(theirs_ptr)[:] = theirs._pointer[:]

err = C.git_merge_commits(cindex, self._repo, ours_ptr[0], theirs_ptr[0], opts)
check_error(err)

GitException.check_result(C.git_merge_commits)(cindex, self._repo, ours_ptr[0], theirs_ptr[0], opts)
return Index.from_c(self, cindex)

def merge_trees(self, ancestor, ours, theirs, favor='normal'):
@@ -731,9 +706,7 @@ def merge_trees(self, ancestor, ours, theirs, favor='normal'):
ffi.buffer(ours_ptr)[:] = ours._pointer[:]
ffi.buffer(theirs_ptr)[:] = theirs._pointer[:]

err = C.git_merge_trees(cindex, self._repo, ancestor_ptr[0], ours_ptr[0], theirs_ptr[0], opts)
check_error(err)

GitException.check_result(C.git_merge_trees)(cindex, self._repo, ancestor_ptr[0], ours_ptr[0], theirs_ptr[0], opts)
return Index.from_c(self, cindex)

#
@@ -826,10 +799,9 @@ def describe(self, committish=None, max_candidates_tags=None,
cptr = ffi.new('git_object **')
ffi.buffer(cptr)[:] = commit._pointer[:]

err = C.git_describe_commit(result, cptr[0], options)
GitException.check_result(C.git_describe_commit)(result, cptr[0], options)
else:
err = C.git_describe_workdir(result, self._repo, options)
check_error(err)
GitException.check_result(C.git_describe_workdir)(result, self._repo, options)

try:
format_options = ffi.new('git_describe_format_options *')
@@ -845,9 +817,7 @@ def describe(self, committish=None, max_candidates_tags=None,
format_options.dirty_suffix = dirty_ptr

buf = ffi.new('git_buf *', (ffi.NULL, 0))

err = C.git_describe_format(buf, result[0], format_options)
check_error(err)
GitException.check_result(C.git_describe_format)(buf, result[0], format_options)

try:
return ffi.string(buf.ptr).decode('utf-8')
@@ -903,15 +873,13 @@ def stash(self, stasher, message=None, keep_index=False,
ffi.buffer(stasher_cptr)[:] = stasher._pointer[:]

coid = ffi.new('git_oid *')
err = C.git_stash_save(coid, self._repo, stasher_cptr[0], stash_msg, flags)
check_error(err)

GitException.check_result(C.git_stash_save)(coid, self._repo, stasher_cptr[0], stash_msg, flags)
return Oid(raw=bytes(ffi.buffer(coid)[:]))

@staticmethod
def _stash_args_to_options(reinstate_index=False, **kwargs):
stash_opts = ffi.new('git_stash_apply_options *')
check_error(C.git_stash_apply_init_options(stash_opts, 1))
GitException.check_result(C.git_stash_apply_init_options)(stash_opts, 1)

flags = reinstate_index * C.GIT_STASH_APPLY_REINSTATE_INDEX
stash_opts.flags = flags
@@ -944,7 +912,7 @@ def stash_apply(self, index=0, **kwargs):
>>> repo.stash_apply(strategy=GIT_CHECKOUT_ALLOW_CONFLICTS)
"""
stash_opts = Repository._stash_args_to_options(**kwargs)
check_error(C.git_stash_apply(self._repo, index, stash_opts))
GitException.check_result(C.git_stash_apply)(self._repo, index, stash_opts)

def stash_drop(self, index=0):
"""
@@ -956,15 +924,15 @@ def stash_drop(self, index=0):
The position within the stash list of the stash to remove. 0 is
the most recent stash.
"""
check_error(C.git_stash_drop(self._repo, index))
GitException.check_result(C.git_stash_drop)(self._repo, index)

def stash_pop(self, index=0, **kwargs):
"""Apply a stashed state and remove it from the stash list.
For arguments, see Repository.stash_apply().
"""
stash_opts = Repository._stash_args_to_options(**kwargs)
check_error(C.git_stash_pop(self._repo, index, stash_opts))
GitException.check_result(C.git_stash_pop)(self._repo, index, stash_opts)

#
# Utility for writing a tree into an archive
@@ -1074,8 +1042,7 @@ def ahead_behind(self, local, upstream):
oid1, oid2 = ffi.new('git_oid *'), ffi.new('git_oid *')
ffi.buffer(oid1)[:] = local.raw[:]
ffi.buffer(oid2)[:] = upstream.raw[:]
err = C.git_graph_ahead_behind(ahead, behind, self._repo, oid1, oid2)
check_error(err)
GitException.check_result(C.git_graph_ahead_behind)(ahead, behind, self._repo, oid1, oid2)

return int(ahead[0]), int(behind[0])

@@ -1104,8 +1071,7 @@ def get_attr(self, path, name, flags=0):
"""

cvalue = ffi.new('char **')
err = C.git_attr_get(cvalue, self._repo, flags, to_bytes(path), to_bytes(name))
check_error(err)
GitException.check_result(C.git_attr_get)(cvalue, self._repo, flags, to_bytes(path), to_bytes(name))

# Now let's see if we can figure out what the value is
attr_kind = C.git_attr_value(cvalue[0])
@@ -1128,9 +1094,7 @@ def ident(self):
cname = ffi.new('char **')
cemail = ffi.new('char **')

err = C.git_repository_ident(cname, cemail, self._repo)
check_error(err)

GitException.check_result(C.git_repository_ident)(cname, cemail, self._repo)
return (ffi.string(cname).decode('utf-8'), ffi.string(cemail).decode('utf-8'))

def set_ident(self, name, email):
@@ -1141,8 +1105,7 @@ def set_ident(self, name, email):
used. If none is set, it will be read from the configuration.
"""

err = C.git_repository_set_ident(self._repo, to_bytes(name), to_bytes(email))
check_error(err)
GitException.check_result(C.git_repository_set_ident)(self._repo, to_bytes(name), to_bytes(email))

def revert_commit(self, revert_commit, our_commit, mainline=0):
"""
@@ -1170,13 +1133,8 @@ def revert_commit(self, revert_commit, our_commit, mainline=0):
ffi.buffer(our_commit_ptr)[:] = our_commit._pointer[:]

opts = ffi.new('git_merge_options *')
err = C.git_merge_init_options(opts, C.GIT_MERGE_OPTIONS_VERSION)
check_error(err)

err = C.git_revert_commit(
cindex, self._repo, revert_commit_ptr[0], our_commit_ptr[0], mainline, opts
)
check_error(err)
GitException.check_result(C.git_merge_init_options)(opts, C.GIT_MERGE_OPTIONS_VERSION)
GitException.check_result(C.git_revert_commit)(cindex, self._repo, revert_commit_ptr[0], our_commit_ptr[0], mainline, opts)

return Index.from_c(self, cindex)

6 changes: 2 additions & 4 deletions pygit2/submodule.py
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@
# Boston, MA 02110-1301, USA.

from ._pygit2 import Oid
from .errors import check_error
from .errors import GitException
from .ffi import ffi, C


@@ -45,9 +45,7 @@ def __del__(self):
def open(self):
"""Open the repository for a submodule."""
crepo = ffi.new('git_repository **')
err = C.git_submodule_open(crepo, self._subm)
check_error(err)

GitException.check_result(C.git_submodule_open)(crepo, self._subm)
return self._repo._from_c(crepo[0], True)

@property
6 changes: 2 additions & 4 deletions test/test_config.py
Original file line number Diff line number Diff line change
@@ -107,10 +107,8 @@ def test_read(self):

with pytest.raises(TypeError): config[()]
with pytest.raises(TypeError): config[-4]
self.assertRaisesWithArg(ValueError, "invalid config item name 'abc'",
lambda: config['abc'])
self.assertRaisesWithArg(KeyError, 'abc.def',
lambda: config['abc.def'])
self.assertRaisesWithArg(ValueError, "invalid config item name 'abc'", lambda: config['abc'])
self.assertRaisesWithArg(KeyError, "config value 'abc.def' was not found", lambda: config['abc.def'])

assert 'core.bare' in config
assert not config.get_bool('core.bare')