Skip to content

Commit 9f5b1b8

Browse files
committed
Internal: Refactoring callbacks code
The purpose is to increase readibility.
1 parent aa6f74d commit 9f5b1b8

File tree

5 files changed

+124
-116
lines changed

5 files changed

+124
-116
lines changed

pygit2/__init__.py

+7-12
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
# High level API
3333
from .blame import Blame, BlameHunk
34+
from .callbacks import git_fetch_options
3435
from .config import Config
3536
from .credentials import *
3637
from .errors import check_error, Passthrough
@@ -246,18 +247,12 @@ def clone_repository(
246247

247248
opts.bare = bare
248249

249-
if callbacks is None:
250-
callbacks = RemoteCallbacks()
251-
callbacks._fill_fetch_options(opts.fetch_opts)
252-
253-
err = C.git_clone(crepo, to_bytes(url), to_bytes(path), opts)
254-
255-
# Error handling
256-
exc = d.get('exception', callbacks._stored_exception)
257-
if exc:
258-
raise exc
259-
260-
check_error(err)
250+
with git_fetch_options(callbacks, opts=opts.fetch_opts) as (_, cb):
251+
err = C.git_clone(crepo, to_bytes(url), to_bytes(path), opts)
252+
exc = d.get('exception')
253+
if exc:
254+
raise exc
255+
check_error(err, cb)
261256

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

pygit2/callbacks.py

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
"""
2+
This is a utility module, it's here to support calls from libgit2 to written in
3+
Python.
4+
"""
5+
6+
# Standard Library
7+
from contextlib import contextmanager
8+
9+
# pygit2
10+
from .ffi import ffi, C
11+
12+
13+
@contextmanager
14+
def git_fetch_options(callbacks, opts=None):
15+
from .remote import RemoteCallbacks
16+
if callbacks is None:
17+
callbacks = RemoteCallbacks()
18+
19+
if opts is None:
20+
opts = ffi.new('git_fetch_options *')
21+
C.git_fetch_init_options(opts, C.GIT_FETCH_OPTIONS_VERSION)
22+
23+
# Plug callbacks
24+
opts.callbacks.sideband_progress = C._sideband_progress_cb
25+
opts.callbacks.transfer_progress = C._transfer_progress_cb
26+
opts.callbacks.update_tips = C._update_tips_cb
27+
opts.callbacks.credentials = C._credentials_cb
28+
opts.callbacks.certificate_check = C._certificate_cb
29+
# Payload
30+
callbacks._stored_exception = None
31+
handle = ffi.new_handle(callbacks)
32+
opts.callbacks.payload = handle
33+
34+
# Give back control
35+
yield opts, callbacks
36+
37+
38+
@contextmanager
39+
def git_push_options(callbacks, opts=None):
40+
from .remote import RemoteCallbacks
41+
if callbacks is None:
42+
callbacks = RemoteCallbacks()
43+
44+
if opts is None:
45+
opts = ffi.new('git_push_options *')
46+
C.git_push_init_options(opts, C.GIT_PUSH_OPTIONS_VERSION)
47+
48+
# Plug callbacks
49+
opts.callbacks.sideband_progress = C._sideband_progress_cb
50+
opts.callbacks.transfer_progress = C._transfer_progress_cb
51+
opts.callbacks.update_tips = C._update_tips_cb
52+
opts.callbacks.credentials = C._credentials_cb
53+
opts.callbacks.certificate_check = C._certificate_cb
54+
opts.callbacks.push_update_reference = C._push_update_reference_cb
55+
# Payload
56+
callbacks._stored_exception = None
57+
handle = ffi.new_handle(callbacks)
58+
opts.callbacks.payload = handle
59+
60+
# Give back control
61+
yield opts, callbacks
62+
63+
64+
@contextmanager
65+
def git_remote_callbacks(callbacks):
66+
from .remote import RemoteCallbacks
67+
if callbacks is None:
68+
callbacks = RemoteCallbacks()
69+
70+
cdata = ffi.new('git_remote_callbacks *')
71+
C.git_remote_init_callbacks(cdata, C.GIT_REMOTE_CALLBACKS_VERSION)
72+
73+
# Plug callbacks
74+
cdata.credentials = C._credentials_cb
75+
cdata.update_tips = C._update_tips_cb
76+
# Payload
77+
callbacks._stored_exception = None
78+
handle = ffi.new_handle(callbacks)
79+
cdata.payload = handle
80+
81+
# Give back control
82+
yield cdata, callbacks

pygit2/errors.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@
3030

3131
value_errors = set([C.GIT_EEXISTS, C.GIT_EINVALIDSPEC, C.GIT_EAMBIGUOUS])
3232

33-
def check_error(err, io=False):
33+
def check_error(err, cb=None, io=False):
34+
if err == C.GIT_EUSER:
35+
assert cb is not None
36+
assert cb._stored_exception is not None
37+
raise cb._stored_exception
38+
3439
if err >= 0:
3540
return
3641

pygit2/remote.py

+21-93
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
# Import from pygit2
2727
from ._pygit2 import Oid
28+
from .callbacks import git_fetch_options, git_push_options, git_remote_callbacks
2829
from .errors import check_error, Passthrough
2930
from .ffi import ffi, C
3031
from .refspec import Refspec
@@ -182,41 +183,6 @@ def push_update_reference(self, refname, message):
182183
Rejection message from the remote. If None, the update was accepted.
183184
"""
184185

185-
def _fill_fetch_options(self, fetch_opts):
186-
fetch_opts.callbacks.sideband_progress = C._sideband_progress_cb
187-
fetch_opts.callbacks.transfer_progress = C._transfer_progress_cb
188-
fetch_opts.callbacks.update_tips = C._update_tips_cb
189-
fetch_opts.callbacks.credentials = C._credentials_cb
190-
fetch_opts.callbacks.certificate_check = C._certificate_cb
191-
# We need to make sure that this handle stays alive
192-
self._self_handle = ffi.new_handle(self)
193-
fetch_opts.callbacks.payload = self._self_handle
194-
195-
self._stored_exception = None
196-
197-
def _fill_push_options(self, push_opts):
198-
push_opts.callbacks.sideband_progress = C._sideband_progress_cb
199-
push_opts.callbacks.transfer_progress = C._transfer_progress_cb
200-
push_opts.callbacks.update_tips = C._update_tips_cb
201-
push_opts.callbacks.credentials = C._credentials_cb
202-
push_opts.callbacks.certificate_check = C._certificate_cb
203-
push_opts.callbacks.push_update_reference = C._push_update_reference_cb
204-
# We need to make sure that this handle stays alive
205-
self._self_handle = ffi.new_handle(self)
206-
push_opts.callbacks.payload = self._self_handle
207-
208-
def _fill_prune_callbacks(self, prune_callbacks):
209-
prune_callbacks.update_tips = C._update_tips_cb
210-
# We need to make sure that this handle stays alive
211-
self._self_handle = ffi.new_handle(self)
212-
prune_callbacks.payload = self._self_handle
213-
214-
def _fill_connect_callbacks(self, connect_callbacks):
215-
connect_callbacks.credentials = C._credentials_cb
216-
# We need to make sure that this handle stays alive
217-
self._self_handle = ffi.new_handle(self)
218-
connect_callbacks.payload = self._self_handle
219-
220186

221187
# These functions exist to be called by the git_remote as callbacks. They proxy
222188
# the call to whatever the user set
@@ -374,21 +340,15 @@ def push_url(self):
374340
return maybe_string(C.git_remote_pushurl(self._remote))
375341

376342
def connect(self, callbacks=None, direction=C.GIT_DIRECTION_FETCH):
377-
"""Connect to the remote."""
378-
379-
remote_callbacks = ffi.new('git_remote_callbacks *')
380-
C.git_remote_init_callbacks(remote_callbacks, C.GIT_REMOTE_CALLBACKS_VERSION)
381-
382-
if callbacks is None:
383-
callbacks = RemoteCallbacks()
384-
callbacks._fill_connect_callbacks(remote_callbacks)
385-
386-
err = C.git_remote_connect(self._remote, direction, remote_callbacks, ffi.NULL, ffi.NULL);
387-
check_error(err)
343+
"""Connect to the remote.
344+
"""
345+
with git_remote_callbacks(callbacks) as (remote_callbacks, cb):
346+
err = C.git_remote_connect(self._remote, direction, remote_callbacks, ffi.NULL, ffi.NULL)
347+
check_error(err, cb)
388348

389349
def save(self):
390-
"""Save a remote to its repository's configuration."""
391-
350+
"""Save a remote to its repository's configuration.
351+
"""
392352
err = C.git_remote_save(self._remote)
393353
check_error(err)
394354

@@ -405,26 +365,12 @@ def fetch(self, refspecs=None, message=None, callbacks=None, prune=C.GIT_FETCH_P
405365
repository that does not exist in the remote and the last will
406366
always keep the remote branches
407367
"""
408-
409368
message = to_bytes(message)
410-
411-
opts = ffi.new('git_fetch_options *')
412-
err = C.git_fetch_init_options(opts, C.GIT_FETCH_OPTIONS_VERSION)
413-
414-
if callbacks is None:
415-
callbacks = RemoteCallbacks()
416-
callbacks._fill_fetch_options(opts)
417-
418-
opts.prune = prune
419-
420-
try:
369+
with git_fetch_options(callbacks) as (opts, cb):
370+
opts.prune = prune
421371
with StrArray(refspecs) as arr:
422-
err = C.git_remote_fetch(self._remote, arr, opts, message)
423-
if callbacks._stored_exception:
424-
raise callbacks._stored_exception
425-
check_error(err)
426-
finally:
427-
callbacks._self_handle = None
372+
err = C.git_remote_fetch(self._remote, arr, opts, to_bytes(message))
373+
check_error(err, cb)
428374

429375
return TransferProgress(C.git_remote_stats(self._remote))
430376

@@ -443,11 +389,8 @@ def ls_remotes(self, callbacks=None):
443389
check_error(err)
444390

445391
results = []
446-
447392
for i in range(int(refs_len[0])):
448-
449393
local = bool(refs[0][i].local)
450-
451394
if local:
452395
loid = Oid(raw=bytes(ffi.buffer(refs[0][i].loid.id)[:]))
453396
else:
@@ -466,17 +409,11 @@ def ls_remotes(self, callbacks=None):
466409
return results
467410

468411
def prune(self, callbacks=None):
469-
"""Perform a prune against this remote."""
470-
471-
remote_callbacks = ffi.new('git_remote_callbacks *')
472-
C.git_remote_init_callbacks(remote_callbacks, C.GIT_REMOTE_CALLBACKS_VERSION)
473-
474-
if callbacks is None:
475-
callbacks = RemoteCallbacks()
476-
callbacks._fill_prune_callbacks(remote_callbacks)
477-
478-
err = C.git_remote_prune(self._remote, remote_callbacks)
479-
check_error(err)
412+
"""Perform a prune against this remote.
413+
"""
414+
with git_remote_callbacks(callbacks) as (remote_callbacks, cb):
415+
err = C.git_remote_prune(self._remote, remote_callbacks)
416+
check_error(err, cb)
480417

481418
@property
482419
def refspec_count(self):
@@ -525,20 +462,11 @@ def push(self, specs, callbacks=None):
525462
specs : [str]
526463
Push refspecs to use.
527464
"""
528-
push_opts = ffi.new('git_push_options *')
529-
err = C.git_push_init_options(push_opts, C.GIT_PUSH_OPTIONS_VERSION)
530-
531-
if callbacks is None:
532-
callbacks = RemoteCallbacks()
533-
callbacks._fill_push_options(push_opts)
534-
535-
# Build custom callback structure
536-
try:
465+
with git_push_options(callbacks) as (opts, cb):
537466
with StrArray(specs) as refspecs:
538-
err = C.git_remote_push(self._remote, refspecs, push_opts)
539-
check_error(err)
540-
finally:
541-
callbacks._self_handle = None
467+
err = C.git_remote_push(self._remote, refspecs, opts)
468+
check_error(err, cb)
469+
542470

543471
def get_credentials(fn, url, username, allowed):
544472
"""Call fn and return the credentials object"""

pygit2/repository.py

+8-10
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,12 @@
4040
from ._pygit2 import Reference, Tree, Commit, Blob
4141
from ._pygit2 import InvalidSpecError
4242

43+
from .callbacks import git_fetch_options
4344
from .config import Config
4445
from .errors import check_error
4546
from .ffi import ffi, C
4647
from .index import Index
47-
from .remote import RemoteCollection, RemoteCallbacks
48+
from .remote import RemoteCollection
4849
from .blame import Blame
4950
from .utils import to_bytes, StrArray
5051
from .submodule import Submodule
@@ -101,15 +102,12 @@ def update_submodules(self, submodules=None, init=False, callbacks=None):
101102
opts = ffi.new('git_submodule_update_options *')
102103
C.git_submodule_update_init_options(opts, C.GIT_SUBMODULE_UPDATE_OPTIONS_VERSION)
103104

104-
if callbacks is None:
105-
callbacks = RemoteCallbacks()
106-
callbacks._fill_fetch_options(opts.fetch_opts)
107-
108-
i = 1 if init else 0
109-
for submodule in submodules:
110-
submodule_instance = self.lookup_submodule(submodule)
111-
err = C.git_submodule_update(submodule_instance._subm, i, opts)
112-
check_error(err)
105+
with git_fetch_options(callbacks, opts=opts.fetch_opts) as (_, cb):
106+
i = 1 if init else 0
107+
for submodule in submodules:
108+
submodule_instance = self.lookup_submodule(submodule)
109+
err = C.git_submodule_update(submodule_instance._subm, i, opts)
110+
check_error(err, cb)
113111

114112
return None
115113

0 commit comments

Comments
 (0)