Safer handling of string arrays
We need to keep hold of the strings which we create. We must also hold on to the array of strings which we assing to our git_strarray. We were not doing the latter, which meant that our strings may have been freed too early, leaving us with with memory access errors (though often not leading to a crash due to the custom allocator in python). As we need to keep hold of two/three pieces of information, this looks like a good place to introduce a context manager. This allows us to keep these pointers alive without burdening the call sites with a return of multiple objects they have no use for.
This commit is contained in:
@@ -32,7 +32,7 @@ from __future__ import absolute_import, unicode_literals
|
|||||||
from _pygit2 import Oid, Tree, Diff
|
from _pygit2 import Oid, Tree, Diff
|
||||||
from .errors import check_error
|
from .errors import check_error
|
||||||
from .ffi import ffi, C
|
from .ffi import ffi, C
|
||||||
from .utils import is_string, strings_to_strarray, to_bytes, to_str
|
from .utils import is_string, to_bytes, to_str, StrArray
|
||||||
|
|
||||||
|
|
||||||
class Index(object):
|
class Index(object):
|
||||||
@@ -175,9 +175,9 @@ class Index(object):
|
|||||||
If pathspecs are specified, only files matching those pathspecs will
|
If pathspecs are specified, only files matching those pathspecs will
|
||||||
be added.
|
be added.
|
||||||
"""
|
"""
|
||||||
arr, refs = strings_to_strarray(pathspecs)
|
with StrArray(pathspecs) as arr:
|
||||||
err = C.git_index_add_all(self._index, arr, 0, ffi.NULL, ffi.NULL)
|
err = C.git_index_add_all(self._index, arr, 0, ffi.NULL, ffi.NULL)
|
||||||
check_error(err, True)
|
check_error(err, True)
|
||||||
|
|
||||||
def add(self, path_or_entry):
|
def add(self, path_or_entry):
|
||||||
"""add([path|entry])
|
"""add([path|entry])
|
||||||
|
@@ -34,7 +34,7 @@ from .errors import check_error, GitError
|
|||||||
from .ffi import ffi, C
|
from .ffi import ffi, C
|
||||||
from .credentials import KeypairFromAgent
|
from .credentials import KeypairFromAgent
|
||||||
from .refspec import Refspec
|
from .refspec import Refspec
|
||||||
from .utils import to_bytes, strarray_to_strings, strings_to_strarray
|
from .utils import to_bytes, strarray_to_strings, StrArray
|
||||||
|
|
||||||
|
|
||||||
def maybe_string(ptr):
|
def maybe_string(ptr):
|
||||||
@@ -253,9 +253,9 @@ class Remote(object):
|
|||||||
|
|
||||||
@fetch_refspecs.setter
|
@fetch_refspecs.setter
|
||||||
def fetch_refspecs(self, l):
|
def fetch_refspecs(self, l):
|
||||||
arr, refs = strings_to_strarray(l)
|
with StrArray(l) as arr:
|
||||||
err = C.git_remote_set_fetch_refspecs(self._remote, arr)
|
err = C.git_remote_set_fetch_refspecs(self._remote, arr)
|
||||||
check_error(err)
|
check_error(err)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def push_refspecs(self):
|
def push_refspecs(self):
|
||||||
@@ -269,9 +269,9 @@ class Remote(object):
|
|||||||
|
|
||||||
@push_refspecs.setter
|
@push_refspecs.setter
|
||||||
def push_refspecs(self, l):
|
def push_refspecs(self, l):
|
||||||
arr, refs = strings_to_strarray(l)
|
with StrArray(l) as arr:
|
||||||
err = C.git_remote_set_push_refspecs(self._remote, arr)
|
err = C.git_remote_set_push_refspecs(self._remote, arr)
|
||||||
check_error(err)
|
check_error(err)
|
||||||
|
|
||||||
def add_fetch(self, spec):
|
def add_fetch(self, spec):
|
||||||
"""add_fetch(refspec)
|
"""add_fetch(refspec)
|
||||||
@@ -305,7 +305,6 @@ class Remote(object):
|
|||||||
err = C.git_remote_init_callbacks(defaultcallbacks, 1)
|
err = C.git_remote_init_callbacks(defaultcallbacks, 1)
|
||||||
check_error(err)
|
check_error(err)
|
||||||
|
|
||||||
refspecs, refspecs_refs = strings_to_strarray(specs)
|
|
||||||
if signature:
|
if signature:
|
||||||
sig_cptr = ffi.new('git_signature **')
|
sig_cptr = ffi.new('git_signature **')
|
||||||
ffi.buffer(sig_cptr)[:] = signature._pointer[:]
|
ffi.buffer(sig_cptr)[:] = signature._pointer[:]
|
||||||
@@ -333,8 +332,9 @@ class Remote(object):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
try:
|
try:
|
||||||
err = C.git_remote_push(self._remote, refspecs, ffi.NULL, sig_ptr, to_bytes(message))
|
with StrArray(specs) as refspecs:
|
||||||
check_error(err)
|
err = C.git_remote_push(self._remote, refspecs, ffi.NULL, sig_ptr, to_bytes(message))
|
||||||
|
check_error(err)
|
||||||
finally:
|
finally:
|
||||||
self._self_handle = None
|
self._self_handle = None
|
||||||
|
|
||||||
|
@@ -50,34 +50,34 @@ def strarray_to_strings(arr):
|
|||||||
return l
|
return l
|
||||||
|
|
||||||
|
|
||||||
def strings_to_strarray(l):
|
class StrArray(object):
|
||||||
"""Convert a list of strings to a git_strarray
|
"""A git_strarray wrapper
|
||||||
|
|
||||||
We return first the git_strarray* you can pass to libgit2 and a
|
Use this in order to get a git_strarray* to pass to libgit2 out of a
|
||||||
list of references to the memory, which we must keep around for as
|
list of strings. This has a context manager, which you should use, e.g.
|
||||||
long as the git_strarray must live.
|
|
||||||
|
with StrArray(list_of_strings) as arr:
|
||||||
|
C.git_function_that_takes_strarray(arr)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not isinstance(l, list):
|
def __init__(self, l):
|
||||||
raise TypeError("Value must be a list")
|
if not isinstance(l, list):
|
||||||
|
raise TypeError("Value must be a list")
|
||||||
|
|
||||||
arr = ffi.new('git_strarray *')
|
arr = ffi.new('git_strarray *')
|
||||||
strings = ffi.new('char *[]', len(l))
|
strings = [None] * len(l)
|
||||||
|
for i in range(len(l)):
|
||||||
|
if not is_string(l[i]):
|
||||||
|
raise TypeError("Value must be a string")
|
||||||
|
|
||||||
# We need refs in order to keep a reference to the value returned
|
strings[i] = ffi.new('char []', to_bytes(l[i]))
|
||||||
# by the ffi.new(). Otherwise, they will be freed and the memory
|
|
||||||
# re-used, with less than great consequences.
|
|
||||||
refs = [None] * len(l)
|
|
||||||
|
|
||||||
for i in range(len(l)):
|
self._arr = ffi.new('char *[]', strings)
|
||||||
if not is_string(l[i]):
|
self._strings = strings
|
||||||
raise TypeError("Value must be a string")
|
self.array = ffi.new('git_strarray *', [self._arr, len(strings)])
|
||||||
|
|
||||||
s = ffi.new('char []', to_bytes(l[i]))
|
def __enter__(self):
|
||||||
refs[i] = s
|
return self.array
|
||||||
strings[i] = s
|
|
||||||
|
|
||||||
arr.strings = strings
|
def __exit__(self, type, value, traceback):
|
||||||
arr.count = len(l)
|
pass
|
||||||
|
|
||||||
return arr, refs
|
|
||||||
|
Reference in New Issue
Block a user