From 9c5e44e5854e298035b369d25e7f0a1211a20dc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 6 Mar 2026 21:08:41 +0000 Subject: [PATCH 1/2] Default to python3 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 90e7cb0e8..0df116c20 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -PYTHON = python +PYTHON = python3 FLAKE8 ?= flake8 PYDOCTOR = pydoctor PYDOCTOR_OPTIONS ?= From dfd52302dc2910584f272e0f0b66e04041ba6855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Mon, 9 Mar 2026 22:09:59 +0000 Subject: [PATCH 2/2] Fix multiple bugs in C bindings _ra.c: - Fix NULL dereference in pyify_lock when lock is NULL (e.g. during unlock) - Fix GIL handling in py_revstart_cb/py_revfinish_cb: acquire GIL before calling Python API functions - Fix memory leak: Py_DECREF py_revprops in replay callbacks - Fix uninitialized variable idx in ra_unlock - Convert do_update, do_switch, do_diff, replay, replay_range, change_rev_prop, mergeinfo, get_file_revs to METH_VARARGS|METH_KEYWORDS client.c: - Fix client_export format string: native_eol used 'b' instead of 'z' - Fix client_list kwnames: missing "include_externals" entry - Fix client_cat kwnames: missing "expand_keywords" entry - Fix client_update format string: missing 'b' for make_parents - Convert client_delete, client_propset, client_propget to support keywords repos.c: - Fix GIL bug in py_pack_notify: acquire GIL before calling Python API - Convert repos_create to METH_VARARGS|METH_KEYWORDS wc.c: - Fix get_update_editor: missing &diff3_cmd in PyArg_ParseTupleAndKeywords, causing all subsequent parameters to be shifted by one position - Fix crawl_revisions: missing &cancel variable for format specifier - Fix walk_status: extra 'O' format specifier with no matching variable --- subvertpy/_ra.c | 95 ++++++++++++++++++++++++++++------------------ subvertpy/client.c | 38 +++++++++++-------- subvertpy/repos.c | 14 +++++-- subvertpy/wc.c | 8 ++-- 4 files changed, 95 insertions(+), 60 deletions(-) diff --git a/subvertpy/_ra.c b/subvertpy/_ra.c index 04e1ab99d..5cac9a831 100644 --- a/subvertpy/_ra.c +++ b/subvertpy/_ra.c @@ -76,6 +76,9 @@ static svn_error_t *py_commit_callback(const svn_commit_info_t *commit_info, voi static PyObject *pyify_lock(const svn_lock_t *lock) { + if (lock == NULL) { + Py_RETURN_NONE; + } return Py_BuildValue("(ssszbLL)", lock->path, lock->token, lock->owner, lock->comment, @@ -809,8 +812,9 @@ static PyObject *ra_get_session_url(PyObject *self) -static PyObject *ra_do_update(PyObject *self, PyObject *args) +static PyObject *ra_do_update(PyObject *self, PyObject *args, PyObject *kwargs) { + char *kwnames[] = { "revision_to_update_to", "update_target", "recurse", "update_editor", "send_copyfrom_args", "ignore_ancestry", NULL }; svn_revnum_t revision_to_update_to; char *update_target; bool recurse; @@ -824,7 +828,7 @@ static PyObject *ra_do_update(PyObject *self, PyObject *args) RemoteAccessObject *ra = (RemoteAccessObject *)self; bool send_copyfrom_args = false; - if (!PyArg_ParseTuple(args, "lsbO|bb:do_update", &revision_to_update_to, &update_target, &recurse, &update_editor, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "lsbO|bb:do_update", kwnames, &revision_to_update_to, &update_target, &recurse, &update_editor, &send_copyfrom_args, &ignore_ancestry)) return NULL; @@ -878,8 +882,9 @@ static PyObject *ra_do_update(PyObject *self, PyObject *args) return (PyObject *)ret; } -static PyObject *ra_do_switch(PyObject *self, PyObject *args) +static PyObject *ra_do_switch(PyObject *self, PyObject *args, PyObject *kwargs) { + char *kwnames[] = { "revision_to_update_to", "update_target", "recurse", "switch_url", "update_editor", "send_copyfrom_args", "ignore_ancestry", NULL }; RemoteAccessObject *ra = (RemoteAccessObject *)self; svn_revnum_t revision_to_update_to; char *update_target; @@ -895,7 +900,7 @@ static PyObject *ra_do_switch(PyObject *self, PyObject *args) PyObject *py_switch_url; svn_error_t *err; - if (!PyArg_ParseTuple(args, "lsbOO|bb:do_switch", &revision_to_update_to, &update_target, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "lsbOO|bb:do_switch", kwnames, &revision_to_update_to, &update_target, &recurse, &py_switch_url, &update_editor, &send_copyfrom_args, &ignore_ancestry)) return NULL; if (ra_check_busy(ra)) @@ -955,7 +960,7 @@ static PyObject *ra_do_switch(PyObject *self, PyObject *args) return (PyObject *)ret; } -static PyObject *ra_do_diff(PyObject *self, PyObject *args) +static PyObject *ra_do_diff(PyObject *self, PyObject *args, PyObject *kwargs) { svn_revnum_t revision_to_update_to; char *diff_target, *versus_url; @@ -967,8 +972,12 @@ static PyObject *ra_do_diff(PyObject *self, PyObject *args) bool ignore_ancestry = false, text_deltas = false, recurse=true; ReporterObject *ret; RemoteAccessObject *ra = (RemoteAccessObject *)self; + char *kwnames[] = { "revision_to_update_to", "diff_target", "versus_url", + "diff_editor", "recurse", "ignore_ancestry", "text_deltas", NULL }; - if (!PyArg_ParseTuple(args, "lssO|bbb:do_diff", &revision_to_update_to, &diff_target, &versus_url, &diff_editor, &recurse, &ignore_ancestry, &text_deltas)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "lssO|bbb:do_diff", kwnames, + &revision_to_update_to, &diff_target, &versus_url, &diff_editor, + &recurse, &ignore_ancestry, &text_deltas)) return NULL; if (ra_check_busy(ra)) @@ -1008,15 +1017,18 @@ static PyObject *ra_do_diff(PyObject *self, PyObject *args) return (PyObject *)ret; } -static PyObject *ra_replay(PyObject *self, PyObject *args) +static PyObject *ra_replay(PyObject *self, PyObject *args, PyObject *kwargs) { RemoteAccessObject *ra = (RemoteAccessObject *)self; apr_pool_t *temp_pool; svn_revnum_t revision, low_water_mark; PyObject *update_editor; bool send_deltas = true; + char *kwnames[] = { "revision", "low_water_mark", "update_editor", + "send_deltas", NULL }; - if (!PyArg_ParseTuple(args, "llO|b:replay", &revision, &low_water_mark, &update_editor, &send_deltas)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "llO|b:replay", kwnames, + &revision, &low_water_mark, &update_editor, &send_deltas)) return NULL; if (ra_check_busy(ra)) @@ -1039,13 +1051,15 @@ static PyObject *ra_replay(PyObject *self, PyObject *args) static svn_error_t *py_revstart_cb(svn_revnum_t revision, void *replay_baton, const svn_delta_editor_t **editor, void **edit_baton, apr_hash_t *rev_props, apr_pool_t *pool) { - PyObject *cbs = (PyObject *)replay_baton; - PyObject *py_start_fn = PyTuple_GetItem(cbs, 0); - PyObject *py_revprops = prop_hash_to_dict(rev_props); - PyObject *ret; + PyObject *cbs, *py_start_fn, *py_revprops, *ret; PyGILState_STATE state = PyGILState_Ensure(); + cbs = (PyObject *)replay_baton; + py_start_fn = PyTuple_GetItem(cbs, 0); + py_revprops = prop_hash_to_dict(rev_props); + ret = PyObject_CallFunction(py_start_fn, "lO", revision, py_revprops); + Py_DECREF(py_revprops); CB_CHECK_PYRETVAL(ret); *editor = &py_editor; @@ -1059,13 +1073,15 @@ static svn_error_t *py_revfinish_cb(svn_revnum_t revision, void *replay_baton, const svn_delta_editor_t *editor, void *edit_baton, apr_hash_t *rev_props, apr_pool_t *pool) { - PyObject *cbs = (PyObject *)replay_baton; - PyObject *py_finish_fn = PyTuple_GetItem(cbs, 1); - PyObject *py_revprops = prop_hash_to_dict(rev_props); - PyObject *ret; + PyObject *cbs, *py_finish_fn, *py_revprops, *ret; PyGILState_STATE state = PyGILState_Ensure(); + cbs = (PyObject *)replay_baton; + py_finish_fn = PyTuple_GetItem(cbs, 1); + py_revprops = prop_hash_to_dict(rev_props); + ret = PyObject_CallFunction(py_finish_fn, "lOO", revision, py_revprops, edit_baton); + Py_DECREF(py_revprops); CB_CHECK_PYRETVAL(ret); Py_DECREF((PyObject *)edit_baton); @@ -1075,15 +1091,16 @@ static svn_error_t *py_revfinish_cb(svn_revnum_t revision, void *replay_baton, return NULL; } -static PyObject *ra_replay_range(PyObject *self, PyObject *args) +static PyObject *ra_replay_range(PyObject *self, PyObject *args, PyObject *kwargs) { + char *kwnames[] = { "start_revision", "end_revision", "low_water_mark", "cbs", "send_deltas", NULL }; RemoteAccessObject *ra = (RemoteAccessObject *)self; apr_pool_t *temp_pool; svn_revnum_t start_revision, end_revision, low_water_mark; PyObject *cbs; bool send_deltas = true; - if (!PyArg_ParseTuple(args, "lllO|b:replay_range", &start_revision, &end_revision, &low_water_mark, &cbs, &send_deltas)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "lllO|b:replay_range", kwnames, &start_revision, &end_revision, &low_water_mark, &cbs, &send_deltas)) return NULL; if (!PyTuple_Check(cbs)) { @@ -1203,7 +1220,7 @@ static PyObject *get_commit_editor(PyObject *self, PyObject *args, PyObject *kwa return NULL; } -static PyObject *ra_change_rev_prop(PyObject *self, PyObject *args) +static PyObject *ra_change_rev_prop(PyObject *self, PyObject *args, PyObject *kwargs) { svn_revnum_t rev; char *name; @@ -1214,8 +1231,10 @@ static PyObject *ra_change_rev_prop(PyObject *self, PyObject *args) svn_string_t *val_string; const svn_string_t *old_val_string; const svn_string_t *const *old_val_string_p; + char *kwnames[] = { "revnum", "name", "value", "old_value", NULL }; - if (!PyArg_ParseTuple(args, "lss#|z#:change_rev_prop", &rev, &name, &value, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "lss#|z#:change_rev_prop", + kwnames, &rev, &name, &value, &vallen, &oldvalue, &oldvallen)) return NULL; if (ra_check_busy(ra)) @@ -1491,7 +1510,7 @@ static PyObject *ra_unlock(PyObject *self, PyObject *args) RemoteAccessObject *ra = (RemoteAccessObject *)self; PyObject *path_tokens, *lock_func, *k, *v; bool break_lock; - Py_ssize_t idx; + Py_ssize_t idx = 0; apr_pool_t *temp_pool; apr_hash_t *hash_path_tokens; @@ -1784,8 +1803,9 @@ static PyObject *mergeinfo_to_dict(svn_mergeinfo_t mergeinfo, apr_pool_t *temp_p return ret; } -static PyObject *ra_mergeinfo(PyObject *self, PyObject *args) +static PyObject *ra_mergeinfo(PyObject *self, PyObject *args, PyObject *kwargs) { + char *kwnames[] = { "paths", "revision", "inherit", "include_descendants", NULL }; RemoteAccessObject *ra = (RemoteAccessObject *)self; apr_array_header_t *apr_paths; apr_pool_t *temp_pool; @@ -1800,7 +1820,7 @@ static PyObject *ra_mergeinfo(PyObject *self, PyObject *args) svn_mergeinfo_inheritance_t inherit = svn_mergeinfo_explicit; bool include_descendants; - if (!PyArg_ParseTuple(args, "O|lib:mergeinfo", &paths, &revision, &inherit, &include_descendants)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|lib:mergeinfo", kwnames, &paths, &revision, &inherit, &include_descendants)) return NULL; temp_pool = Pool(NULL); @@ -1896,8 +1916,9 @@ static PyObject *ra_get_location_segments(PyObject *self, PyObject *args) } -static PyObject *ra_get_file_revs(PyObject *self, PyObject *args) +static PyObject *ra_get_file_revs(PyObject *self, PyObject *args, PyObject *kwargs) { + char *kwnames[] = { "path", "start", "end", "file_rev_handler", "include_merged_revisions", NULL }; char *path; svn_revnum_t start, end; PyObject *file_rev_handler; @@ -1905,7 +1926,7 @@ static PyObject *ra_get_file_revs(PyObject *self, PyObject *args) RemoteAccessObject *ra = (RemoteAccessObject *)self; bool include_merged_revisions = false; - if (!PyArg_ParseTuple(args, "sllO|b:get_file_revs", &path, &start, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sllO|b:get_file_revs", kwnames, &path, &start, &end, &file_rev_handler, &include_merged_revisions)) return NULL; @@ -1965,8 +1986,8 @@ static PyGetSetDef ra_getsetters[] = { static PyMethodDef ra_methods[] = { { "get_session_url", (PyCFunction)ra_get_session_url, METH_NOARGS, "S.get_session_url() -> url" }, - { "get_file_revs", ra_get_file_revs, METH_VARARGS, - "S.get_file_revs(path, start_rev, end_revs, handler)" }, + { "get_file_revs", (PyCFunction)ra_get_file_revs, METH_VARARGS|METH_KEYWORDS, + "S.get_file_revs(path, start_rev, end_revs, handler, include_merged_revisions=False)" }, { "get_locations", ra_get_locations, METH_VARARGS, "S.get_locations(path, peg_revision, location_revisions)" }, { "get_locks", ra_get_locks, METH_VARARGS, @@ -1975,8 +1996,8 @@ static PyMethodDef ra_methods[] = { "S.lock(path_revs, comment, steal_lock, lock_func)\n" }, { "unlock", ra_unlock, METH_VARARGS, "S.unlock(path_tokens, break_lock, lock_func)\n" }, - { "mergeinfo", ra_mergeinfo, METH_VARARGS, - "S.mergeinfo(paths, revision, inherit, include_descendants)\n" }, + { "mergeinfo", (PyCFunction)ra_mergeinfo, METH_VARARGS|METH_KEYWORDS, + "S.mergeinfo(paths, revision=-1, inherit=MERGEINFO_EXPLICIT, include_descendants=False)\n" }, { "get_location_segments", ra_get_location_segments, METH_VARARGS, "S.get_location_segments(path, peg_revision, start_revision, " "end_revision, rcvr)\n" @@ -1999,8 +2020,8 @@ static PyMethodDef ra_methods[] = { { "get_file", ra_get_file, METH_VARARGS, "S.get_file(path, stream, revnum=-1) -> (fetched_rev, properties)\n" "Fetch a file. The contents will be written to stream." }, - { "change_rev_prop", ra_change_rev_prop, METH_VARARGS, - "S.change_rev_prop(revnum, name, value)\n" + { "change_rev_prop", (PyCFunction)ra_change_rev_prop, METH_VARARGS|METH_KEYWORDS, + "S.change_rev_prop(revnum, name, value, old_value=None)\n" "Change a revision property" }, { "get_commit_editor", (PyCFunction)get_commit_editor, METH_VARARGS|METH_KEYWORDS, "S.get_commit_editor(revprops, commit_callback, lock_tokens, keep_locks) -> editor\n" @@ -2008,22 +2029,22 @@ static PyMethodDef ra_methods[] = { { "rev_proplist", ra_rev_proplist, METH_VARARGS, "S.rev_proplist(revnum) -> properties\n" "Return a dictionary with the properties set on the specified revision" }, - { "replay", ra_replay, METH_VARARGS, + { "replay", (PyCFunction)ra_replay, METH_VARARGS|METH_KEYWORDS, "S.replay(revision, low_water_mark, update_editor, send_deltas=True)\n" "Replay a revision, reporting changes to update_editor." }, - { "replay_range", ra_replay_range, METH_VARARGS, + { "replay_range", (PyCFunction)ra_replay_range, METH_VARARGS|METH_KEYWORDS, "S.replay_range(start_rev, end_rev, low_water_mark, cbs, send_deltas=True)\n" "Replay a range of revisions, reporting them to an update editor.\n" "cbs is a two-tuple with two callbacks:\n" "- start_rev_cb(revision, revprops) -> editor\n" "- finish_rev_cb(revision, revprops, editor)\n" }, - { "do_switch", ra_do_switch, METH_VARARGS, + { "do_switch", (PyCFunction)ra_do_switch, METH_VARARGS|METH_KEYWORDS, "S.do_switch(revision_to_update_to, update_target, recurse, switch_url, update_editor, send_copyfrom_args=False, ignore_ancestry=True)\n" }, - { "do_update", ra_do_update, METH_VARARGS, + { "do_update", (PyCFunction)ra_do_update, METH_VARARGS|METH_KEYWORDS, "S.do_update(revision_to_update_to, update_target, recurse, update_editor, send_copyfrom_args=False, ignore_ancestry=True)\n" }, - { "do_diff", ra_do_diff, METH_VARARGS, - "S.do_diff(revision_to_update_to, diff_target, versus_url, diff_editor, recurse, ignore_ancestry, text_deltas) -> Reporter object\n" + { "do_diff", (PyCFunction)ra_do_diff, METH_VARARGS|METH_KEYWORDS, + "S.do_diff(revision_to_update_to, diff_target, versus_url, diff_editor, recurse=True, ignore_ancestry=False, text_deltas=False) -> Reporter object\n" }, { "get_repos_root", (PyCFunction)ra_get_repos_root, METH_NOARGS, "S.get_repos_root() -> url\n" diff --git a/subvertpy/client.c b/subvertpy/client.c index d2253cf46..43a34239b 100644 --- a/subvertpy/client.c +++ b/subvertpy/client.c @@ -738,7 +738,7 @@ static PyObject *client_export(PyObject *self, PyObject *args, PyObject *kwargs) PyObject *peg_rev=Py_None, *rev=Py_None; bool recurse=true, ignore_externals=false, overwrite=false, ignore_keywords=false; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|OObbbbb", kwnames, &py_from, &py_to, &rev, &peg_rev, &recurse, &ignore_externals, &overwrite, &native_eol, &ignore_keywords)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|OObbbzb", kwnames, &py_from, &py_to, &rev, &peg_rev, &recurse, &ignore_externals, &overwrite, &native_eol, &ignore_keywords)) return NULL; if (!to_opt_revision(peg_rev, &c_peg_rev)) @@ -774,7 +774,7 @@ static PyObject *client_export(PyObject *self, PyObject *args, PyObject *kwargs) static PyObject *client_cat(PyObject *self, PyObject *args, PyObject *kwargs) { ClientObject *client = (ClientObject *)self; - char *kwnames[] = { "path", "output_stream", "revision", "peg_revision", NULL }; + char *kwnames[] = { "path", "output_stream", "revision", "peg_revision", "expand_keywords", NULL }; char *path; PyObject *peg_rev=Py_None, *rev=Py_None; svn_opt_revision_t c_peg_rev, c_rev; @@ -823,7 +823,7 @@ static PyObject *client_cat(PyObject *self, PyObject *args, PyObject *kwargs) return ret; } -static PyObject *client_delete(PyObject *self, PyObject *args) +static PyObject *client_delete(PyObject *self, PyObject *args, PyObject *kwargs) { PyObject *paths; bool force=false, keep_local=false; @@ -833,8 +833,9 @@ static PyObject *client_delete(PyObject *self, PyObject *args) ClientObject *client = (ClientObject *)self; apr_hash_t *hash_revprops; PyObject *callback = Py_None; + char *kwnames[] = { "paths", "force", "keep_local", "revprops", "callback", NULL }; - if (!PyArg_ParseTuple(args, "O|bbOO", &paths, &force, &keep_local, &py_revprops, &callback)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|bbOO", kwnames, &paths, &force, &keep_local, &py_revprops, &callback)) return NULL; temp_pool = Pool(NULL); @@ -973,7 +974,7 @@ static PyObject *client_copy(PyObject *self, PyObject *args, PyObject *kwargs) Py_RETURN_NONE; } -static PyObject *client_propset(PyObject *self, PyObject *args) +static PyObject *client_propset(PyObject *self, PyObject *args, PyObject *kwargs) { char *propname; svn_string_t c_propval; @@ -987,8 +988,11 @@ static PyObject *client_propset(PyObject *self, PyObject *args) svn_revnum_t base_revision_for_url = SVN_INVALID_REVNUM; apr_hash_t *revprops; svn_commit_info_t *commit_info = NULL; + char *kwnames[] = { "name", "value", "target", "recurse", "skip_checks", + "base_revision_for_url", "revprops", NULL }; - if (!PyArg_ParseTuple(args, "sz#s|bblO", &propname, &c_propval.data, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sz#s|bblO", kwnames, + &propname, &c_propval.data, &vallen, &target, &recurse, &skip_checks, &base_revision_for_url, &py_revprops)) return NULL; @@ -1020,7 +1024,7 @@ static PyObject *client_propset(PyObject *self, PyObject *args) return ret; } -static PyObject *client_propget(PyObject *self, PyObject *args) +static PyObject *client_propget(PyObject *self, PyObject *args, PyObject *kwargs) { svn_opt_revision_t c_peg_rev; svn_opt_revision_t c_rev; @@ -1033,8 +1037,10 @@ static PyObject *client_propget(PyObject *self, PyObject *args) PyObject *revision; ClientObject *client = (ClientObject *)self; PyObject *ret, *py_target; + char *kwnames[] = { "name", "target", "peg_revision", "revision", "recurse", NULL }; - if (!PyArg_ParseTuple(args, "sOO|Ob", &propname, &py_target, &peg_revision, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sOO|Ob", kwnames, + &propname, &py_target, &peg_revision, &revision, &recurse)) return NULL; if (!to_opt_revision(peg_revision, &c_peg_rev)) @@ -1148,7 +1154,7 @@ static PyObject *client_update(PyObject *self, PyObject *args, PyObject *kwargs) "allow_unver_obstructions", "adds_as_modification", "make_parents", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Obbbbb", kwnames, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Obbbbbb", kwnames, &paths, &rev, &recurse, &ignore_externals, &depth_is_sticky, &allow_unver_obstructions, &adds_as_modification, &make_parents)) @@ -1189,7 +1195,7 @@ static PyObject *client_update(PyObject *self, PyObject *args, PyObject *kwargs) static PyObject *client_list(PyObject *self, PyObject *args, PyObject *kwargs) { char *kwnames[] = - { "path", "peg_revision", "depth", "dirents", "revision", NULL }; + { "path", "peg_revision", "depth", "dirents", "revision", "include_externals", NULL }; svn_opt_revision_t c_peg_rev; svn_opt_revision_t c_rev; int depth; @@ -1542,10 +1548,10 @@ static PyMethodDef client_methods[] = { { "cat", (PyCFunction)client_cat, METH_VARARGS|METH_KEYWORDS, "S.cat(path, output_stream, revision=None, peg_revision=None)" }, { "commit", (PyCFunction)client_commit, METH_VARARGS|METH_KEYWORDS, "S.commit(targets, recurse=True, keep_locks=True, revprops=None, keep_changelist=False, commit_as_operations=False, include_file_externals=False, include_dir_externals=False, callback=None) -> (revnum, date, author)" }, - { "delete", client_delete, METH_VARARGS, "S.delete(paths, force=False)" }, + { "delete", (PyCFunction)client_delete, METH_VARARGS|METH_KEYWORDS, "S.delete(paths, force=False, keep_local=False, revprops=None, callback=None)" }, { "copy", (PyCFunction)client_copy, METH_VARARGS|METH_KEYWORDS, "S.copy(src_path, dest_path, srv_rev=None)" }, - { "propset", client_propset, METH_VARARGS, "S.propset(name, value, target, recurse=True, skip_checks=False)" }, - { "propget", client_propget, METH_VARARGS, "S.propget(name, target, peg_revision, revision=None, recurse=False) -> value" }, + { "propset", (PyCFunction)client_propset, METH_VARARGS|METH_KEYWORDS, "S.propset(name, value, target, recurse=True, skip_checks=False)" }, + { "propget", (PyCFunction)client_propget, METH_VARARGS|METH_KEYWORDS, "S.propget(name, target, peg_revision, revision=None, recurse=False) -> value" }, { "proplist", (PyCFunction)client_proplist, METH_VARARGS|METH_KEYWORDS, "S.proplist(path, peg_revision, depth, revision=None)" }, { "resolve", client_resolve, METH_VARARGS, "S.resolve(path, depth, choice)" }, { "update", (PyCFunction)client_update, METH_VARARGS|METH_KEYWORDS, "S.update(path, rev=None, recurse=True, ignore_externals=False) -> list of revnums" }, @@ -1556,10 +1562,10 @@ static PyMethodDef client_methods[] = { "S.log(callback, paths, start_rev=None, end_rev=None, limit=0, peg_revision=None, discover_changed_paths=False, strict_node_history=False, include_merged_revisions=False, revprops=None)" }, { "info", (PyCFunction)client_info, METH_VARARGS|METH_KEYWORDS, "S.info(path, revision=None, peg_revision=None, depth=DEPTH_EMPTY) -> dict of info entries" }, - { "lock", (PyCFunction)client_lock, METH_VARARGS, + { "lock", (PyCFunction)client_lock, METH_VARARGS|METH_KEYWORDS, "S.lock(targets, comment, steal_lock=False)" }, - { "unlock", (PyCFunction)client_unlock, METH_VARARGS, - "S.lock(targets, break_lock=False)" }, + { "unlock", (PyCFunction)client_unlock, METH_VARARGS|METH_KEYWORDS, + "S.unlock(targets, break_lock=False)" }, { NULL, } }; diff --git a/subvertpy/repos.c b/subvertpy/repos.c index daf78eafe..955c6937a 100644 --- a/subvertpy/repos.c +++ b/subvertpy/repos.c @@ -37,8 +37,9 @@ typedef struct { svn_repos_t *repos; } RepositoryObject; -static PyObject *repos_create(PyObject *self, PyObject *args) +static PyObject *repos_create(PyObject *self, PyObject *args, PyObject *kwargs) { + char *kwnames[] = { "path", "config", "fs_config", NULL }; const char *path; PyObject *config=Py_None, *fs_config=Py_None, *py_path; svn_repos_t *repos = NULL; @@ -46,7 +47,7 @@ static PyObject *repos_create(PyObject *self, PyObject *args) apr_hash_t *hash_config, *hash_fs_config; RepositoryObject *ret; - if (!PyArg_ParseTuple(args, "O|OO:create", &py_path, &config, &fs_config)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OO:create", kwnames, &py_path, &config, &fs_config)) return NULL; pool = Pool(NULL); @@ -435,7 +436,7 @@ static PyObject *api_version(PyObject *self) } static PyMethodDef repos_module_methods[] = { - { "create", (PyCFunction)repos_create, METH_VARARGS, + { "create", (PyCFunction)repos_create, METH_VARARGS|METH_KEYWORDS, "create(path, config=None, fs_config=None)\n\n" "Create a new repository." }, { "delete", (PyCFunction)repos_delete, METH_VARARGS, @@ -501,12 +502,17 @@ static PyObject *repos_verify(RepositoryObject *self, PyObject *args) static svn_error_t *py_pack_notify(void *baton, apr_int64_t shard, svn_fs_pack_notify_action_t action, apr_pool_t *pool) { PyObject *ret; + PyGILState_STATE state; if (baton == Py_None) return NULL; + state = PyGILState_Ensure(); ret = PyObject_CallFunction((PyObject *)baton, "li", shard, action); - if (ret == NULL) + if (ret == NULL) { + PyGILState_Release(state); return py_svn_error(); + } Py_DECREF(ret); + PyGILState_Release(state); return NULL; } diff --git a/subvertpy/wc.c b/subvertpy/wc.c index bd920d26e..adc0a5642 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -1095,13 +1095,14 @@ static PyObject *py_wc_context_crawl_revisions(PyObject *self, PyObject *args, P bool honor_depth_exclude = true; bool depth_compatibility_trick = false; bool use_commit_times = false; + PyObject *cancel = Py_None; PyObject *notify = Py_None; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bibbbOO", kwnames, &py_path, &py_reporter, &restore_files, &depth, &honor_depth_exclude, &depth_compatibility_trick, - &use_commit_times, ¬ify)) { + &use_commit_times, &cancel, ¬ify)) { return NULL; } @@ -1169,7 +1170,8 @@ static PyObject *py_wc_context_get_update_editor(PyObject *self, PyObject *args, &allow_unver_obstructions, &adds_as_modification, &server_performs_filtering, - &clean_checkout, &py_preserved_exts, + &clean_checkout, &diff3_cmd, + &py_preserved_exts, &dirents_func, &conflict_func, &external_func, ¬ify_func)) { return NULL; @@ -1466,7 +1468,7 @@ static PyObject *py_wc_walk_status(PyObject *self, PyObject *args, PyObject *kwa apr_array_header_t *ignore_patterns; apr_pool_t *pool; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|ibbbOO", kwnames, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|ibbbO", kwnames, &py_path, &status_func, &depth, &get_all, &no_ignore, &ignore_text_mode, &py_ignore_patterns)) { return NULL;