From cde7126f0c625406068d6dd6c62dad841c6086f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Wed, 19 Jul 2017 23:44:54 +0000 Subject: Start on 0.10.2. --- NEWS | 2 ++ setup.py | 2 +- subvertpy/__init__.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 1ebd49b4..5e75b2e6 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,5 @@ +0.10.2 UNRELEASED + 0.10.1 2017-07-19 BUG FIXES diff --git a/setup.py b/setup.py index 44b9ec9f..de5f979d 100755 --- a/setup.py +++ b/setup.py @@ -412,7 +412,7 @@ def subvertpy_modules(): ] -subvertpy_version = (0, 10, 1) +subvertpy_version = (0, 10, 2) subvertpy_version_string = ".".join(map(str, subvertpy_version)) diff --git a/subvertpy/__init__.py b/subvertpy/__init__.py index 6b0762de..f36af65d 100644 --- a/subvertpy/__init__.py +++ b/subvertpy/__init__.py @@ -17,7 +17,7 @@ """Python bindings for Subversion.""" __author__ = "Jelmer Vernooij " -__version__ = (0, 10, 1) +__version__ = (0, 10, 2) NODE_DIR = 2 NODE_FILE = 1 -- cgit v1.2.3 From 01cfebd2e32b940ddfa55373640907b39da8413f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 05:39:25 +0000 Subject: Avoid deprecated svn_path_is_canonical. --- subvertpy/client.c | 607 ++++++++++++++++++++++++++--------------------------- subvertpy/util.c | 261 +++++++++++++---------- subvertpy/util.h | 5 + subvertpy/wc.c | 5 - 4 files changed, 452 insertions(+), 426 deletions(-) diff --git a/subvertpy/client.c b/subvertpy/client.c index 04b9ae57..5424ff03 100644 --- a/subvertpy/client.c +++ b/subvertpy/client.c @@ -73,12 +73,10 @@ typedef struct { static int client_set_auth(PyObject *self, PyObject *auth, void *closure); static int client_set_config(PyObject *self, PyObject *auth, void *closure); -static bool client_check_path(const char *path, apr_pool_t *scratch_pool) -{ - return svn_path_is_canonical(path, scratch_pool); -} - -static bool client_path_list_to_apr_array(apr_pool_t *pool, PyObject *l, apr_array_header_t **ret) +static bool client_list_to_apr_array( + apr_pool_t *pool, PyObject *l, + const char *(*convert)(PyObject*, apr_pool_t *), + apr_array_header_t **ret) { int i; const char *path; @@ -88,22 +86,17 @@ static bool client_path_list_to_apr_array(apr_pool_t *pool, PyObject *l, apr_arr } if (PyUnicode_Check(l) || PyBytes_Check(l)) { *ret = apr_array_make(pool, 1, sizeof(char *)); - path = py_object_to_svn_string(l, pool); + path = convert(l, pool); if (path == NULL) { return false; } - if (!client_check_path(path, pool)) { - PyErr_SetString(PyExc_ValueError, "Expected canonical path or URL"); - return false; - } APR_ARRAY_PUSH(*ret, const char *) = path; } else if (PyList_Check(l)) { *ret = apr_array_make(pool, PyList_Size(l), sizeof(char *)); for (i = 0; i < PyList_GET_SIZE(l); i++) { PyObject *item = PyList_GET_ITEM(l, i); - path = py_object_to_svn_string(item, pool); - if (!client_check_path(path, pool)) { - PyErr_SetString(PyExc_ValueError, "Expected canonical path or URL"); + path = convert(item, pool); + if (path == NULL) { return false; } APR_ARRAY_PUSH(*ret, const char *) = path; @@ -761,7 +754,7 @@ static PyObject *client_commit(PyObject *self, PyObject *args, PyObject *kwargs) if (temp_pool == NULL) { return NULL; } - if (!client_path_list_to_apr_array(temp_pool, targets, &apr_targets)) { + if (!client_list_to_apr_array(temp_pool, targets, py_object_to_svn_path_or_url, &apr_targets)) { apr_pool_destroy(temp_pool); return NULL; } @@ -812,10 +805,10 @@ static PyObject *client_export(PyObject *self, PyObject *args, PyObject *kwargs) char *kwnames[] = { "from", "to", "rev", "peg_rev", "recurse", "ignore_externals", "overwrite", "native_eol", "ignore_keywords", NULL }; svn_revnum_t result_rev; svn_opt_revision_t c_peg_rev, c_rev; - PyObject *py_from, *py_to; + PyObject *py_from, *py_to; const char *from, *to; apr_pool_t *temp_pool; - char *native_eol = NULL; + char *native_eol = NULL; PyObject *peg_rev=Py_None, *rev=Py_None; bool recurse=true, ignore_externals=false, overwrite=false, ignore_keywords=false; @@ -831,27 +824,27 @@ static PyObject *client_export(PyObject *self, PyObject *args, PyObject *kwargs) if (temp_pool == NULL) return NULL; - from = py_object_to_svn_string(py_from, temp_pool); - if (from == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } + from = py_object_to_svn_string(py_from, temp_pool); + if (from == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } - to = py_object_to_svn_dirent(py_to, temp_pool); - if (to == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } + to = py_object_to_svn_dirent(py_to, temp_pool); + if (to == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } #if ONLY_SINCE_SVN(1, 7) - RUN_SVN_WITH_POOL(temp_pool, svn_client_export5(&result_rev, from, to, + RUN_SVN_WITH_POOL(temp_pool, svn_client_export5(&result_rev, from, to, &c_peg_rev, &c_rev, overwrite, ignore_externals, ignore_keywords, recurse?svn_depth_infinity:svn_depth_files, native_eol, client->client, temp_pool)); #elif ONLY_SINCE_SVN(1, 5) RUN_SVN_WITH_POOL(temp_pool, svn_client_export4(&result_rev, from, to, &c_peg_rev, &c_rev, overwrite, ignore_externals, - recurse?svn_depth_infinity:svn_depth_files, + recurse?svn_depth_infinity:svn_depth_files, native_eol, client->client, temp_pool)); #else RUN_SVN_WITH_POOL(temp_pool, svn_client_export3(&result_rev, from, to, @@ -864,128 +857,128 @@ 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 *path; - PyObject *peg_rev=Py_None, *rev=Py_None; - svn_opt_revision_t c_peg_rev, c_rev; - apr_pool_t *temp_pool; - svn_stream_t *stream; - bool expand_keywords = true; - PyObject *py_stream, *py_path, *ret; - apr_hash_t *props = NULL; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|OOb", kwnames, &py_path, &py_stream, &rev, &peg_rev, &expand_keywords)) - return NULL; - - if (!to_opt_revision(rev, &c_rev)) - return NULL; - if (!to_opt_revision(peg_rev, &c_peg_rev)) - return NULL; - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_string(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - stream = new_py_stream(temp_pool, py_stream); - if (stream == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } + ClientObject *client = (ClientObject *)self; + char *kwnames[] = { "path", "output_stream", "revision", "peg_revision", NULL }; + char *path; + PyObject *peg_rev=Py_None, *rev=Py_None; + svn_opt_revision_t c_peg_rev, c_rev; + apr_pool_t *temp_pool; + svn_stream_t *stream; + bool expand_keywords = true; + PyObject *py_stream, *py_path, *ret; + apr_hash_t *props = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|OOb", kwnames, &py_path, &py_stream, &rev, &peg_rev, &expand_keywords)) + return NULL; + + if (!to_opt_revision(rev, &c_rev)) + return NULL; + if (!to_opt_revision(peg_rev, &c_peg_rev)) + return NULL; + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_string(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + stream = new_py_stream(temp_pool, py_stream); + if (stream == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } #if ONLY_SINCE_SVN(1, 9) - RUN_SVN_WITH_POOL(temp_pool, svn_client_cat3( - &props, stream, path, &c_peg_rev, &c_rev, expand_keywords, - client->client, temp_pool, temp_pool)); - - ret = prop_hash_to_dict(props); - if (ret == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } + RUN_SVN_WITH_POOL(temp_pool, svn_client_cat3( + &props, stream, path, &c_peg_rev, &c_rev, expand_keywords, + client->client, temp_pool, temp_pool)); + + ret = prop_hash_to_dict(props); + if (ret == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } #else - if (!expand_keywords) { - PyErr_SetString(PyExc_NotImplementedError, - "expand_keywords=false only supported with svn >= 1.9"); - apr_pool_destroy(temp_pool); - return NULL; - } - RUN_SVN_WITH_POOL(temp_pool, svn_client_cat2(stream, path, - &c_peg_rev, &c_rev, client->client, temp_pool)); - - ret = Py_None; - Py_INCREF(ret); + if (!expand_keywords) { + PyErr_SetString(PyExc_NotImplementedError, + "expand_keywords=false only supported with svn >= 1.9"); + apr_pool_destroy(temp_pool); + return NULL; + } + RUN_SVN_WITH_POOL(temp_pool, svn_client_cat2(stream, path, + &c_peg_rev, &c_rev, client->client, temp_pool)); + + ret = Py_None; + Py_INCREF(ret); #endif - apr_pool_destroy(temp_pool); - return ret; + apr_pool_destroy(temp_pool); + return ret; } static PyObject *client_delete(PyObject *self, PyObject *args) { - PyObject *paths; - bool force=false, keep_local=false; - apr_pool_t *temp_pool; - svn_commit_info_t *commit_info = NULL; - PyObject *ret, *py_revprops; - apr_array_header_t *apr_paths; - ClientObject *client = (ClientObject *)self; - apr_hash_t *hash_revprops; - - if (!PyArg_ParseTuple(args, "O|bbO", &paths, &force, &keep_local, &py_revprops)) - return NULL; - - temp_pool = Pool(NULL); - if (temp_pool == NULL) - return NULL; - if (!client_path_list_to_apr_array(temp_pool, paths, &apr_paths)) { - apr_pool_destroy(temp_pool); - return NULL; - } - - if (py_revprops != Py_None) { - hash_revprops = prop_dict_to_hash(temp_pool, py_revprops); - if (hash_revprops == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - } else { - hash_revprops = NULL; - } + PyObject *paths; + bool force=false, keep_local=false; + apr_pool_t *temp_pool; + svn_commit_info_t *commit_info = NULL; + PyObject *ret, *py_revprops; + apr_array_header_t *apr_paths; + ClientObject *client = (ClientObject *)self; + apr_hash_t *hash_revprops; + + if (!PyArg_ParseTuple(args, "O|bbO", &paths, &force, &keep_local, &py_revprops)) + return NULL; + + temp_pool = Pool(NULL); + if (temp_pool == NULL) + return NULL; + if (!client_list_to_apr_array(temp_pool, paths, py_object_to_svn_path_or_url, &apr_paths)) { + apr_pool_destroy(temp_pool); + return NULL; + } + + if (py_revprops != Py_None) { + hash_revprops = prop_dict_to_hash(temp_pool, py_revprops); + if (hash_revprops == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + } else { + hash_revprops = NULL; + } #if ONLY_SINCE_SVN(1, 5) - RUN_SVN_WITH_POOL(temp_pool, svn_client_delete3( - &commit_info, apr_paths, force, keep_local, hash_revprops, client->client, temp_pool)); + RUN_SVN_WITH_POOL(temp_pool, svn_client_delete3( + &commit_info, apr_paths, force, keep_local, hash_revprops, client->client, temp_pool)); #else - if (hash_revprops != NULL) { - PyErr_SetString(PyExc_NotImplementedError, + if (hash_revprops != NULL) { + PyErr_SetString(PyExc_NotImplementedError, "revprops not supported against svn 1.4"); - apr_pool_destroy(temp_pool); - return NULL; - } + apr_pool_destroy(temp_pool); + return NULL; + } - if (keep_local) { - PyErr_SetString(PyExc_NotImplementedError, + if (keep_local) { + PyErr_SetString(PyExc_NotImplementedError, "keep_local not supported against svn 1.4"); - apr_pool_destroy(temp_pool); - return NULL; - } - RUN_SVN_WITH_POOL(temp_pool, svn_client_delete2( - &commit_info, apr_paths, force, client->client, temp_pool)); + apr_pool_destroy(temp_pool); + return NULL; + } + RUN_SVN_WITH_POOL(temp_pool, svn_client_delete2( + &commit_info, apr_paths, force, client->client, temp_pool)); #endif - ret = py_commit_info_tuple(commit_info); + ret = py_commit_info_tuple(commit_info); - apr_pool_destroy(temp_pool); + apr_pool_destroy(temp_pool); - return ret; + return ret; } static PyObject *client_mkdir(PyObject *self, PyObject *args) @@ -1005,7 +998,7 @@ static PyObject *client_mkdir(PyObject *self, PyObject *args) temp_pool = Pool(NULL); if (temp_pool == NULL) return NULL; - if (!client_path_list_to_apr_array(temp_pool, paths, &apr_paths)) { + if (!client_list_to_apr_array(temp_pool, paths, py_object_to_svn_path_or_url, &apr_paths)) { apr_pool_destroy(temp_pool); return NULL; } @@ -1247,10 +1240,10 @@ static PyObject *client_propget(PyObject *self, PyObject *args) /* FIXME: Support changelists */ /* FIXME: Support actual_revnum */ /* FIXME: Support depth properly */ - /* FIXME: Support inherited_props */ + /* FIXME: Support inherited_props */ RUN_SVN_WITH_POOL(temp_pool, svn_client_propget5(&hash_props, NULL, - propname, target, + propname, target, &c_peg_rev, &c_rev, NULL, recurse?svn_depth_infinity:svn_depth_files, NULL, client->client, temp_pool, temp_pool)); #elif ONLY_SINCE_SVN(1, 5) @@ -1332,39 +1325,39 @@ static PyObject *client_proplist(PyObject *self, PyObject *args, } - RUN_SVN_WITH_POOL(temp_pool, - svn_client_proplist2(&props, target, &c_peg_rev, &c_rev, - (depth == svn_depth_infinity), - client->client, temp_pool)); + RUN_SVN_WITH_POOL(temp_pool, + svn_client_proplist2(&props, target, &c_peg_rev, &c_rev, + (depth == svn_depth_infinity), + client->client, temp_pool)); - for (i = 0; i < props->nelts; i++) { - svn_client_proplist_item_t *item; - PyObject *prop_dict, *value; + for (i = 0; i < props->nelts; i++) { + svn_client_proplist_item_t *item; + PyObject *prop_dict, *value; - item = APR_ARRAY_IDX(props, i, svn_client_proplist_item_t *); + item = APR_ARRAY_IDX(props, i, svn_client_proplist_item_t *); - prop_dict = prop_hash_to_dict(item->prop_hash); - if (prop_dict == NULL) { - apr_pool_destroy(temp_pool); - Py_DECREF(prop_list); - return NULL; - } + prop_dict = prop_hash_to_dict(item->prop_hash); + if (prop_dict == NULL) { + apr_pool_destroy(temp_pool); + Py_DECREF(prop_list); + return NULL; + } - value = Py_BuildValue("(sO)", item->node_name, prop_dict); - if (value == NULL) { - apr_pool_destroy(temp_pool); - Py_DECREF(prop_list); - Py_DECREF(prop_dict); - return NULL; - } - if (PyList_Append(prop_list, value) != 0) { - apr_pool_destroy(temp_pool); - Py_DECREF(prop_list); - Py_DECREF(prop_dict); - Py_DECREF(value); - return NULL; - } - Py_DECREF(value); + value = Py_BuildValue("(sO)", item->node_name, prop_dict); + if (value == NULL) { + apr_pool_destroy(temp_pool); + Py_DECREF(prop_list); + Py_DECREF(prop_dict); + return NULL; + } + if (PyList_Append(prop_list, value) != 0) { + apr_pool_destroy(temp_pool); + Py_DECREF(prop_list); + Py_DECREF(prop_dict); + Py_DECREF(value); + return NULL; + } + Py_DECREF(value); } apr_pool_destroy(temp_pool); @@ -1417,48 +1410,48 @@ static PyObject *client_update(PyObject *self, PyObject *args, PyObject *kwargs) ClientObject *client = (ClientObject *)self; bool allow_unver_obstructions = false, depth_is_sticky = false; - char *kwnames[] = - { "path", "revision", "recurse", "ignore_externals", "depth_is_sticky", - "allow_unver_obstructions", NULL }; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Obbbb", kwnames, - &paths, &rev, &recurse, &ignore_externals, - &depth_is_sticky, &allow_unver_obstructions)) - return NULL; - - if (!to_opt_revision(rev, &c_rev)) - return NULL; - temp_pool = Pool(NULL); - if (temp_pool == NULL) - return NULL; - if (!client_path_list_to_apr_array(temp_pool, paths, &apr_paths)) { - apr_pool_destroy(temp_pool); - return NULL; - } + char *kwnames[] = + { "path", "revision", "recurse", "ignore_externals", "depth_is_sticky", + "allow_unver_obstructions", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Obbbb", kwnames, + &paths, &rev, &recurse, &ignore_externals, + &depth_is_sticky, &allow_unver_obstructions)) + return NULL; + + if (!to_opt_revision(rev, &c_rev)) + return NULL; + temp_pool = Pool(NULL); + if (temp_pool == NULL) + return NULL; + if (!client_list_to_apr_array(temp_pool, paths, py_object_to_svn_path_or_url, &apr_paths)) { + apr_pool_destroy(temp_pool); + return NULL; + } #if ONLY_SINCE_SVN(1, 5) - RUN_SVN_WITH_POOL(temp_pool, svn_client_update3(&result_revs, - apr_paths, &c_rev, recurse?svn_depth_infinity:svn_depth_files, - depth_is_sticky?TRUE:FALSE, ignore_externals, allow_unver_obstructions?TRUE:FALSE, - client->client, temp_pool)); + RUN_SVN_WITH_POOL(temp_pool, svn_client_update3(&result_revs, + apr_paths, &c_rev, recurse?svn_depth_infinity:svn_depth_files, + depth_is_sticky?TRUE:FALSE, ignore_externals, allow_unver_obstructions?TRUE:FALSE, + client->client, temp_pool)); #else - RUN_SVN_WITH_POOL(temp_pool, svn_client_update2(&result_revs, - apr_paths, &c_rev, - recurse, ignore_externals, client->client, temp_pool)); + RUN_SVN_WITH_POOL(temp_pool, svn_client_update2(&result_revs, + apr_paths, &c_rev, + recurse, ignore_externals, client->client, temp_pool)); #endif - ret = PyList_New(result_revs->nelts); - if (ret == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - for (i = 0; i < result_revs->nelts; i++) { - ret_rev = APR_ARRAY_IDX(result_revs, i, svn_revnum_t); - if (PyList_SetItem(ret, i, PyLong_FromLong(ret_rev)) != 0) { - Py_DECREF(ret); - return NULL; - } - } - apr_pool_destroy(temp_pool); - return ret; + ret = PyList_New(result_revs->nelts); + if (ret == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + for (i = 0; i < result_revs->nelts; i++) { + ret_rev = APR_ARRAY_IDX(result_revs, i, svn_revnum_t); + if (PyList_SetItem(ret, i, PyLong_FromLong(ret_rev)) != 0) { + Py_DECREF(ret); + return NULL; + } + } + apr_pool_destroy(temp_pool); + return ret; } static PyObject *client_list(PyObject *self, PyObject *args, PyObject *kwargs) @@ -1700,17 +1693,17 @@ static PyObject *client_log(PyObject *self, PyObject *args, PyObject *kwargs) } #endif - if (!client_path_list_to_apr_array(temp_pool, paths, &apr_paths)) { - apr_pool_destroy(temp_pool); - return NULL; - } + if (!client_list_to_apr_array(temp_pool, paths, py_object_to_svn_path_or_url, &apr_paths)) { + apr_pool_destroy(temp_pool); + return NULL; + } - if (revprops) { - if (!string_list_to_apr_array(temp_pool, revprops, &apr_revprops)) { - apr_pool_destroy(temp_pool); - return NULL; - } - } + if (revprops) { + if (!string_list_to_apr_array(temp_pool, revprops, &apr_revprops)) { + apr_pool_destroy(temp_pool); + return NULL; + } + } #if ONLY_SINCE_SVN(1, 6) revision_range.start = c_start_rev; @@ -1803,11 +1796,11 @@ static PyObject *client_info(PyObject *self, PyObject *args, PyObject *kwargs) Py_BEGIN_ALLOW_THREADS; #if ONLY_SINCE_SVN(1, 7) /* FIXME: Support changelists */ - err = svn_client_info3(path, &c_peg_rev, &c_rev, depth, fetch_excluded?TRUE:FALSE, - fetch_actual_only?TRUE:FALSE, NULL, - info_receiver, - entry_dict, - client->client, temp_pool); + err = svn_client_info3(path, &c_peg_rev, &c_rev, depth, fetch_excluded?TRUE:FALSE, + fetch_actual_only?TRUE:FALSE, NULL, + info_receiver, + entry_dict, + client->client, temp_pool); #elif ONLY_SINCE_SVN(1, 5) /* FIXME: Support changelists */ err = svn_client_info2(path, &c_peg_rev, &c_rev, info_receiver, entry_dict, @@ -1839,8 +1832,8 @@ static PyMethodDef client_methods[] = { "S.add(path, recursive=True, force=False, no_ignore=False, no_autoprops=False)" }, { "checkout", (PyCFunction)client_checkout, METH_VARARGS|METH_KEYWORDS, "S.checkout(url, path, rev=None, peg_rev=None, recurse=True, ignore_externals=False, allow_unver_obstructions=False)" }, - { "export", (PyCFunction)client_export, METH_VARARGS|METH_KEYWORDS, - "S.export(from, to, rev=None, peg_rev=None, recurse=True, ignore_externals=False, overwrite=False, native_eol=None)" }, + { "export", (PyCFunction)client_export, METH_VARARGS|METH_KEYWORDS, + "S.export(from, to, rev=None, peg_rev=None, recurse=True, ignore_externals=False, overwrite=False, native_eol=None)" }, { "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) -> (revnum, date, author)" }, @@ -1871,34 +1864,34 @@ static PyGetSetDef client_getset[] = { static PyObject *get_default_ignores(PyObject *self) { - apr_array_header_t *patterns; - apr_pool_t *pool; - int i = 0; - ConfigObject *configobj = (ConfigObject *)self; - PyObject *ret; - - pool = Pool(NULL); - if (pool == NULL) - return NULL; - RUN_SVN_WITH_POOL(pool, svn_wc_get_default_ignores(&patterns, configobj->config, pool)); - ret = PyList_New(patterns->nelts); - for (i = 0; i < patterns->nelts; i++) { - PyObject *item = PyBytes_FromString(APR_ARRAY_IDX(patterns, i, char *)); - if (item == NULL) { - apr_pool_destroy(pool); - Py_DECREF(item); - Py_DECREF(ret); - return NULL; - } + apr_array_header_t *patterns; + apr_pool_t *pool; + int i = 0; + ConfigObject *configobj = (ConfigObject *)self; + PyObject *ret; + + pool = Pool(NULL); + if (pool == NULL) + return NULL; + RUN_SVN_WITH_POOL(pool, svn_wc_get_default_ignores(&patterns, configobj->config, pool)); + ret = PyList_New(patterns->nelts); + for (i = 0; i < patterns->nelts; i++) { + PyObject *item = PyBytes_FromString(APR_ARRAY_IDX(patterns, i, char *)); + if (item == NULL) { + apr_pool_destroy(pool); + Py_DECREF(item); + Py_DECREF(ret); + return NULL; + } if (PyList_SetItem(ret, i, item) != 0) { - apr_pool_destroy(pool); - Py_DECREF(item); - Py_DECREF(ret); - return NULL; - } - } - apr_pool_destroy(pool); - return ret; + apr_pool_destroy(pool); + Py_DECREF(item); + Py_DECREF(ret); + return NULL; + } + } + apr_pool_destroy(pool); + return ret; } static PyMethodDef config_methods[] = { @@ -2134,7 +2127,7 @@ static PyMemberDef wc_info_members[] = { static void wcinfo_dealloc(PyObject *self) { - PyObject_Del(self); + PyObject_Del(self); } PyTypeObject WCInfo_Type = { @@ -2302,9 +2295,9 @@ static PyObject *get_config(PyObject *self, PyObject *args) */ static PyObject *version(PyObject *self) { - const svn_version_t *ver = svn_client_version(); - return Py_BuildValue("(iiis)", ver->major, ver->minor, - ver->patch, ver->tag); + const svn_version_t *ver = svn_client_version(); + return Py_BuildValue("(iiis)", ver->major, ver->minor, + ver->patch, ver->tag); } SVN_VERSION_DEFINE(svn_api_version); @@ -2316,96 +2309,96 @@ SVN_VERSION_DEFINE(svn_api_version); */ static PyObject *api_version(PyObject *self) { - const svn_version_t *ver = &svn_api_version; - return Py_BuildValue("(iiis)", ver->major, ver->minor, - ver->patch, ver->tag); + const svn_version_t *ver = &svn_api_version; + return Py_BuildValue("(iiis)", ver->major, ver->minor, + ver->patch, ver->tag); } static PyMethodDef client_mod_methods[] = { - { "get_config", get_config, METH_VARARGS, "get_config(config_dir=None) -> config" }, - { "api_version", (PyCFunction)api_version, METH_NOARGS, - "api_version() -> (major, minor, patch, tag)\n\n" - "Version of libsvn_client Subvertpy was compiled against." - }, - { "version", (PyCFunction)version, METH_NOARGS, - "version() -> (major, minor, patch, tag)\n\n" - "Version of libsvn_wc currently used." - }, - { NULL } + { "get_config", get_config, METH_VARARGS, "get_config(config_dir=None) -> config" }, + { "api_version", (PyCFunction)api_version, METH_NOARGS, + "api_version() -> (major, minor, patch, tag)\n\n" + "Version of libsvn_client Subvertpy was compiled against." + }, + { "version", (PyCFunction)version, METH_NOARGS, + "version() -> (major, minor, patch, tag)\n\n" + "Version of libsvn_wc currently used." + }, + { NULL } }; static PyObject * moduleinit(void) { - PyObject *mod; + PyObject *mod; - if (PyType_Ready(&Client_Type) < 0) - return NULL; + if (PyType_Ready(&Client_Type) < 0) + return NULL; - if (PyType_Ready(&Config_Type) < 0) - return NULL; + if (PyType_Ready(&Config_Type) < 0) + return NULL; - if (PyType_Ready(&ConfigItem_Type) < 0) - return NULL; + if (PyType_Ready(&ConfigItem_Type) < 0) + return NULL; - if (PyType_Ready(&Info_Type) < 0) - return NULL; + if (PyType_Ready(&Info_Type) < 0) + return NULL; - if (PyType_Ready(&WCInfo_Type) < 0) - return NULL; + if (PyType_Ready(&WCInfo_Type) < 0) + return NULL; - /* Make sure APR is initialized */ - apr_initialize(); + /* Make sure APR is initialized */ + apr_initialize(); #if PY_MAJOR_VERSION >= 3 - static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "client", /* m_name */ - "Client methods", /* m_doc */ - -1, /* m_size */ - client_mod_methods, /* m_methods */ - NULL, /* m_reload */ - NULL, /* m_traverse */ - NULL, /* m_clear*/ - NULL, /* m_free */ - }; - mod = PyModule_Create(&moduledef); + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "client", /* m_name */ + "Client methods", /* m_doc */ + -1, /* m_size */ + client_mod_methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear*/ + NULL, /* m_free */ + }; + mod = PyModule_Create(&moduledef); #else - mod = Py_InitModule3("client", client_mod_methods, "Client methods"); + mod = Py_InitModule3("client", client_mod_methods, "Client methods"); #endif - if (mod == NULL) - return NULL; + if (mod == NULL) + return NULL; - Py_INCREF(&Client_Type); - PyModule_AddObject(mod, "Client", (PyObject *)&Client_Type); + Py_INCREF(&Client_Type); + PyModule_AddObject(mod, "Client", (PyObject *)&Client_Type); - PyModule_AddObject(mod, "depth_empty", - (PyObject *)PyLong_FromLong(svn_depth_empty)); - PyModule_AddObject(mod, "depth_files", - (PyObject *)PyLong_FromLong(svn_depth_files)); - PyModule_AddObject(mod, "depth_immediates", - (PyObject *)PyLong_FromLong(svn_depth_immediates)); - PyModule_AddObject(mod, "depth_infinity", - (PyObject *)PyLong_FromLong(svn_depth_infinity)); + PyModule_AddObject(mod, "depth_empty", + (PyObject *)PyLong_FromLong(svn_depth_empty)); + PyModule_AddObject(mod, "depth_files", + (PyObject *)PyLong_FromLong(svn_depth_files)); + PyModule_AddObject(mod, "depth_immediates", + (PyObject *)PyLong_FromLong(svn_depth_immediates)); + PyModule_AddObject(mod, "depth_infinity", + (PyObject *)PyLong_FromLong(svn_depth_infinity)); - Py_INCREF(&Config_Type); - PyModule_AddObject(mod, "Config", (PyObject *)&Config_Type); + Py_INCREF(&Config_Type); + PyModule_AddObject(mod, "Config", (PyObject *)&Config_Type); - return mod; + return mod; } #if PY_MAJOR_VERSION >= 3 PyMODINIT_FUNC PyInit_client(void) { - return moduleinit(); + return moduleinit(); } #else PyMODINIT_FUNC initclient(void) { - moduleinit(); + moduleinit(); } #endif diff --git a/subvertpy/util.c b/subvertpy/util.c index 49c68899..0f82a667 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -57,160 +57,193 @@ svn_relpath_canonicalize(const char *relpath, return svn_path_canonicalize(relpath, result_pool); } +const char * +svn_dirent_canonicalize(const char *dirent, + apr_pool_t *result_pool) +{ + return svn_path_canonicalize(dirent, result_pool); +} + #endif -const char *py_object_to_svn_dirent(PyObject *obj, apr_pool_t *pool) +const char *py_object_to_svn_path_or_url(PyObject *obj, apr_pool_t *pool) { - const char *ret; - PyObject *bytes_obj = NULL; + const char *ret; + PyObject *bytes_obj = NULL; + + if (PyUnicode_Check(obj)) { + bytes_obj = obj = PyUnicode_AsUTF8String(obj); + if (obj == NULL) { + return NULL; + } + } - if (PyUnicode_Check(obj)) { - bytes_obj = obj = PyUnicode_AsUTF8String(obj); - if (obj == NULL) { - return NULL; - } - } + if (!PyBytes_Check(obj)) { + PyErr_SetString(PyExc_TypeError, + "URIs need to be UTF-8 bytestrings or unicode strings"); + Py_XDECREF(bytes_obj); + return NULL; + } - if (PyBytes_Check(obj)) { -#if ONLY_SINCE_SVN(1, 7) - ret = svn_dirent_canonicalize(PyBytes_AsString(obj), pool); -#else - ret = svn_path_canonicalize(PyBytes_AsString(obj), pool); -#endif - Py_XDECREF(bytes_obj); - return ret; - } else { - PyErr_SetString(PyExc_TypeError, - "URIs need to be UTF-8 bytestrings or unicode strings"); - Py_XDECREF(bytes_obj); - return NULL; - } + ret = PyBytes_AsString(obj); + if (svn_path_is_url(ret)) { + ret = svn_uri_canonicalize(ret, pool); + } else { + ret = svn_dirent_canonicalize(ret, pool); + } + + Py_XDECREF(bytes_obj); + return ret; } -char *py_object_to_svn_string(PyObject *obj, apr_pool_t *pool) +const char *py_object_to_svn_dirent(PyObject *obj, apr_pool_t *pool) { - char *ret; - PyObject *bytes_obj = NULL; + const char *ret; + PyObject *bytes_obj = NULL; + + if (PyUnicode_Check(obj)) { + bytes_obj = obj = PyUnicode_AsUTF8String(obj); + if (obj == NULL) { + return NULL; + } + } - if (PyUnicode_Check(obj)) { - bytes_obj = obj = PyUnicode_AsUTF8String(obj); - if (obj == NULL) { - return NULL; - } - } + if (PyBytes_Check(obj)) { + ret = svn_dirent_canonicalize(PyBytes_AsString(obj), pool); + Py_XDECREF(bytes_obj); + return ret; + } else { + PyErr_SetString(PyExc_TypeError, + "URIs need to be UTF-8 bytestrings or unicode strings"); + Py_XDECREF(bytes_obj); + return NULL; + } +} - if (PyBytes_Check(obj)) { - ret = apr_pstrdup(pool, PyBytes_AsString(obj)); - Py_XDECREF(bytes_obj); - return ret; - } else { - PyErr_SetString(PyExc_TypeError, - "URIs need to be UTF-8 bytestrings or unicode strings"); - Py_XDECREF(bytes_obj); - return NULL; - } +char *py_object_to_svn_string(PyObject *obj, apr_pool_t *pool) +{ + char *ret; + PyObject *bytes_obj = NULL; + + if (PyUnicode_Check(obj)) { + bytes_obj = obj = PyUnicode_AsUTF8String(obj); + if (obj == NULL) { + return NULL; + } + } + + if (PyBytes_Check(obj)) { + ret = apr_pstrdup(pool, PyBytes_AsString(obj)); + Py_XDECREF(bytes_obj); + return ret; + } else { + PyErr_SetString(PyExc_TypeError, + "URIs need to be UTF-8 bytestrings or unicode strings"); + Py_XDECREF(bytes_obj); + return NULL; + } } const char *py_object_to_svn_uri(PyObject *obj, apr_pool_t *pool) { - const char *ret; - PyObject *bytes_obj = NULL; - - if (PyUnicode_Check(obj)) { - bytes_obj = obj = PyUnicode_AsUTF8String(obj); - if (obj == NULL) { - return NULL; - } - } + const char *ret; + PyObject *bytes_obj = NULL; + + if (PyUnicode_Check(obj)) { + bytes_obj = obj = PyUnicode_AsUTF8String(obj); + if (obj == NULL) { + return NULL; + } + } - if (PyBytes_Check(obj)) { - ret = svn_uri_canonicalize(PyBytes_AsString(obj), pool); - Py_XDECREF(bytes_obj); - return ret; - } else { - PyErr_SetString(PyExc_TypeError, - "URIs need to be UTF-8 bytestrings or unicode strings"); - Py_XDECREF(bytes_obj); - return NULL; - } + if (PyBytes_Check(obj)) { + ret = svn_uri_canonicalize(PyBytes_AsString(obj), pool); + Py_XDECREF(bytes_obj); + return ret; + } else { + PyErr_SetString(PyExc_TypeError, + "URIs need to be UTF-8 bytestrings or unicode strings"); + Py_XDECREF(bytes_obj); + return NULL; + } } const char *py_object_to_svn_relpath(PyObject *obj, apr_pool_t *pool) { - const char *ret; - - if (PyUnicode_Check(obj)) { - obj = PyUnicode_AsUTF8String(obj); - if (obj == NULL) { - return NULL; - } - } else { - Py_INCREF(obj); - } + const char *ret; + + if (PyUnicode_Check(obj)) { + obj = PyUnicode_AsUTF8String(obj); + if (obj == NULL) { + return NULL; + } + } else { + Py_INCREF(obj); + } - if (PyBytes_Check(obj)) { - ret = svn_relpath_canonicalize(PyBytes_AsString(obj), pool); - Py_DECREF(obj); - return ret; - } else { - PyErr_SetString(PyExc_TypeError, - "relative paths need to be UTF-8 bytestrings or unicode strings"); - Py_DECREF(obj); - return NULL; - } + if (PyBytes_Check(obj)) { + ret = svn_relpath_canonicalize(PyBytes_AsString(obj), pool); + Py_DECREF(obj); + return ret; + } else { + PyErr_SetString(PyExc_TypeError, + "relative paths need to be UTF-8 bytestrings or unicode strings"); + Py_DECREF(obj); + return NULL; + } } apr_pool_t *Pool(apr_pool_t *parent) { - apr_status_t status; - apr_pool_t *ret; - ret = NULL; - status = apr_pool_create(&ret, parent); - if (status != 0) { - PyErr_SetAprStatus(status); - return NULL; - } - return ret; + apr_status_t status; + apr_pool_t *ret; + ret = NULL; + status = apr_pool_create(&ret, parent); + if (status != 0) { + PyErr_SetAprStatus(status); + return NULL; + } + return ret; } PyTypeObject *PyErr_GetSubversionExceptionTypeObject(void) { - PyObject *coremod, *excobj; - coremod = PyImport_ImportModule("subvertpy"); + PyObject *coremod, *excobj; + coremod = PyImport_ImportModule("subvertpy"); - if (coremod == NULL) { - return NULL; - } + if (coremod == NULL) { + return NULL; + } - excobj = PyObject_GetAttrString(coremod, "SubversionException"); - Py_DECREF(coremod); + excobj = PyObject_GetAttrString(coremod, "SubversionException"); + Py_DECREF(coremod); - if (excobj == NULL) { - PyErr_BadInternalCall(); - return NULL; - } + if (excobj == NULL) { + PyErr_BadInternalCall(); + return NULL; + } - return (PyTypeObject *)excobj; + return (PyTypeObject *)excobj; } PyTypeObject *PyErr_GetGaiExceptionTypeObject(void) { - PyObject *socketmod, *excobj; - socketmod = PyImport_ImportModule("socket"); + PyObject *socketmod, *excobj; + socketmod = PyImport_ImportModule("socket"); - if (socketmod == NULL) { - return NULL; - } + if (socketmod == NULL) { + return NULL; + } - excobj = PyObject_GetAttrString(socketmod, "gaierror"); - Py_DECREF(socketmod); + excobj = PyObject_GetAttrString(socketmod, "gaierror"); + Py_DECREF(socketmod); - if (excobj == NULL) { - PyErr_BadInternalCall(); - return NULL; - } + if (excobj == NULL) { + PyErr_BadInternalCall(); + return NULL; + } - return (PyTypeObject *)excobj; + return (PyTypeObject *)excobj; } PyObject *PyErr_NewSubversionException(svn_error_t *error) diff --git a/subvertpy/util.h b/subvertpy/util.h index bbbcf9b3..6e178377 100644 --- a/subvertpy/util.h +++ b/subvertpy/util.h @@ -144,11 +144,16 @@ svn_uri_canonicalize(const char *uri, const char * svn_relpath_canonicalize(const char *relpath, apr_pool_t *result_pool); + +const char * +svn_dirent_canonicalize(const char *dirent, + apr_pool_t *result_pool); #endif const char *py_object_to_svn_uri(PyObject *obj, apr_pool_t *pool); const char *py_object_to_svn_dirent(PyObject *obj, apr_pool_t *pool); const char *py_object_to_svn_relpath(PyObject *obj, apr_pool_t *pool); +const char *py_object_to_svn_path_or_url(PyObject *obj, apr_pool_t *pool); char *py_object_to_svn_string(PyObject *obj, apr_pool_t *pool); #define py_object_from_svn_abspath PyBytes_FromString diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 7a635b25..bf7e7c46 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -3089,16 +3089,11 @@ moduleinit(void) PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_MERGED", svn_wc_conflict_choose_merged); #endif -#if ONLY_BEFORE_SVN(1, 7) - /* Subversion 1.7 has a couple of significant behaviour changes that break subvertpy. - * We haven't updated the code to deal with these changes in behaviour yet. - * */ PyModule_AddObject(mod, "WorkingCopy", (PyObject *)&Adm_Type); Py_INCREF(&Adm_Type); PyModule_AddObject(mod, "CommittedQueue", (PyObject *)&CommittedQueue_Type); Py_INCREF(&CommittedQueue_Type); -#endif return mod; } -- cgit v1.2.3 From 8a5d2d1a1151473a2c11ca429ca231d9ca50a040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 06:34:03 +0000 Subject: Support newer version of svn_client_commit. --- subvertpy/client.c | 110 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 94 insertions(+), 16 deletions(-) diff --git a/subvertpy/client.c b/subvertpy/client.c index 5424ff03..b19e7e8c 100644 --- a/subvertpy/client.c +++ b/subvertpy/client.c @@ -734,22 +734,72 @@ static PyObject *client_checkout(PyObject *self, PyObject *args, PyObject *kwarg return PyLong_FromLong(result_rev); } +static svn_error_t *py_commit_callback2(const svn_commit_info_t *commit_info, + void *callback, apr_pool_t *pool) { + PyObject *py_callback = callback; + PyObject *py_commit_info; + PyObject *ret; + + PyGILState_STATE state = PyGILState_Ensure(); + + py_commit_info = py_commit_info_tuple(commit_info); + + if (py_commit_info == NULL) { + PyGILState_Release(state); + return py_svn_error(); + } + + if (py_callback != Py_None) { + ret = PyObject_CallFunction(py_callback, "O", py_commit_info); + } else { + ret = Py_None; + Py_INCREF(ret); + } + Py_DECREF(py_commit_info); + + PyGILState_Release(state); + + if (ret == NULL) { + return py_svn_error(); + } + + return NULL; +} + static PyObject *client_commit(PyObject *self, PyObject *args, PyObject *kwargs) { PyObject *targets; ClientObject *client = (ClientObject *)self; bool recurse=true, keep_locks=true; apr_pool_t *temp_pool; +#if ONLY_BEFORE_SVN(1, 8) svn_commit_info_t *commit_info = NULL; - PyObject *ret; +#endif apr_array_header_t *apr_targets; + bool include_file_externals = false; + bool include_dir_externals = false; + bool keep_changelist = false; + bool commit_as_operations = false; + const apr_array_header_t *changelists = NULL; PyObject *revprops = Py_None; - char *kwnames[] = { "targets", "recurse", "keep_locks", "revprops", NULL }; + PyObject *callback = Py_None; + char *kwnames[] = { + "targets", "recurse", "keep_locks", "revprops", + "keep_changelist", "commit_as_operations", "include_file_externals", + "include_dir_externals", "callback", NULL }; #if ONLY_SINCE_SVN(1, 5) apr_hash_t *hash_revprops; #endif - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|bbO", kwnames, &targets, &recurse, &keep_locks, &revprops)) + /* TODO(jelmer): Support changelists */ + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|bbObbbbO", kwnames, + &targets, &recurse, &keep_locks, + &revprops, &keep_changelist, + &commit_as_operations, + &include_file_externals, + &include_dir_externals, + &callback)) { return NULL; + } temp_pool = Pool(NULL); if (temp_pool == NULL) { return NULL; @@ -765,9 +815,16 @@ static PyObject *client_commit(PyObject *self, PyObject *args, PyObject *kwargs) return NULL; } - -#if ONLY_SINCE_SVN(1, 5) if (revprops != Py_None) { +#if ONLY_BEFORE_SVN(1, 5) + if (PyDict_Size(revprops) > 0) { + PyErr_SetString(PyExc_NotImplementedError, + "Setting revision properties only supported on svn >= 1.5"); + apr_pool_destroy(temp_pool); + return NULL; + } +#endif + hash_revprops = prop_dict_to_hash(temp_pool, revprops); if (hash_revprops == NULL) { apr_pool_destroy(temp_pool); @@ -777,26 +834,47 @@ static PyObject *client_commit(PyObject *self, PyObject *args, PyObject *kwargs) hash_revprops = NULL; } - /* FIXME: Support keep_changelist and changelists */ - RUN_SVN_WITH_POOL(temp_pool, svn_client_commit4(&commit_info, - apr_targets, recurse?svn_depth_infinity:svn_depth_files, - keep_locks, false, NULL, hash_revprops, - client->client, temp_pool)); +#if ONLY_SINCE_SVN(1, 8) + RUN_SVN_WITH_POOL(temp_pool, svn_client_commit6( + apr_targets, recurse?svn_depth_infinity:svn_depth_files, + keep_locks, keep_changelist, commit_as_operations, + include_file_externals, include_dir_externals, changelists, + hash_revprops, py_commit_callback2, callback, client->client, temp_pool)); +#elif ONLY_SINCE_SVN(1, 5) + RUN_SVN_WITH_POOL(temp_pool, svn_client_commit4( + &commit_info, apr_targets, recurse?svn_depth_infinity:svn_depth_files, + keep_locks, keep_changelist, changelists, hash_revprops, + client->client, temp_pool)); #else - if (revprops != Py_None && PyDict_Size(revprops) > 0) { - PyErr_SetString(PyExc_NotImplementedError, - "Setting revision properties only supported on svn >= 1.5"); + if (commit_as_operations) { + PyErr_SetString(PyExc_NotImplementedError, "commit_as_operations only support on svn >= 1.8"); apr_pool_destroy(temp_pool); return NULL; } + RUN_SVN_WITH_POOL(temp_pool, svn_client_commit3(&commit_info, apr_targets, recurse, keep_locks, client->client, temp_pool)); #endif - ret = py_commit_info_tuple(commit_info); + +#if ONLY_BEFORE_SVN(1, 8) + { + PyObject *py_commit_info = py_commit_info_tuple(commit_info); + if (py_commit_info == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + ret = PyObject_CallFunction(py_commit_callback2, "O", py_commit_info); + Py_DECREF(py_commit_info); + if (ret == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } +#endif + apr_pool_destroy(temp_pool); - return ret; + Py_RETURN_NONE; } static PyObject *client_export(PyObject *self, PyObject *args, PyObject *kwargs) @@ -1836,7 +1914,7 @@ static PyMethodDef client_methods[] = { "S.export(from, to, rev=None, peg_rev=None, recurse=True, ignore_externals=False, overwrite=False, native_eol=None)" }, { "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) -> (revnum, date, author)" }, + { "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)" }, { "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)" }, -- cgit v1.2.3 From d26f740a56f31ea575159a936eb367bb0a331637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 07:30:05 +0000 Subject: Use new style subvertpy.client.delete. --- subvertpy/client.c | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/subvertpy/client.c b/subvertpy/client.c index b19e7e8c..48705742 100644 --- a/subvertpy/client.c +++ b/subvertpy/client.c @@ -70,6 +70,22 @@ typedef struct { apr_pool_t *pool; } InfoObject; +#define INVOKE_COMMIT_CALLBACK(pool, commit_info, callback) \ + { \ + PyObject *py_commit_info = py_commit_info_tuple(commit_info); \ + if (py_commit_info == NULL) { \ + apr_pool_destroy(pool); \ + return NULL; \ + } \ + ret = PyObject_CallFunction(py_commit_callback2, "O", py_commit_info); \ + Py_DECREF(py_commit_info); \ + if (ret == NULL) { \ + apr_pool_destroy(pool); \ + return NULL; \ + } \ + } \ + + static int client_set_auth(PyObject *self, PyObject *auth, void *closure); static int client_set_config(PyObject *self, PyObject *auth, void *closure); @@ -430,7 +446,7 @@ static svn_error_t *py_log_msg_func2(const char **log_msg, const char **tmp_file return NULL; } -static PyObject *py_commit_info_tuple(svn_commit_info_t *ci) +static PyObject *py_commit_info_tuple(const svn_commit_info_t *ci) { if (ci == NULL) Py_RETURN_NONE; @@ -858,18 +874,7 @@ static PyObject *client_commit(PyObject *self, PyObject *args, PyObject *kwargs) #endif #if ONLY_BEFORE_SVN(1, 8) - { - PyObject *py_commit_info = py_commit_info_tuple(commit_info); - if (py_commit_info == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - ret = PyObject_CallFunction(py_commit_callback2, "O", py_commit_info); - Py_DECREF(py_commit_info); - if (ret == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } + INVOKE_COMMIT_CALLBACK(temp_pool, commit_info, callback); #endif apr_pool_destroy(temp_pool); @@ -1004,13 +1009,16 @@ static PyObject *client_delete(PyObject *self, PyObject *args) PyObject *paths; bool force=false, keep_local=false; apr_pool_t *temp_pool; +#if ONLY_BEFORE_SVN(1, 7) svn_commit_info_t *commit_info = NULL; +#endif PyObject *ret, *py_revprops; apr_array_header_t *apr_paths; ClientObject *client = (ClientObject *)self; apr_hash_t *hash_revprops; + PyObject *callback = Py_None; - if (!PyArg_ParseTuple(args, "O|bbO", &paths, &force, &keep_local, &py_revprops)) + if (!PyArg_ParseTuple(args, "O|bbOO", &paths, &force, &keep_local, &py_revprops, &callback)) return NULL; temp_pool = Pool(NULL); @@ -1031,7 +1039,10 @@ static PyObject *client_delete(PyObject *self, PyObject *args) hash_revprops = NULL; } -#if ONLY_SINCE_SVN(1, 5) +#if ONLY_SINCE_SVN(1, 7) + RUN_SVN_WITH_POOL(temp_pool, svn_client_delete4( + apr_paths, force, keep_local, hash_revprops, py_commit_callback2, callback, client->client, temp_pool)); +#elif ONLY_SINCE_SVN(1, 5) RUN_SVN_WITH_POOL(temp_pool, svn_client_delete3( &commit_info, apr_paths, force, keep_local, hash_revprops, client->client, temp_pool)); #else @@ -1052,7 +1063,9 @@ static PyObject *client_delete(PyObject *self, PyObject *args) &commit_info, apr_paths, force, client->client, temp_pool)); #endif - ret = py_commit_info_tuple(commit_info); +#if ONLY_BEFORE_SVN(1, 7) + INVOKE_COMMIT_CALLBACK(temp_pool, commit_info, callback); +#endif apr_pool_destroy(temp_pool); -- cgit v1.2.3 From 9cc60c0b4b70aca6d06a8450452648126439aec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 07:36:50 +0000 Subject: Support new style subvertpy.client.mkdir. --- subvertpy/client.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/subvertpy/client.c b/subvertpy/client.c index 48705742..0a4138c4 100644 --- a/subvertpy/client.c +++ b/subvertpy/client.c @@ -77,7 +77,12 @@ typedef struct { apr_pool_destroy(pool); \ return NULL; \ } \ - ret = PyObject_CallFunction(py_commit_callback2, "O", py_commit_info); \ + if (callback != Py_None) { \ + ret = PyObject_CallFunction(callback, "O", py_commit_info); \ + } else { \ + ret = Py_None; \ + Py_INCREF(ret); \ + } \ Py_DECREF(py_commit_info); \ if (ret == NULL) { \ apr_pool_destroy(pool); \ @@ -1072,7 +1077,7 @@ static PyObject *client_delete(PyObject *self, PyObject *args) return ret; } -static PyObject *client_mkdir(PyObject *self, PyObject *args) +static PyObject *client_mkdir(PyObject *self, PyObject *args, PyObject *kwargs) { PyObject *paths, *revprops = NULL; bool make_parents = false; @@ -1082,8 +1087,10 @@ static PyObject *client_mkdir(PyObject *self, PyObject *args) apr_array_header_t *apr_paths; apr_hash_t *hash_revprops; ClientObject *client = (ClientObject *)self; + PyObject *callback = Py_None; + char *kwnames[] = { "path", "make_parents", "revprops", "callback", NULL }; - if (!PyArg_ParseTuple(args, "O|bO", &paths, &make_parents, &revprops)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|bOO", kwnames, &paths, &make_parents, &revprops, &callback)) return NULL; temp_pool = Pool(NULL); @@ -1111,9 +1118,15 @@ static PyObject *client_mkdir(PyObject *self, PyObject *args) hash_revprops = NULL; } +#if ONLY_SINCE_SVN(1, 7) + RUN_SVN_WITH_POOL(temp_pool, svn_client_mkdir4(apr_paths, + make_parents?TRUE:FALSE, hash_revprops, py_commit_callback2, callback, + client->client, temp_pool)); +#else RUN_SVN_WITH_POOL(temp_pool, svn_client_mkdir3(&commit_info, apr_paths, make_parents?TRUE:FALSE, hash_revprops, client->client, temp_pool)); +#endif #else if (make_parents) { PyErr_SetString(PyExc_ValueError, @@ -1133,15 +1146,13 @@ static PyObject *client_mkdir(PyObject *self, PyObject *args) client->client, temp_pool)); #endif - ret = py_commit_info_tuple(commit_info); + INVOKE_COMMIT_CALLBACK(temp_pool, commit_info, callback); apr_pool_destroy(temp_pool); return ret; } - - static PyObject *client_copy(PyObject *self, PyObject *args, PyObject *kwargs) { char *src_path, *dst_path; @@ -1937,7 +1948,7 @@ static PyMethodDef client_methods[] = { { "update", (PyCFunction)client_update, METH_VARARGS|METH_KEYWORDS, "S.update(path, rev=None, recurse=True, ignore_externals=False) -> list of revnums" }, { "list", (PyCFunction)client_list, METH_VARARGS|METH_KEYWORDS, "S.list(path, peg_revision, depth, dirents=ra.DIRENT_ALL, revision=None) -> list of directory entries" }, { "diff", (PyCFunction)client_diff, METH_VARARGS|METH_KEYWORDS, "S.diff(rev1, rev2, path1=None, path2=None, relative_to_dir=None, diffopts=[], encoding=\"utf-8\", ignore_ancestry=True, no_diff_deleted=True, ignore_content_type=False) -> unified diff as a string" }, - { "mkdir", (PyCFunction)client_mkdir, METH_VARARGS|METH_KEYWORDS, "S.mkdir(paths, make_parents=False, revprops=None) -> (revnum, date, author)" }, + { "mkdir", (PyCFunction)client_mkdir, METH_VARARGS|METH_KEYWORDS, "S.mkdir(paths, make_parents=False, revprops=None, callback=None)" }, { "log", (PyCFunction)client_log, METH_VARARGS|METH_KEYWORDS, "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, -- cgit v1.2.3 From 87be1241fbea3924e48cabd0acd9cd11a4170b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 07:47:07 +0000 Subject: Support new style svn.client.copy. --- subvertpy/client.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/subvertpy/client.c b/subvertpy/client.c index 0a4138c4..c3900230 100644 --- a/subvertpy/client.c +++ b/subvertpy/client.c @@ -1082,7 +1082,9 @@ static PyObject *client_mkdir(PyObject *self, PyObject *args, PyObject *kwargs) PyObject *paths, *revprops = NULL; bool make_parents = false; apr_pool_t *temp_pool; +#if ONLY_BEFORE_SVN(1, 7) svn_commit_info_t *commit_info = NULL; +#endif PyObject *ret; apr_array_header_t *apr_paths; apr_hash_t *hash_revprops; @@ -1146,7 +1148,9 @@ static PyObject *client_mkdir(PyObject *self, PyObject *args, PyObject *kwargs) client->client, temp_pool)); #endif +#if ONLY_BEFORE_SVN(1, 7) INVOKE_COMMIT_CALLBACK(temp_pool, commit_info, callback); +#endif apr_pool_destroy(temp_pool); @@ -1157,16 +1161,25 @@ static PyObject *client_copy(PyObject *self, PyObject *args, PyObject *kwargs) { char *src_path, *dst_path; PyObject *src_rev = Py_None; +#if ONLY_BEFORE_SVN(1, 7) svn_commit_info_t *commit_info = NULL; +#endif apr_pool_t *temp_pool; svn_opt_revision_t c_src_rev; bool copy_as_child = true, make_parents = false; PyObject *ret; apr_hash_t *revprops; bool ignore_externals = false; + bool metadata_only = false; ClientObject *client = (ClientObject *)self; + PyObject *callback = Py_None; + bool pin_externals = false; +#if ONLY_SINCE_SVN(1, 9) + apr_hash_t *pinned_externals = NULL; +#endif char *kwnames[] = { "src_path", "dst_path", "src_rev", "copy_as_child", - "make_parents", "ignore_externals", "revprpos", NULL }; + "make_parents", "ignore_externals", "revprops", "metadata_only", + "pin_externals", "callback", NULL }; #if ONLY_SINCE_SVN(1, 4) PyObject *py_revprops = Py_None; @@ -1176,9 +1189,10 @@ static PyObject *client_copy(PyObject *self, PyObject *args, PyObject *kwargs) svn_client_copy_source_t src; #endif - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss|ObbbO", kwnames, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss|ObbbbOOO", kwnames, &src_path, &dst_path, &src_rev, ©_as_child, &make_parents, - &ignore_externals, &py_revprops)) + &ignore_externals, &py_revprops, &metadata_only, &pin_externals, + &callback)) return NULL; if (!to_opt_revision(src_rev, &c_src_rev)) return NULL; @@ -1224,6 +1238,21 @@ static PyObject *client_copy(PyObject *self, PyObject *args, PyObject *kwargs) return NULL; } #endif +#if ONLY_BEFORE_SVN(1, 9) + if (metadata_only) { + PyErr_SetString(PyExc_NotImplementedError, + "metadata_only not supported in svn < 1.9"); + apr_pool_destroy(temp_pool); + return NULL; + } + if (pin_externals != Py_None) { + PyErr_SetString(PyExc_NotImplementedError, + "pin_externals not supported in svn < 1.9"); + apr_pool_destroy(temp_pool); + return NULL; + } + // TODO(jelmer): Set pinned_externals +#endif #if ONLY_SINCE_SVN(1, 5) src.path = src_path; src.revision = src.peg_revision = &c_src_rev; @@ -1235,7 +1264,18 @@ static PyObject *client_copy(PyObject *self, PyObject *args, PyObject *kwargs) } APR_ARRAY_IDX(src_paths, 0, svn_client_copy_source_t *) = &src; #endif -#if ONLY_SINCE_SVN(1, 6) +#if ONLY_SINCE_SVN(1, 9) + RUN_SVN_WITH_POOL(temp_pool, svn_client_copy7(src_paths, + dst_path, copy_as_child, make_parents, + ignore_externals, metadata_only, pin_externals, + pinned_externals, revprops, py_commit_callback2, + callback, client->client, temp_pool)); +#elif ONLY_SINCE_SVN(1, 7) + RUN_SVN_WITH_POOL(temp_pool, svn_client_copy6(src_paths, + dst_path, copy_as_child, make_parents, + ignore_externals, revprops, py_commit_callback2, callback, + client->client, temp_pool)); +#elif ONLY_SINCE_SVN(1, 6) RUN_SVN_WITH_POOL(temp_pool, svn_client_copy5(&commit_info, src_paths, dst_path, copy_as_child, make_parents, ignore_externals, revprops, client->client, temp_pool)); @@ -1247,7 +1287,9 @@ static PyObject *client_copy(PyObject *self, PyObject *args, PyObject *kwargs) RUN_SVN_WITH_POOL(temp_pool, svn_client_copy2(&commit_info, src_path, &c_src_rev, dst_path, client->client, temp_pool)); #endif - ret = py_commit_info_tuple(commit_info); +#if ONLY_BEFORE_SVN(1, 7) + INVOKE_COMMIT_CALLBACK(temp_pool, commit_info, callback); +#endif apr_pool_destroy(temp_pool); return ret; } -- cgit v1.2.3 From 6c5b4e27640bfcd7aca00c790691bdaf9dfd965f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 07:52:43 +0000 Subject: Support new style svn.client.update. --- subvertpy/client.c | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/subvertpy/client.c b/subvertpy/client.c index c3900230..452e0fec 100644 --- a/subvertpy/client.c +++ b/subvertpy/client.c @@ -1552,15 +1552,19 @@ static PyObject *client_update(PyObject *self, PyObject *args, PyObject *kwargs) PyObject *ret; int i = 0; ClientObject *client = (ClientObject *)self; - bool allow_unver_obstructions = false, - depth_is_sticky = false; + bool allow_unver_obstructions = false; + bool depth_is_sticky = false; + bool adds_as_modification = true; + bool make_parents = false; char *kwnames[] = { "path", "revision", "recurse", "ignore_externals", "depth_is_sticky", - "allow_unver_obstructions", NULL }; + "allow_unver_obstructions", "adds_as_modification", "make_parents", + NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Obbbb", kwnames, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Obbbbb", kwnames, &paths, &rev, &recurse, &ignore_externals, - &depth_is_sticky, &allow_unver_obstructions)) + &depth_is_sticky, &allow_unver_obstructions, &adds_as_modification, + &make_parents)) return NULL; if (!to_opt_revision(rev, &c_rev)) @@ -1572,7 +1576,30 @@ static PyObject *client_update(PyObject *self, PyObject *args, PyObject *kwargs) apr_pool_destroy(temp_pool); return NULL; } -#if ONLY_SINCE_SVN(1, 5) + +#if ONLY_BEFORE_SVN(1, 7) + if (!adds_as_modification) { + PyErr_SetString(PyExc_NotImplementedError, + "!adds_as_modification not supported before svn 1.7"); + apr_pool_destroy(temp_pool); + return NULL; + } + + if (make_parents) { + PyErr_SetString(PyExc_NotImplementedError, + "make_parents not supported before svn 1.7"); + apr_pool_destroy(temp_pool); + return NULL; + } +#endif + +#if ONLY_SINCE_SVN(1, 7) + RUN_SVN_WITH_POOL(temp_pool, svn_client_update4(&result_revs, + apr_paths, &c_rev, recurse?svn_depth_infinity:svn_depth_files, + depth_is_sticky?TRUE:FALSE, ignore_externals, allow_unver_obstructions?TRUE:FALSE, + adds_as_modification?TRUE:FALSE, make_parents?TRUE:FALSE, + client->client, temp_pool)); +#elif ONLY_SINCE_SVN(1, 5) RUN_SVN_WITH_POOL(temp_pool, svn_client_update3(&result_revs, apr_paths, &c_rev, recurse?svn_depth_infinity:svn_depth_files, depth_is_sticky?TRUE:FALSE, ignore_externals, allow_unver_obstructions?TRUE:FALSE, -- cgit v1.2.3 From b216fc03283cb52bec8d8e972b2f32c75e51ec6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 09:42:49 +0000 Subject: Split wc.c. --- Makefile | 3 + setup.py | 6 +- subvertpy/wc.c | 2609 +++++++--------------------------------------------- subvertpy/wc.h | 17 + subvertpy/wc_adm.c | 2037 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 2368 insertions(+), 2304 deletions(-) create mode 100644 subvertpy/wc_adm.c diff --git a/Makefile b/Makefile index bad12947..dff0eb45 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,9 @@ all: build build-inplace build:: $(SETUP) build +build-nodeprecated: + $(MAKE) build CFLAGS+=-Wno-deprecated-declarations + build-inplace:: $(SETUP) build_ext --inplace diff --git a/setup.py b/setup.py index de5f979d..4b44cf60 100755 --- a/setup.py +++ b/setup.py @@ -396,7 +396,8 @@ def subvertpy_modules(): SvnExtension( "subvertpy.client", [source_path(n) - for n in ("client.c", "editor.c", "util.c", "_ra.c", "wc.c")], + for n in ("client.c", "editor.c", "util.c", "_ra.c", "wc.c", + "wc_adm.c")], libraries=["svn_client-1", "svn_subr-1", "svn_ra-1", "svn_wc-1"]), SvnExtension( "subvertpy._ra", @@ -407,7 +408,8 @@ def subvertpy_modules(): libraries=["svn_repos-1", "svn_subr-1", "svn_fs-1"]), SvnExtension( "subvertpy.wc", - [source_path(n) for n in ("wc.c", "util.c", "editor.c")], + [source_path(n) for n in + ["wc.c", "wc_adm.c", "util.c", "editor.c"]], libraries=["svn_wc-1", "svn_subr-1"]) ] diff --git a/subvertpy/wc.c b/subvertpy/wc.c index bf7e7c46..b10b812d 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -34,18 +34,8 @@ #define T_BOOL T_BYTE #endif -#if ONLY_SINCE_SVN(1, 5) -#define REPORTER_T svn_ra_reporter3_t -#else -#define REPORTER_T svn_ra_reporter2_t -#endif - static PyTypeObject Entry_Type; static PyTypeObject Status_Type; -static PyTypeObject Adm_Type; - -static PyObject *py_entry(const svn_wc_entry_t *entry); -static PyObject *py_status(const svn_wc_status2_t *status); #if ONLY_BEFORE_SVN(1, 5) struct svn_wc_committed_queue_t @@ -55,10 +45,6 @@ struct svn_wc_committed_queue_t svn_boolean_t have_recursive; }; -#if SVN_VER_MINOR < 5 -typedef struct svn_wc_committed_queue_t svn_wc_committed_queue_t; -#endif - typedef struct { const char *path; @@ -69,9 +55,6 @@ typedef struct unsigned char *digest; } committed_queue_item_t; -#if SVN_VER_MINOR < 5 -static -#endif svn_wc_committed_queue_t *svn_wc_committed_queue_create(apr_pool_t *pool) { svn_wc_committed_queue_t *q; @@ -84,9 +67,6 @@ svn_wc_committed_queue_t *svn_wc_committed_queue_create(apr_pool_t *pool) return q; } -#if SVN_VER_MINOR < 5 -static -#endif svn_error_t *svn_wc_queue_committed(svn_wc_committed_queue_t **queue, const char *path, svn_wc_adm_access_t *adm_access, @@ -126,10 +106,14 @@ typedef struct { apr_pool_t *pool; svn_wc_committed_queue_t *queue; } CommittedQueueObject; -static PyTypeObject CommittedQueue_Type; + +svn_wc_committed_queue_t *PyObject_GetCommittedQueue(PyObject *obj) +{ + return ((CommittedQueueObject *)obj)->queue; +} #if ONLY_SINCE_SVN(1, 5) -static svn_error_t *py_ra_report_set_path(void *baton, const char *path, svn_revnum_t revision, svn_depth_t depth, int start_empty, const char *lock_token, apr_pool_t *pool) +static svn_error_t *py_ra_report3_set_path(void *baton, const char *path, svn_revnum_t revision, svn_depth_t depth, int start_empty, const char *lock_token, apr_pool_t *pool) { PyObject *self = (PyObject *)baton, *py_lock_token, *ret; PyGILState_STATE state = PyGILState_Ensure(); @@ -147,7 +131,7 @@ static svn_error_t *py_ra_report_set_path(void *baton, const char *path, svn_rev return NULL; } -static svn_error_t *py_ra_report_link_path(void *report_baton, const char *path, const char *url, svn_revnum_t revision, svn_depth_t depth, int start_empty, const char *lock_token, apr_pool_t *pool) +static svn_error_t *py_ra_report3_link_path(void *report_baton, const char *path, const char *url, svn_revnum_t revision, svn_depth_t depth, int start_empty, const char *lock_token, apr_pool_t *pool) { PyObject *self = (PyObject *)report_baton, *ret, *py_lock_token; PyGILState_STATE state = PyGILState_Ensure(); @@ -165,9 +149,9 @@ static svn_error_t *py_ra_report_link_path(void *report_baton, const char *path, return NULL; } +#endif -#else -static svn_error_t *py_ra_report_set_path(void *baton, const char *path, svn_revnum_t revision, int start_empty, const char *lock_token, apr_pool_t *pool) +static svn_error_t *py_ra_report2_set_path(void *baton, const char *path, svn_revnum_t revision, int start_empty, const char *lock_token, apr_pool_t *pool) { PyObject *self = (PyObject *)baton, *py_lock_token, *ret; PyGILState_STATE state = PyGILState_Ensure(); @@ -184,7 +168,7 @@ static svn_error_t *py_ra_report_set_path(void *baton, const char *path, svn_rev return NULL; } -static svn_error_t *py_ra_report_link_path(void *report_baton, const char *path, const char *url, svn_revnum_t revision, int start_empty, const char *lock_token, apr_pool_t *pool) +static svn_error_t *py_ra_report2_link_path(void *report_baton, const char *path, const char *url, svn_revnum_t revision, int start_empty, const char *lock_token, apr_pool_t *pool) { PyObject *self = (PyObject *)report_baton, *ret, *py_lock_token; PyGILState_STATE state = PyGILState_Ensure(); @@ -201,9 +185,6 @@ static svn_error_t *py_ra_report_link_path(void *report_baton, const char *path, return NULL; } - -#endif - static svn_error_t *py_ra_report_delete_path(void *baton, const char *path, apr_pool_t *pool) { PyObject *self = (PyObject *)baton, *ret; @@ -237,14 +218,23 @@ static svn_error_t *py_ra_report_abort(void *baton, apr_pool_t *pool) return NULL; } -static const REPORTER_T py_ra_reporter = { - py_ra_report_set_path, +#if ONLY_SINCE_SVN(1, 5) +const svn_ra_reporter3_t py_ra_reporter3 = { + py_ra_report3_set_path, py_ra_report_delete_path, - py_ra_report_link_path, + py_ra_report3_link_path, py_ra_report_finish, py_ra_report_abort, }; +#endif +const svn_ra_reporter2_t py_ra_reporter2 = { + py_ra_report2_set_path, + py_ra_report_delete_path, + py_ra_report2_link_path, + py_ra_report_finish, + py_ra_report_abort, +}; /** @@ -273,57 +263,6 @@ static PyObject *api_version(PyObject *self) ver->patch, ver->tag); } -static svn_error_t *py_wc_found_entry(const char *path, const svn_wc_entry_t *entry, void *walk_baton, apr_pool_t *pool) -{ - PyObject *fn, *ret; - PyObject *callbacks = (PyObject *)walk_baton; - PyGILState_STATE state = PyGILState_Ensure(); - if (PyTuple_Check(callbacks)) { - fn = PyTuple_GET_ITEM(callbacks, 0); - } else { - fn = (PyObject *)walk_baton; - } - ret = PyObject_CallFunction(fn, "sO", path, py_entry(entry)); - CB_CHECK_PYRETVAL(ret); - Py_DECREF(ret); - PyGILState_Release(state); - return NULL; -} - -#if ONLY_SINCE_SVN(1, 5) - -svn_error_t *py_wc_handle_error(const char *path, svn_error_t *err, void *walk_baton, apr_pool_t *pool) -{ - PyObject *fn, *ret; - PyObject *py_err; - PyGILState_STATE state; - PyObject *callbacks = (PyObject *)walk_baton; - if (PyTuple_Check(callbacks)) { - fn = PyTuple_GET_ITEM(callbacks, 1); - } else { - return err; - } - state = PyGILState_Ensure(); - py_err = PyErr_NewSubversionException(err); - ret = PyObject_CallFunction(fn, "sO", path, py_err); - CB_CHECK_PYRETVAL(ret); - Py_DECREF(ret); - PyGILState_Release(state); - Py_DECREF(py_err); - return NULL; -} - -static svn_wc_entry_callbacks2_t py_wc_entry_callbacks2 = { - py_wc_found_entry, - py_wc_handle_error, -}; -#else -static svn_wc_entry_callbacks_t py_wc_entry_callbacks = { - py_wc_found_entry -}; - -#endif - void py_wc_notify_func(void *baton, const svn_wc_notify_t *notify, apr_pool_t *pool) { @@ -339,2259 +278,114 @@ void py_wc_notify_func(void *baton, const svn_wc_notify_t *notify, apr_pool_t *p /* If ret was NULL, the cancel func should abort the operation. */ } } - -typedef struct { - PyObject_VAR_HEAD - apr_pool_t *pool; - svn_wc_entry_t entry; -} EntryObject; - -static void entry_dealloc(PyObject *self) +bool py_dict_to_wcprop_changes(PyObject *dict, apr_pool_t *pool, apr_array_header_t **ret) { - apr_pool_destroy(((EntryObject *)self)->pool); - PyObject_Del(self); -} - -static PyMemberDef entry_members[] = { - { "name", T_STRING, offsetof(EntryObject, entry.name), READONLY, - "Name of the file"}, - { "copyfrom_url", T_STRING, offsetof(EntryObject, entry.copyfrom_url), READONLY, - "Copyfrom location" }, - { "copyfrom_rev", T_LONG, offsetof(EntryObject, entry.copyfrom_rev), READONLY, - "Copyfrom revision" }, - { "uuid", T_STRING, offsetof(EntryObject, entry.uuid), READONLY, - "UUID of repository" }, - { "url", T_STRING, offsetof(EntryObject, entry.url), READONLY, - "URL in repository" }, - { "repos", T_STRING, offsetof(EntryObject, entry.repos), READONLY, - "Canonical repository URL" }, - { "schedule", T_INT, offsetof(EntryObject, entry.schedule), READONLY, - "Scheduling (add, replace, delete, etc)" }, - { "kind", T_INT, offsetof(EntryObject, entry.kind), READONLY, - "Kind of file (file, dir, etc)" }, - { "revision", T_LONG, offsetof(EntryObject, entry.revision), READONLY, - "Base revision", }, - { "cmt_rev", T_LONG, offsetof(EntryObject, entry.cmt_rev), READONLY, - "Last revision this was changed" }, - { "checksum", T_STRING, offsetof(EntryObject, entry.checksum), READONLY, - "Hex MD5 checksum for the untranslated text base file" }, - { "cmt_date", T_LONG, offsetof(EntryObject, entry.cmt_date), READONLY, - "Last date this was changed" }, - { "cmt_author", T_STRING, offsetof(EntryObject, entry.cmt_author), READONLY, - "Last commit author of this item" }, - { NULL, } -}; - -static PyTypeObject Entry_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "wc.Entry", /* const char *tp_name; For printing, in format "." */ - sizeof(EntryObject), - 0,/* Py_ssize_t tp_basicsize, tp_itemsize; For allocation */ - - /* Methods to implement standard operations */ - - entry_dealloc, /* destructor tp_dealloc; */ - NULL, /* printfunc tp_print; */ - NULL, /* getattrfunc tp_getattr; */ - NULL, /* setattrfunc tp_setattr; */ - NULL, /* cmpfunc tp_compare; */ - NULL, /* reprfunc tp_repr; */ - - /* Method suites for standard classes */ - - NULL, /* PyNumberMethods *tp_as_number; */ - NULL, /* PySequenceMethods *tp_as_sequence; */ - NULL, /* PyMappingMethods *tp_as_mapping; */ - - /* More standard operations (here for binary compatibility) */ - - NULL, /* hashfunc tp_hash; */ - NULL, /* ternaryfunc tp_call; */ - NULL, /* reprfunc tp_str; */ - NULL, /* getattrofunc tp_getattro; */ - NULL, /* setattrofunc tp_setattro; */ - - /* Functions to access object as input/output buffer */ - NULL, /* PyBufferProcs *tp_as_buffer; */ - - /* Flags to define presence of optional/expanded features */ - 0, /* long tp_flags; */ - - NULL, /* const char *tp_doc; Documentation string */ - - /* Assigned meaning in release 2.0 */ - /* call function for all accessible objects */ - NULL, /* traverseproc tp_traverse; */ - - /* delete references to contained objects */ - NULL, /* inquiry tp_clear; */ - - /* Assigned meaning in release 2.1 */ - /* rich comparisons */ - NULL, /* richcmpfunc tp_richcompare; */ - - /* weak reference enabler */ - 0, /* Py_ssize_t tp_weaklistoffset; */ - - /* Added in release 2.2 */ - /* Iterators */ - NULL, /* getiterfunc tp_iter; */ - NULL, /* iternextfunc tp_iternext; */ - - /* Attribute descriptor and subclassing stuff */ - NULL, /* struct PyMethodDef *tp_methods; */ - entry_members, /* struct PyMemberDef *tp_members; */ - -}; + PyObject *key, *val; + Py_ssize_t idx; -static PyObject *py_entry(const svn_wc_entry_t *entry) -{ - EntryObject *ret; + if (dict == Py_None) { + *ret = NULL; + return true; + } - ret = PyObject_New(EntryObject, &Entry_Type); - if (ret == NULL) - return NULL; + if (!PyDict_Check(dict)) { + PyErr_SetString(PyExc_TypeError, "Expected dictionary with property changes"); + return false; + } - ret->pool = Pool(NULL); - if (ret->pool == NULL) - return NULL; - ret->entry = *svn_wc_entry_dup(entry, ret->pool); - return (PyObject *)ret; -} + *ret = apr_array_make(pool, PyDict_Size(dict), sizeof(char *)); -typedef struct { - PyObject_VAR_HEAD - apr_pool_t *pool; - svn_wc_status2_t status; - PyObject *entry; -} StatusObject; + while (PyDict_Next(dict, &idx, &key, &val)) { + svn_prop_t *prop = apr_palloc(pool, sizeof(svn_prop_t)); + prop->name = py_object_to_svn_string(key, pool); + if (prop->name == NULL) { + return false; + } + if (val == Py_None) { + prop->value = NULL; + } else { + if (!PyBytes_Check(val)) { + PyErr_SetString(PyExc_TypeError, "property values should be bytes"); + return false; + } + prop->value = svn_string_ncreate(PyBytes_AsString(val), PyBytes_Size(val), pool); + } + APR_ARRAY_PUSH(*ret, svn_prop_t *) = prop; + } -static void status_dealloc(PyObject *self) -{ - apr_pool_destroy(((StatusObject *)self)->pool); - Py_XDECREF(((StatusObject *)self)->entry); - PyObject_Del(self); + return true; } -static PyMemberDef status_members[] = { - { "entry", T_OBJECT, offsetof(StatusObject, entry), READONLY, - "Can be NULL if not under version control." }, - { "locked", T_BOOL, offsetof(StatusObject, status.locked), READONLY, - "a directory can be 'locked' if a working copy update was interrupted." }, - { "copied", T_BOOL, offsetof(StatusObject, status.copied), READONLY, - "a file or directory can be 'copied' if it's scheduled for addition-with-history (or part of a subtree that is scheduled as such.)." }, - { "switched", T_BOOL, offsetof(StatusObject, status.switched), READONLY, - "a file or directory can be 'switched' if the switch command has been used." }, - { "url", T_STRING, offsetof(StatusObject, status.url), READONLY, - "URL (actual or expected) in repository" }, - { "revision", T_LONG, offsetof(StatusObject, status.ood_last_cmt_rev), READONLY, - "Set to the youngest committed revision, or SVN_INVALID_REVNUM if not out of date.", }, - { "kind", T_INT, offsetof(StatusObject, status.ood_kind), READONLY, - "Set to the node kind of the youngest commit, or svn_node_none if not out of date.", }, - { "status", T_INT, offsetof(StatusObject, status.text_status), READONLY, - "The status of the entry.", }, - { NULL, } -}; - -static PyTypeObject Status_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "wc.Status", /* const char *tp_name; For printing, in format "." */ - sizeof(StatusObject), - 0,/* Py_ssize_t tp_basicsize, tp_itemsize; For allocation */ - - /* Methods to implement standard operations */ - - status_dealloc, /* destructor tp_dealloc; */ - NULL, /* printfunc tp_print; */ - NULL, /* getattrfunc tp_getattr; */ - NULL, /* setattrfunc tp_setattr; */ - NULL, /* cmpfunc tp_compare; */ - NULL, /* reprfunc tp_repr; */ - - /* Method suites for standard classes */ - - NULL, /* PyNumberMethods *tp_as_number; */ - NULL, /* PySequenceMethods *tp_as_sequence; */ - NULL, /* PyMappingMethods *tp_as_mapping; */ - - /* More standard operations (here for binary compatibility) */ - - NULL, /* hashfunc tp_hash; */ - NULL, /* ternaryfunc tp_call; */ - NULL, /* reprfunc tp_str; */ - NULL, /* getattrofunc tp_getattro; */ - NULL, /* setattrofunc tp_setattro; */ - - /* Functions to access object as input/output buffer */ - NULL, /* PyBufferProcs *tp_as_buffer; */ - - /* Flags to define presence of optional/expanded features */ - 0, /* long tp_flags; */ - - "Working copy status object", /* const char *tp_doc; Documentation string */ - - /* Assigned meaning in release 2.0 */ - /* call function for all accessible objects */ - NULL, /* traverseproc tp_traverse; */ - - /* delete references to contained objects */ - NULL, /* inquiry tp_clear; */ - - /* Assigned meaning in release 2.1 */ - /* rich comparisons */ - NULL, /* richcmpfunc tp_richcompare; */ - - /* weak reference enabler */ - 0, /* Py_ssize_t tp_weaklistoffset; */ - - /* Added in release 2.2 */ - /* Iterators */ - NULL, /* getiterfunc tp_iter; */ - NULL, /* iternextfunc tp_iternext; */ - - /* Attribute descriptor and subclassing stuff */ - NULL, /* struct PyMethodDef *tp_methods; */ - status_members, /* struct PyMemberDef *tp_members; */ - -}; - -static PyObject *py_status(const svn_wc_status2_t *status) +#if ONLY_SINCE_SVN(1, 6) +svn_error_t *wc_validator3(void *baton, const char *uuid, const char *url, const char *root_url, apr_pool_t *pool) { - StatusObject *ret; - svn_wc_status2_t *dup_status; - - ret = PyObject_New(StatusObject, &Status_Type); - if (ret == NULL) - return NULL; + PyObject *py_validator = baton, *ret; - ret->pool = Pool(NULL); - if (ret->pool == NULL) { - PyObject_Del(ret); + if (py_validator == Py_None) { return NULL; } - dup_status = svn_wc_dup_status2(status, ret->pool); - if (dup_status == NULL) - { - PyErr_NoMemory(); - return NULL; + ret = PyObject_CallFunction(py_validator, "sss", uuid, url, root_url); + if (ret == NULL) { + return py_svn_error(); } - ret->status = *dup_status; - ret->entry = py_entry(ret->status.entry); - return (PyObject *)ret; -} + Py_DECREF(ret); -typedef struct { - PyObject_VAR_HEAD - svn_wc_adm_access_t *adm; - apr_pool_t *pool; -} AdmObject; + return NULL; +} -#define ADM_CHECK_CLOSED(adm_obj) \ - if (adm_obj->adm == NULL) { \ - PyErr_SetString(PyExc_RuntimeError, "WorkingCopy instance already closed"); \ - return NULL; \ - } +#endif -static PyObject *adm_init(PyTypeObject *self, PyObject *args, PyObject *kwargs) +svn_error_t *wc_validator2(void *baton, const char *uuid, const char *url, svn_boolean_t root, apr_pool_t *pool) { - PyObject *associated, *py_path; - const char *path; - bool write_lock=false; - int depth=0; - svn_wc_adm_access_t *parent_wc; - svn_error_t *err; - AdmObject *ret; - char *kwnames[] = { "associated", "path", "write_lock", "depth", NULL }; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bi", kwnames, - &associated, &py_path, &write_lock, &depth)) - return NULL; - - ret = PyObject_New(AdmObject, &Adm_Type); - if (ret == NULL) - return NULL; + PyObject *py_validator = baton, *ret; - ret->pool = Pool(NULL); - if (ret->pool == NULL) { + if (py_validator == Py_None) { return NULL; } - if (associated == Py_None) { - parent_wc = NULL; - } else { - parent_wc = ((AdmObject *)associated)->adm; - } - path = py_object_to_svn_dirent(py_path, ret->pool); - if (path == NULL) { - Py_DECREF(ret); - return NULL; + ret = PyObject_CallFunction(py_validator, "ssO", uuid, url, Py_None); + if (ret == NULL) { + return py_svn_error(); } - Py_BEGIN_ALLOW_THREADS - err = svn_wc_adm_open3(&ret->adm, parent_wc, - path, - write_lock, depth, py_cancel_check, NULL, - ret->pool); - Py_END_ALLOW_THREADS - - if (err != NULL) { - handle_svn_error(err); - svn_error_clear(err); - Py_DECREF(ret); - return NULL; - } + Py_DECREF(ret); - return (PyObject *)ret; -} - -static PyObject *adm_access_path(PyObject *self) -{ - AdmObject *admobj = (AdmObject *)self; - ADM_CHECK_CLOSED(admobj); - return py_object_from_svn_abspath(svn_wc_adm_access_path(admobj->adm)); -} - -static PyObject *adm_locked(PyObject *self) -{ - AdmObject *admobj = (AdmObject *)self; - ADM_CHECK_CLOSED(admobj); - return PyBool_FromLong(svn_wc_adm_locked(admobj->adm)); -} - -static PyObject *adm_prop_get(PyObject *self, PyObject *args) -{ - char *name; - const char *path; - AdmObject *admobj = (AdmObject *)self; - const svn_string_t *value; - apr_pool_t *temp_pool; - PyObject *ret, *py_path; - - if (!PyArg_ParseTuple(args, "sO", &name, &py_path)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_get(&value, name, path, admobj->adm, temp_pool)); - if (value == NULL || value->data == NULL) { - ret = Py_None; - Py_INCREF(ret); - } else { - ret = PyBytes_FromStringAndSize(value->data, value->len); - } - apr_pool_destroy(temp_pool); - return ret; -} - -static PyObject *adm_prop_set(PyObject *self, PyObject *args) -{ - char *name, *value; - const char *path; - AdmObject *admobj = (AdmObject *)self; - bool skip_checks=false; - apr_pool_t *temp_pool; - int vallen; - svn_string_t *cvalue; - PyObject *py_path; - PyObject *notify_func = Py_None; - - if (!PyArg_ParseTuple(args, "sz#O|bO", &name, &value, &vallen, &py_path, &skip_checks, - ¬ify_func)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - if (value == NULL) { - cvalue = NULL; - } else { - cvalue = svn_string_ncreate(value, vallen, temp_pool); - } -#if ONLY_SINCE_SVN(1, 6) - RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_set3(name, cvalue, path, admobj->adm, - skip_checks, py_wc_notify_func, (void *)notify_func, - temp_pool)); -#else - RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_set2(name, cvalue, path, admobj->adm, - skip_checks, temp_pool)); -#endif - apr_pool_destroy(temp_pool); - - Py_RETURN_NONE; -} - -static PyObject *adm_entries_read(PyObject *self, PyObject *args) -{ - apr_hash_t *entries; - AdmObject *admobj = (AdmObject *)self; - apr_pool_t *temp_pool; - bool show_hidden=false; - apr_hash_index_t *idx; - const char *key; - apr_ssize_t klen; - svn_wc_entry_t *entry; - PyObject *py_entries, *obj; - - if (!PyArg_ParseTuple(args, "|b", &show_hidden)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) - return NULL; - RUN_SVN_WITH_POOL(temp_pool, svn_wc_entries_read(&entries, admobj->adm, - show_hidden, temp_pool)); - py_entries = PyDict_New(); - if (py_entries == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - idx = apr_hash_first(temp_pool, entries); - while (idx != NULL) { - apr_hash_this(idx, (const void **)&key, &klen, (void **)&entry); - if (entry == NULL) { - obj = Py_None; - Py_INCREF(obj); - } else { - obj = py_entry(entry); - } - PyDict_SetItemString(py_entries, key, obj); - Py_DECREF(obj); - idx = apr_hash_next(idx); - } - apr_pool_destroy(temp_pool); - return py_entries; -} - -static PyObject *adm_walk_entries(PyObject *self, PyObject *args) -{ - const char *path; - PyObject *callbacks; - bool show_hidden=false; - apr_pool_t *temp_pool; - AdmObject *admobj = (AdmObject *)self; - svn_depth_t depth = svn_depth_infinity; - PyObject *py_path; - - if (!PyArg_ParseTuple(args, "OO|bi", &py_path, &callbacks, &show_hidden, &depth)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - -#if ONLY_SINCE_SVN(1, 5) - RUN_SVN_WITH_POOL(temp_pool, svn_wc_walk_entries3( - path, admobj->adm, - &py_wc_entry_callbacks2, (void *)callbacks, - depth, show_hidden, py_cancel_check, NULL, - temp_pool)); -#else - if (depth != svn_depth_infinity) { - PyErr_SetString(PyExc_NotImplementedError, - "depth != infinity not supported for svn < 1.5"); - apr_pool_destroy(temp_pool); - return NULL; - } - RUN_SVN_WITH_POOL(temp_pool, svn_wc_walk_entries2( - path, admobj->adm, - &py_wc_entry_callbacks, (void *)callbacks, - show_hidden, py_cancel_check, NULL, - temp_pool)); -#endif - apr_pool_destroy(temp_pool); - - Py_RETURN_NONE; -} - -static PyObject *adm_entry(PyObject *self, PyObject *args) -{ - const char *path; - PyObject *py_path; - bool show_hidden=false; - apr_pool_t *temp_pool; - AdmObject *admobj = (AdmObject *)self; - const svn_wc_entry_t *entry; - PyObject *ret; - - if (!PyArg_ParseTuple(args, "O|b", &py_path, &show_hidden)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - RUN_SVN_WITH_POOL(temp_pool, svn_wc_entry(&entry, path, admobj->adm, show_hidden, temp_pool)); - - if (entry == NULL) { - PyErr_Format(PyExc_KeyError, "No such entry '%s'", path); - ret = NULL; - } else { - ret = py_entry(entry); - } - - apr_pool_destroy(temp_pool); - return ret; -} - -static PyObject *adm_get_prop_diffs(PyObject *self, PyObject *args) -{ - const char *path; - apr_pool_t *temp_pool; - apr_array_header_t *propchanges; - apr_hash_t *original_props; - PyObject *py_path; - AdmObject *admobj = (AdmObject *)self; - svn_prop_t el; - int i; - PyObject *py_propchanges, *py_orig_props, *pyval; - - if (!PyArg_ParseTuple(args, "O", &py_path)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_prop_diffs(&propchanges, &original_props, - path, admobj->adm, temp_pool)); - py_propchanges = PyList_New(propchanges->nelts); - if (py_propchanges == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - for (i = 0; i < propchanges->nelts; i++) { - el = APR_ARRAY_IDX(propchanges, i, svn_prop_t); - if (el.value != NULL) - pyval = Py_BuildValue("(sz#)", el.name, el.value->data, el.value->len); - else - pyval = Py_BuildValue("(sO)", el.name, Py_None); - if (pyval == NULL) { - apr_pool_destroy(temp_pool); - Py_DECREF(py_propchanges); - return NULL; - } - if (PyList_SetItem(py_propchanges, i, pyval) != 0) { - Py_DECREF(py_propchanges); - apr_pool_destroy(temp_pool); - return NULL; - } - } - py_orig_props = prop_hash_to_dict(original_props); - apr_pool_destroy(temp_pool); - if (py_orig_props == NULL) { - Py_DECREF(py_propchanges); - return NULL; - } - return Py_BuildValue("(NN)", py_propchanges, py_orig_props); -} - -static PyObject *adm_add(PyObject *self, PyObject *args, PyObject *kwargs) -{ - const char *path; - char *copyfrom_url=NULL; - svn_revnum_t copyfrom_rev=-1; - char *kwnames[] = { "path", "copyfrom_url", "copyfrom_rev", "notify_func", "depth", NULL }; - PyObject *notify_func=Py_None, *py_path; - AdmObject *admobj = (AdmObject *)self; - apr_pool_t *temp_pool; - svn_depth_t depth = svn_depth_infinity; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|zlOi", kwnames, &py_path, - ©from_url, ©from_rev, ¬ify_func, &depth)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - -#if ONLY_SINCE_SVN(1, 6) - RUN_SVN_WITH_POOL(temp_pool, svn_wc_add3( - path, admobj->adm, - depth, svn_uri_canonicalize(copyfrom_url, temp_pool), - copyfrom_rev, py_cancel_check, NULL, - py_wc_notify_func, - (void *)notify_func, - temp_pool)); -#else - if (depth != svn_depth_infinity) { - PyErr_SetString(PyExc_NotImplementedError, "depth != infinity not supported on svn < 1.6"); - apr_pool_destroy(temp_pool); - return NULL; - } - RUN_SVN_WITH_POOL(temp_pool, svn_wc_add2( - path, admobj->adm, copyfrom_url, - copyfrom_rev, py_cancel_check, - py_wc_notify_func, - (void *)notify_func, - temp_pool)); -#endif - apr_pool_destroy(temp_pool); - - Py_RETURN_NONE; -} - -static PyObject *adm_copy(PyObject *self, PyObject *args) -{ - AdmObject *admobj = (AdmObject *)self; - char *src, *dst; - PyObject *notify_func=Py_None; - apr_pool_t *temp_pool; - - if (!PyArg_ParseTuple(args, "ss|O", &src, &dst, ¬ify_func)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) - return NULL; - RUN_SVN_WITH_POOL(temp_pool, svn_wc_copy2(src, admobj->adm, dst, - py_cancel_check, NULL, - py_wc_notify_func, (void *)notify_func, - temp_pool)); - apr_pool_destroy(temp_pool); - - Py_RETURN_NONE; -} - -static PyObject *adm_delete(PyObject *self, PyObject *args, PyObject *kwargs) -{ - AdmObject *admobj = (AdmObject *)self; - apr_pool_t *temp_pool; - char *kwnames[] = { "path", "notify_func", "keep_local", - NULL }; - const char *path; - PyObject *py_path; - PyObject *notify_func=Py_None; - bool keep_local = false; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Ob:delete", kwnames, - &py_path, ¬ify_func, &keep_local)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - -#if ONLY_SINCE_SVN(1, 5) - RUN_SVN_WITH_POOL(temp_pool, svn_wc_delete3(path, admobj->adm, - py_cancel_check, NULL, - py_wc_notify_func, (void *)notify_func, - keep_local, - temp_pool)); -#else - if (keep_local) { - PyErr_SetString(PyExc_NotImplementedError, - "keep_local not supported on Subversion < 1.5"); - return NULL; - } - - RUN_SVN_WITH_POOL(temp_pool, svn_wc_delete2(path, admobj->adm, - py_cancel_check, NULL, - py_wc_notify_func, (void *)notify_func, - temp_pool)); -#endif - apr_pool_destroy(temp_pool); - - Py_RETURN_NONE; -} - -static PyObject *adm_crawl_revisions(PyObject *self, PyObject *args, PyObject *kwargs) -{ - const char *path; - PyObject *reporter; - bool restore_files=true, recurse=true, use_commit_times=true; - PyObject *notify_func=Py_None; - apr_pool_t *temp_pool; - AdmObject *admobj = (AdmObject *)self; - svn_wc_traversal_info_t *traversal_info; - bool depth_compatibility_trick = false; - bool honor_depth_exclude = false; - char *kwnames[] = { "path", "reporter", "restore_files", "recurse", "use_commit_times", "notify_func", "depth_compatibility_trick", "honor_depth_exclude,", NULL }; - PyObject *py_path; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bbbObb", kwnames, &py_path, - &reporter, &restore_files, &recurse, &use_commit_times, - ¬ify_func, &depth_compatibility_trick, &honor_depth_exclude)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - traversal_info = svn_wc_init_traversal_info(temp_pool); -#if ONLY_SINCE_SVN(1, 6) - RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions4(path, admobj->adm, - &py_ra_reporter, (void *)reporter, - restore_files, recurse?svn_depth_infinity:svn_depth_files, - honor_depth_exclude?TRUE:FALSE, - depth_compatibility_trick?TRUE:FALSE, use_commit_times, - py_wc_notify_func, (void *)notify_func, - traversal_info, temp_pool)); -#elif ONLY_SINCE_SVN(1, 5) - RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions3(path, admobj->adm, - &py_ra_reporter, (void *)reporter, - restore_files, recurse?svn_depth_infinity:svn_depth_files, - depth_compatibility_trick, use_commit_times, - py_wc_notify_func, (void *)notify_func, - traversal_info, temp_pool)); -#else - RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions2(path, admobj->adm, - &py_ra_reporter, (void *)reporter, - restore_files, recurse, use_commit_times, - py_wc_notify_func, (void *)notify_func, - traversal_info, temp_pool)); -#endif - apr_pool_destroy(temp_pool); - - Py_RETURN_NONE; -} - -static void wc_done_handler(void *self) -{ - AdmObject *admobj = (AdmObject *)self; - - Py_DECREF(admobj); -} - -static PyObject *adm_get_update_editor(PyObject *self, PyObject *args) -{ - char *target; - bool use_commit_times=true, recurse=true; - PyObject * notify_func=Py_None; - char *diff3_cmd=NULL; - const svn_delta_editor_t *editor; - AdmObject *admobj = (AdmObject *)self; - void *edit_baton; - apr_pool_t *pool; - svn_revnum_t *latest_revnum; - svn_error_t *err; - bool allow_unver_obstructions = false; - bool depth_is_sticky = false; - - if (!PyArg_ParseTuple(args, "s|bbOzbb", &target, &use_commit_times, - &recurse, ¬ify_func, &diff3_cmd, &depth_is_sticky, - &allow_unver_obstructions)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - pool = Pool(NULL); - if (pool == NULL) - return NULL; - latest_revnum = (svn_revnum_t *)apr_palloc(pool, sizeof(svn_revnum_t)); - Py_BEGIN_ALLOW_THREADS -#if ONLY_SINCE_SVN(1, 5) - /* FIXME: Support all values of depth */ - /* FIXME: Support fetch_func */ - /* FIXME: Support conflict func */ - err = svn_wc_get_update_editor3(latest_revnum, admobj->adm, target, - use_commit_times, recurse?svn_depth_infinity:svn_depth_files, - depth_is_sticky?TRUE:FALSE, allow_unver_obstructions?TRUE:FALSE, - py_wc_notify_func, (void *)notify_func, - py_cancel_check, NULL, - NULL, NULL, NULL, NULL, - diff3_cmd, NULL, &editor, &edit_baton, - NULL, pool); -#else - if (allow_unver_obstructions) { - PyErr_SetString(PyExc_NotImplementedError, - "allow_unver_obstructions is not supported in svn < 1.5"); - apr_pool_destroy(pool); - PyEval_RestoreThread(_save); - return NULL; - } - if (depth_is_sticky) { - PyErr_SetString(PyExc_NotImplementedError, - "depth_is_sticky is not supported in svn < 1.5"); - apr_pool_destroy(pool); - PyEval_RestoreThread(_save); - return NULL; - } - err = svn_wc_get_update_editor2(latest_revnum, admobj->adm, target, - use_commit_times, recurse, py_wc_notify_func, (void *)notify_func, - py_cancel_check, NULL, diff3_cmd, &editor, &edit_baton, - NULL, pool); -#endif - Py_END_ALLOW_THREADS - if (err != NULL) { - handle_svn_error(err); - svn_error_clear(err); - apr_pool_destroy(pool); - return NULL; - } - Py_INCREF(admobj); - return new_editor_object(NULL, editor, edit_baton, pool, &Editor_Type, - wc_done_handler, admobj, NULL); -} - -static bool py_dict_to_wcprop_changes(PyObject *dict, apr_pool_t *pool, apr_array_header_t **ret) -{ - PyObject *key, *val; - Py_ssize_t idx; - - if (dict == Py_None) { - *ret = NULL; - return true; - } - - if (!PyDict_Check(dict)) { - PyErr_SetString(PyExc_TypeError, "Expected dictionary with property changes"); - return false; - } - - *ret = apr_array_make(pool, PyDict_Size(dict), sizeof(char *)); - - while (PyDict_Next(dict, &idx, &key, &val)) { - svn_prop_t *prop = apr_palloc(pool, sizeof(svn_prop_t)); - prop->name = py_object_to_svn_string(key, pool); - if (prop->name == NULL) { - return false; - } - if (val == Py_None) { - prop->value = NULL; - } else { - if (!PyBytes_Check(val)) { - PyErr_SetString(PyExc_TypeError, "property values should be bytes"); - return false; - } - prop->value = svn_string_ncreate(PyBytes_AsString(val), PyBytes_Size(val), pool); - } - APR_ARRAY_PUSH(*ret, svn_prop_t *) = prop; - } - - return true; -} - -static PyObject *adm_has_binary_prop(PyObject *self, PyObject *args) -{ - const char *path; - svn_boolean_t binary; - AdmObject *admobj = (AdmObject *)self; - apr_pool_t *temp_pool; - PyObject *py_path; - - if (!PyArg_ParseTuple(args, "O", &py_path)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - RUN_SVN_WITH_POOL(temp_pool, svn_wc_has_binary_prop(&binary, path, admobj->adm, temp_pool)); - - apr_pool_destroy(temp_pool); - - return PyBool_FromLong(binary); -} - -static PyObject *adm_process_committed(PyObject *self, PyObject *args, PyObject *kwargs) -{ - const char *path; - char *rev_date = NULL, *rev_author = NULL; - bool recurse, remove_lock = false; - unsigned char *digest = NULL; - svn_revnum_t new_revnum; - PyObject *py_wcprop_changes = Py_None, *py_path; - apr_array_header_t *wcprop_changes = NULL; - AdmObject *admobj = (AdmObject *)self; - apr_pool_t *temp_pool; - int digest_len; - bool remove_changelist = false; - char *kwnames[] = { "path", "recurse", "new_revnum", "rev_date", "rev_author", - "wcprop_changes", "remove_lock", "digest", "remove_changelist", NULL }; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oblzz|Obz#b", kwnames, - &py_path, &recurse, &new_revnum, &rev_date, - &rev_author, &py_wcprop_changes, - &remove_lock, &digest, &digest_len, &remove_changelist)) - return NULL; - - PyErr_WarnEx(PyExc_DeprecationWarning, "process_committed is deprecated. Use process_committed_queue instead.", 2); - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - if (!py_dict_to_wcprop_changes(py_wcprop_changes, temp_pool, &wcprop_changes)) { - apr_pool_destroy(temp_pool); - return NULL; - } - -#if ONLY_SINCE_SVN(1, 6) - RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed4( - path, admobj->adm, recurse, new_revnum, - rev_date, rev_author, wcprop_changes, - remove_lock, remove_changelist?TRUE:FALSE, digest, temp_pool)); -#else - if (remove_changelist) { - PyErr_SetString(PyExc_NotImplementedError, "remove_changelist only supported in svn < 1.6"); - apr_pool_destroy(temp_pool); - return NULL; - } - RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed3(path, admobj->adm, recurse, new_revnum, - rev_date, rev_author, wcprop_changes, - remove_lock, digest, temp_pool)); -#endif - - apr_pool_destroy(temp_pool); - - Py_RETURN_NONE; -} - -static PyObject *adm_close(PyObject *self) -{ - AdmObject *admobj = (AdmObject *)self; - if (admobj->adm != NULL) { -#if ONLY_SINCE_SVN(1, 6) - apr_pool_t *temp_pool = Pool(NULL); - Py_BEGIN_ALLOW_THREADS - svn_wc_adm_close2(admobj->adm, temp_pool); - apr_pool_destroy(temp_pool); -#else - Py_BEGIN_ALLOW_THREADS - svn_wc_adm_close(admobj->adm); -#endif - Py_END_ALLOW_THREADS - admobj->adm = NULL; - } - - Py_RETURN_NONE; -} - -static void adm_dealloc(PyObject *self) -{ - apr_pool_destroy(((AdmObject *)self)->pool); - PyObject_Del(self); -} - -static PyObject *adm_repr(PyObject *self) -{ - AdmObject *admobj = (AdmObject *)self; - - if (admobj->adm == NULL) { - return PyRepr_FromFormat("", admobj); - } else { - return PyRepr_FromFormat("", - svn_wc_adm_access_path(admobj->adm)); - } -} - -static PyObject *adm_remove_lock(PyObject *self, PyObject *args) -{ - const char *path; - PyObject *py_path; - AdmObject *admobj = (AdmObject *)self; - apr_pool_t *temp_pool; - - if (!PyArg_ParseTuple(args, "O", &py_path)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - RUN_SVN_WITH_POOL(temp_pool, svn_wc_remove_lock(path, admobj->adm, temp_pool)) - - apr_pool_destroy(temp_pool); - - Py_RETURN_NONE; -} - -static PyObject *get_ancestry(PyObject *self, PyObject *args) -{ - const char *path; - char *url; - svn_revnum_t rev; - apr_pool_t *temp_pool; - PyObject *py_path; - AdmObject *admobj = (AdmObject *)self; - - if (!PyArg_ParseTuple(args, "O", &py_path)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_ancestry(&url, &rev, path, admobj->adm, temp_pool)); - - apr_pool_destroy(temp_pool); - - return Py_BuildValue("(si)", url, rev); -} - -static PyObject *maybe_set_repos_root(PyObject *self, PyObject *args) -{ - char *path, *repos; - apr_pool_t *temp_pool; - AdmObject *admobj = (AdmObject *)self; - - if (!PyArg_ParseTuple(args, "ss", &path, &repos)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) - return NULL; - - RUN_SVN_WITH_POOL(temp_pool, svn_wc_maybe_set_repos_root(admobj->adm, path, repos, temp_pool)); - - Py_RETURN_NONE; -} - -static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs) -{ - char *kwnames[] = { "dst_path", "new_base_contents", "new_contents", - "new_base_props", "new_props", "copyfrom_url", "copyfrom_rev", - "notify", NULL }; - AdmObject *admobj = (AdmObject *)self; - apr_pool_t *temp_pool; - char *dst_path, *copyfrom_url = NULL; - svn_revnum_t copyfrom_rev = -1; - PyObject *py_new_base_contents, *py_new_contents, *py_new_base_props, - *py_new_props, *notify = Py_None; - svn_stream_t *new_contents, *new_base_contents; - apr_hash_t *new_props, *new_base_props; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sOOOO|zlO", kwnames, - &dst_path, &py_new_base_contents, &py_new_contents, &py_new_base_props, - &py_new_props, ©from_url, ©from_rev, ¬ify)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) - return NULL; - - new_base_props = prop_dict_to_hash(temp_pool, py_new_base_props); - - new_props = prop_dict_to_hash(temp_pool, py_new_props); - - new_base_contents = new_py_stream(temp_pool, py_new_base_contents); - - new_contents = new_py_stream(temp_pool, py_new_contents); - -#if ONLY_SINCE_SVN(1, 6) - RUN_SVN_WITH_POOL(temp_pool, svn_wc_add_repos_file3(dst_path, admobj->adm, - new_base_contents, - new_contents, - new_base_props, - new_props, - copyfrom_url, copyfrom_rev, - py_cancel_check, NULL, - py_wc_notify_func, notify, temp_pool)); -#else - PyErr_SetString(PyExc_NotImplementedError, - "add_repos_file3 not supported on svn < 1.6"); - apr_pool_destroy(temp_pool); -#endif - - apr_pool_destroy(temp_pool); - - Py_RETURN_NONE; -} - -static PyObject *mark_missing_deleted(PyObject *self, PyObject *args) -{ - const char *path; - AdmObject *admobj = (AdmObject *)self; - apr_pool_t *temp_pool; - PyObject *py_path; - - if (!PyArg_ParseTuple(args, "O", &py_path)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - RUN_SVN_WITH_POOL(temp_pool, svn_wc_mark_missing_deleted(path, admobj->adm, temp_pool)); - - apr_pool_destroy(temp_pool); - - Py_RETURN_NONE; -} - -static PyObject *remove_from_revision_control(PyObject *self, PyObject *args) -{ - char *name; - bool destroy_wf = false, instant_error = false; - AdmObject *admobj = (AdmObject *)self; - apr_pool_t *temp_pool; - - if (!PyArg_ParseTuple(args, "s|bb", &name, &destroy_wf, &instant_error)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) - return NULL; - - RUN_SVN_WITH_POOL(temp_pool, - svn_wc_remove_from_revision_control(admobj->adm, name, - destroy_wf?TRUE:FALSE, instant_error?TRUE:FALSE, py_cancel_check, NULL, temp_pool)); - - apr_pool_destroy(temp_pool); - - Py_RETURN_NONE; -} - -#if ONLY_SINCE_SVN(1, 6) -static svn_error_t *wc_validator3(void *baton, const char *uuid, const char *url, const char *root_url, apr_pool_t *pool) -{ - PyObject *py_validator = baton, *ret; - - if (py_validator == Py_None) { - return NULL; - } - - ret = PyObject_CallFunction(py_validator, "sss", uuid, url, root_url); - if (ret == NULL) { - return py_svn_error(); - } - - Py_DECREF(ret); - - return NULL; -} - -#else - -static svn_error_t *wc_validator2(void *baton, const char *uuid, const char *url, svn_boolean_t root, apr_pool_t *pool) -{ - PyObject *py_validator = baton, *ret; - - if (py_validator == Py_None) { - return NULL; - } - - ret = PyObject_CallFunction(py_validator, "ssO", uuid, url, Py_None); - if (ret == NULL) { - return py_svn_error(); - } - - Py_DECREF(ret); - - return NULL; -} - -#endif - -static PyObject *relocate(PyObject *self, PyObject *args) -{ - const char *path; - char *from, *to; - AdmObject *admobj = (AdmObject *)self; - apr_pool_t *temp_pool; - bool recurse = true; - PyObject *py_validator = Py_None, *py_path; - - if (!PyArg_ParseTuple(args, "Oss|bO:relocate", &py_path, &from, &to, &recurse, - &py_validator)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - -#if ONLY_SINCE_SVN(1, 6) - RUN_SVN_WITH_POOL(temp_pool, svn_wc_relocate3(path, admobj->adm, from, to, recurse?TRUE:FALSE, wc_validator3, py_validator, temp_pool)); -#else - RUN_SVN_WITH_POOL(temp_pool, svn_wc_relocate2(path, admobj->adm, from, to, recurse?TRUE:FALSE, wc_validator2, py_validator, temp_pool)); -#endif - - apr_pool_destroy(temp_pool); - - Py_RETURN_NONE; -} - -static PyObject *crop_tree(PyObject *self, PyObject *args) -{ - char *target; - svn_depth_t depth; - PyObject *notify; - apr_pool_t *temp_pool; - AdmObject *admobj = (AdmObject *)self; - - if (!PyArg_ParseTuple(args, "si|O", &target, &depth, ¬ify)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - -#if ONLY_SINCE_SVN(1, 6) - temp_pool = Pool(NULL); - if (temp_pool == NULL) - return NULL; - - RUN_SVN_WITH_POOL(temp_pool, svn_wc_crop_tree(admobj->adm, - target, depth, py_wc_notify_func, notify, - py_cancel_check, NULL, temp_pool)); - - apr_pool_destroy(temp_pool); - - Py_RETURN_NONE; -#else - PyErr_SetString(PyExc_NotImplementedError, - "crop_tree only available on subversion < 1.6"); - return NULL; -#endif -} - -static PyObject *translated_stream(PyObject *self, PyObject *args) -{ - char *path, *versioned_file; - StreamObject *ret; - svn_stream_t *stream; - AdmObject *admobj = (AdmObject *)self; - apr_pool_t *stream_pool; - int flags; - - if (!PyArg_ParseTuple(args, "ssi", &path, &versioned_file, &flags)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - -#if ONLY_SINCE_SVN(1, 5) - stream_pool = Pool(NULL); - if (stream_pool == NULL) - return NULL; - - RUN_SVN_WITH_POOL(stream_pool, - svn_wc_translated_stream(&stream, path, versioned_file, admobj->adm, - flags, stream_pool)); - - ret = PyObject_New(StreamObject, &Stream_Type); - if (ret == NULL) - return NULL; - - ret->pool = stream_pool; - ret->closed = FALSE; - ret->stream = stream; - - return (PyObject *)ret; -#else - PyErr_SetString(PyExc_NotImplementedError, - "translated_stream() is only available on Subversion >= 1.5"); - return NULL; -#endif -} - -static PyObject *adm_text_modified(PyObject *self, PyObject *args) -{ - const char *path; - bool force_comparison = false; - apr_pool_t *temp_pool; - svn_boolean_t ret; - AdmObject *admobj = (AdmObject *)self; - PyObject *py_path; - - if (!PyArg_ParseTuple(args, "O|b", &py_path, &force_comparison)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - RUN_SVN_WITH_POOL(temp_pool, - svn_wc_text_modified_p(&ret, path, force_comparison?TRUE:FALSE, admobj->adm, - temp_pool)); - - apr_pool_destroy(temp_pool); - - return PyBool_FromLong(ret); -} - -static PyObject *adm_props_modified(PyObject *self, PyObject *args) -{ - const char *path; - apr_pool_t *temp_pool; - svn_boolean_t ret; - AdmObject *admobj = (AdmObject *)self; - PyObject *py_path; - - if (!PyArg_ParseTuple(args, "O", &py_path)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - RUN_SVN_WITH_POOL(temp_pool, - svn_wc_props_modified_p(&ret, path, admobj->adm, temp_pool)); - - apr_pool_destroy(temp_pool); - - return PyBool_FromLong(ret); -} - -static PyObject *adm_process_committed_queue(PyObject *self, PyObject *args) -{ - apr_pool_t *temp_pool; - AdmObject *admobj = (AdmObject *)self; - svn_revnum_t revnum; - char *date, *author; - CommittedQueueObject *py_queue; - - if (!PyArg_ParseTuple(args, "O!lss", &CommittedQueue_Type, &py_queue, - &revnum, &date, &author)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) - return NULL; - -#if ONLY_SINCE_SVN(1, 5) - RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed_queue(py_queue->queue, admobj->adm, revnum, date, author, temp_pool)); -#else - { - int i; - for (i = 0; i < py_queue->queue->queue->nelts; i++) { - committed_queue_item_t *cqi = APR_ARRAY_IDX(py_queue->queue->queue, i, - committed_queue_item_t *); - - RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed3(cqi->path, admobj->adm, - cqi->recurse, revnum, date, author, cqi->wcprop_changes, - cqi->remove_lock, cqi->digest, temp_pool)); - } - } -#endif - apr_pool_destroy(temp_pool); - - Py_RETURN_NONE; -} - -static PyObject *get_actual_target(PyObject *self, PyObject *args) -{ - const char *path; - const char *anchor = NULL, *target = NULL; - apr_pool_t *temp_pool; - PyObject *ret, *py_path; - - if (!PyArg_ParseTuple(args, "O", &py_path)) - return NULL; - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - RUN_SVN_WITH_POOL(temp_pool, - svn_wc_get_actual_target(path, - &anchor, &target, temp_pool)); - - ret = Py_BuildValue("(ss)", anchor, target); - - apr_pool_destroy(temp_pool); - - return ret; -} - -static PyObject *is_wc_root(PyObject *self, PyObject *args) -{ - const char *path; - svn_boolean_t wc_root; - apr_pool_t *temp_pool; - AdmObject *admobj = (AdmObject *)self; - PyObject *py_path; - - if (!PyArg_ParseTuple(args, "O", &py_path)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - RUN_SVN_WITH_POOL(temp_pool, - svn_wc_is_wc_root(&wc_root, path, admobj->adm, temp_pool)); - - apr_pool_destroy(temp_pool); - - return PyBool_FromLong(wc_root); -} - -static PyObject *transmit_text_deltas(PyObject *self, PyObject *args) -{ - const char *path; - const char *tempfile; - bool fulltext; - PyObject *editor_obj, *py_digest, *py_path; - unsigned char digest[APR_MD5_DIGESTSIZE]; - apr_pool_t *temp_pool; - AdmObject *admobj = (AdmObject *)self; - PyObject *ret; - - if (!PyArg_ParseTuple(args, "ObO", &py_path, &fulltext, &editor_obj)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - Py_INCREF(editor_obj); - - RUN_SVN_WITH_POOL(temp_pool, - svn_wc_transmit_text_deltas2(&tempfile, digest, - path, admobj->adm, fulltext?TRUE:FALSE, - &py_editor, editor_obj, temp_pool)); - - py_digest = PyBytes_FromStringAndSize((char *)digest, APR_MD5_DIGESTSIZE); - if (py_digest == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - ret = Py_BuildValue("sN", tempfile, py_digest); - if (ret == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - apr_pool_destroy(temp_pool); - - return ret; -} - -static PyObject *transmit_prop_deltas(PyObject *self, PyObject *args) -{ - const char *path; - PyObject *editor_obj; - apr_pool_t *temp_pool; - AdmObject *admobj = (AdmObject *)self; - PyObject *py_path; - EntryObject *py_entry; - - if (!PyArg_ParseTuple(args, "OO!O", &py_path, &Entry_Type, &py_entry, &editor_obj)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - Py_INCREF(editor_obj); - - RUN_SVN_WITH_POOL(temp_pool, - svn_wc_transmit_prop_deltas(path, - admobj->adm, &(py_entry->entry), &py_editor, editor_obj, NULL, temp_pool)); - - apr_pool_destroy(temp_pool); - - Py_RETURN_NONE; -} - -static PyObject *retrieve(PyObject *self, PyObject *args) -{ - const char *path; - svn_wc_adm_access_t *result; - PyObject *py_path; - AdmObject *admobj = (AdmObject *)self, *ret; - apr_pool_t *pool; - - if (!PyArg_ParseTuple(args, "O", &py_path)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - pool = Pool(NULL); - if (pool == NULL) - return NULL; - - path = py_object_to_svn_dirent(py_path, pool); - if (path == NULL) { - apr_pool_destroy(pool); - return NULL; - } - - RUN_SVN_WITH_POOL(pool, svn_wc_adm_retrieve(&result, admobj->adm, - path, pool)); - - ret = PyObject_New(AdmObject, &Adm_Type); - if (ret == NULL) - return NULL; - - ret->pool = pool; - ret->adm = result; - - return (PyObject *)ret; -} - -static PyObject *probe_retrieve(PyObject *self, PyObject *args) -{ - const char *path; - svn_wc_adm_access_t *result; - AdmObject *admobj = (AdmObject *)self, *ret; - apr_pool_t *pool; - PyObject *py_path; - - if (!PyArg_ParseTuple(args, "O", &py_path)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - pool = Pool(NULL); - if (pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, pool); - if (path == NULL) { - apr_pool_destroy(pool); - return NULL; - } - - RUN_SVN_WITH_POOL(pool, svn_wc_adm_probe_retrieve(&result, admobj->adm, - path, pool)); - - ret = PyObject_New(AdmObject, &Adm_Type); - if (ret == NULL) - return NULL; - - ret->pool = pool; - ret->adm = result; - - return (PyObject *)ret; -} - -static PyObject *probe_try(PyObject *self, PyObject *args) -{ - const char *path; - svn_wc_adm_access_t *result = NULL; - AdmObject *admobj = (AdmObject *)self, *ret; - apr_pool_t *pool; - int levels_to_lock = -1; - bool writelock = false; - PyObject *py_path; - - if (!PyArg_ParseTuple(args, "O|bi", &py_path, &writelock, &levels_to_lock)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - pool = Pool(NULL); - if (pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, pool); - if (path == NULL) { - apr_pool_destroy(pool); - return NULL; - } - - RUN_SVN_WITH_POOL(pool, svn_wc_adm_probe_try3(&result, admobj->adm, - path, writelock, levels_to_lock, - py_cancel_check, NULL, pool)); - - if (result == NULL) { - apr_pool_destroy(pool); - Py_RETURN_NONE; - } - - ret = PyObject_New(AdmObject, &Adm_Type); - if (ret == NULL) - return NULL; - - ret->pool = pool; - ret->adm = result; - - return (PyObject *)ret; -} - -static PyObject *resolved_conflict(PyObject *self, PyObject *args) -{ - AdmObject *admobj = (AdmObject *)self; - apr_pool_t *temp_pool; - bool resolve_props, resolve_tree, resolve_text; - int depth; -#if ONLY_SINCE_SVN(1, 5) - svn_wc_conflict_choice_t conflict_choice; -#else - int conflict_choice; -#endif - PyObject *notify_func = Py_None; - const char *path; - PyObject *py_path; - - if (!PyArg_ParseTuple(args, "Obbbii|O", &py_path, &resolve_text, - &resolve_props, &resolve_tree, &depth, - &conflict_choice, ¬ify_func)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - -#if ONLY_SINCE_SVN(1, 6) - RUN_SVN_WITH_POOL(temp_pool, - svn_wc_resolved_conflict4(path, admobj->adm, resolve_text?TRUE:FALSE, - resolve_props?TRUE:FALSE, resolve_tree?TRUE:FALSE, depth, - conflict_choice, py_wc_notify_func, - (void *)notify_func, py_cancel_check, - NULL, temp_pool)); -#elif ONLY_SINCE_SVN(1, 5) - if (resolve_tree) { - PyErr_SetString(PyExc_NotImplementedError, - "resolve_tree not supported with svn < 1.6"); - apr_pool_destroy(temp_pool); - return NULL; - } else { - RUN_SVN_WITH_POOL(temp_pool, - svn_wc_resolved_conflict3(path, admobj->adm, resolve_text?TRUE:FALSE, - resolve_props?TRUE:FALSE, depth, - conflict_choice, py_wc_notify_func, - (void *)notify_func, py_cancel_check, - NULL, temp_pool)); - } -#else - if (resolve_tree) { - PyErr_SetString(PyExc_NotImplementedError, - "resolve_tree not supported with svn < 1.6"); - apr_pool_destroy(temp_pool); - return NULL; - } else if (depth != svn_depth_infinity && depth != svn_depth_files) { - PyErr_SetString(PyExc_NotImplementedError, - "only infinity and files values for depth are supported"); - apr_pool_destroy(temp_pool); - return NULL; - } else if (conflict_choice != 0) { - PyErr_SetString(PyExc_NotImplementedError, - "conflict choice not supported with svn < 1.5"); - apr_pool_destroy(temp_pool); - return NULL; - } else { - RUN_SVN_WITH_POOL(temp_pool, - svn_wc_resolved_conflict2(path, admobj->adm, resolve_text?TRUE:FALSE, - resolve_props?TRUE:FALSE, - (depth == svn_depth_infinity), - py_wc_notify_func, - (void *)notify_func, py_cancel_check, - NULL, - temp_pool)); - } -#endif - - apr_pool_destroy(temp_pool); - - Py_RETURN_NONE; -} - -static PyObject *conflicted(PyObject *self, PyObject *args) -{ - const char *path; - apr_pool_t *temp_pool; - PyObject *ret; - AdmObject *admobj = (AdmObject *)self; - svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted; - PyObject *py_path; - - if (!PyArg_ParseTuple(args, "O", &py_path)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - -#if ONLY_SINCE_SVN(1, 6) - RUN_SVN_WITH_POOL(temp_pool, svn_wc_conflicted_p2(&text_conflicted, - &prop_conflicted, &tree_conflicted, path, admobj->adm, temp_pool)); - - ret = Py_BuildValue("(bbb)", text_conflicted, prop_conflicted, tree_conflicted); -#else - RUN_SVN_WITH_POOL(temp_pool, svn_wc_conflicted_p(&text_conflicted, - &prop_conflicted, path, admobj->adm, temp_pool)); - - ret = Py_BuildValue("(bbO)", text_conflicted, prop_conflicted, Py_None); -#endif - - apr_pool_destroy(temp_pool); - - return ret; -} - -/** - * Determine the status of a file in the specified working copy. - * - * :return: A status object. - */ -static PyObject *ra_status(PyObject *self, PyObject *args) -{ - const char *path; - svn_wc_status2_t *st; - apr_pool_t *temp_pool; - PyObject *ret; - AdmObject *admobj = (AdmObject *)self; - PyObject *py_path; - - if (!PyArg_ParseTuple(args, "O", &py_path)) - return NULL; - - ADM_CHECK_CLOSED(admobj); - - temp_pool = Pool(NULL); - if (temp_pool == NULL) { - return NULL; - } - - path = py_object_to_svn_dirent(py_path, temp_pool); - if (path == NULL) { - apr_pool_destroy(temp_pool); - return NULL; - } - - RUN_SVN_WITH_POOL(temp_pool, - svn_wc_status2(&st, path, admobj->adm, temp_pool)); - - ret = py_status(st); - - apr_pool_destroy(temp_pool); - - return (PyObject*)ret; -} - -static PyMethodDef adm_methods[] = { - { "prop_set", adm_prop_set, METH_VARARGS, "S.prop_set(name, value, path, skip_checks=False)" }, - { "access_path", (PyCFunction)adm_access_path, METH_NOARGS, - "S.access_path() -> path\n" - "Returns the base path for this working copy handle." }, - { "prop_get", adm_prop_get, METH_VARARGS, "S.prop_get(name, path) -> value" }, - { "entries_read", adm_entries_read, METH_VARARGS, "S.entries_read(include_hidden=False) -> dict" }, - { "walk_entries", adm_walk_entries, METH_VARARGS, - "S.walk_entries(path, callback, show_hidden=False)\n" - "callback should be a function that takes a path and a wc entry" }, - { "locked", (PyCFunction)adm_locked, METH_NOARGS, - "S.locked() -> bool" }, - { "get_prop_diffs", adm_get_prop_diffs, METH_VARARGS, - "S.get_prop_diffs(path) -> (propchanges, originalprops)" }, - { "add", (PyCFunction)adm_add, METH_VARARGS|METH_KEYWORDS, "S.add(path, copyfrom_url=None, copyfrom_rev=-1, notify_func=None)" }, - { "copy", adm_copy, METH_VARARGS, "S.copy(src_path, dest_path, notify_func=None)" }, - { "delete", (PyCFunction)adm_delete, METH_VARARGS|METH_KEYWORDS, "S.delete(path, notify_func=None, keep_local=False)" }, - { "crawl_revisions", (PyCFunction)adm_crawl_revisions, METH_VARARGS|METH_KEYWORDS, - "S.crawl_revisions(path, reporter, restore_files=True, recurse=True, use_commit_times=True, notify_func=None) -> None" }, - { "get_update_editor", adm_get_update_editor, METH_VARARGS, NULL }, - { "close", (PyCFunction)adm_close, METH_NOARGS, - "S.close()" }, - { "entry", (PyCFunction)adm_entry, METH_VARARGS, - "s.entry(path, show_hidden=False) -> entry" }, - { "process_committed", (PyCFunction)adm_process_committed, METH_VARARGS|METH_KEYWORDS, "S.process_committed(path, recurse, new_revnum, rev_date, rev_author, wcprop_changes=None, remove_lock=False, digest=None)" }, - { "process_committed_queue", (PyCFunction)adm_process_committed_queue, METH_VARARGS, "S.process_committed_queue(queue, new_revnum, rev_date, rev_author)" }, - { "remove_lock", (PyCFunction)adm_remove_lock, METH_VARARGS, "S.remove_lock(path)" }, - { "has_binary_prop", (PyCFunction)adm_has_binary_prop, METH_VARARGS, "S.has_binary_prop(path) -> bool" }, - { "text_modified", (PyCFunction)adm_text_modified, METH_VARARGS, "S.text_modified(filename, force_comparison=False) -> bool" }, - { "props_modified", (PyCFunction)adm_props_modified, METH_VARARGS, "S.props_modified(filename) -> bool" }, - { "get_ancestry", (PyCFunction)get_ancestry, METH_VARARGS, - "S.get_ancestry(path) -> (url, rev)" }, - { "maybe_set_repos_root", (PyCFunction)maybe_set_repos_root, METH_VARARGS, "S.maybe_set_repos_root(path, repos)" }, - { "add_repos_file", (PyCFunction)add_repos_file, METH_KEYWORDS, - "S.add_repos_file(dst_path, new_base_contents, new_contents, new_base_props, new_props, copyfrom_url=None, copyfrom_rev=-1, notify_func=None)" }, - { "mark_missing_deleted", (PyCFunction)mark_missing_deleted, METH_VARARGS, - "S.mark_missing_deleted(path)" }, - { "remove_from_revision_control", (PyCFunction)remove_from_revision_control, METH_VARARGS, - "S.remove_from_revision_control(name, destroy_wf=False, instant_error=False)" }, - { "relocate", (PyCFunction)relocate, METH_VARARGS, - "S.relocate(path, from, to, recurse=TRUE, validator=None)" }, - { "crop_tree", (PyCFunction)crop_tree, METH_VARARGS, - "S.crop_tree(target, depth, notify_func=None, cancel=None)" }, - { "translated_stream", (PyCFunction)translated_stream, METH_VARARGS, - "S.translated_stream(path, versioned_file, flags) -> stream" }, - { "is_wc_root", (PyCFunction)is_wc_root, METH_VARARGS, - "S.is_wc_root(path) -> wc_root" }, - { "transmit_text_deltas", (PyCFunction)transmit_text_deltas, METH_VARARGS, - "S.transmit_text_deltas(fulltext, editor) -> (tempfile, digest)" }, - { "transmit_prop_deltas", (PyCFunction)transmit_prop_deltas, METH_VARARGS, - "S.transmit_prop_deltas(path, entry, editor)" }, - { "probe_retrieve", (PyCFunction)probe_retrieve, METH_VARARGS, - "S.probe_retrieve(path) -> WorkingCopy" }, - { "retrieve", (PyCFunction)retrieve, METH_VARARGS, - "S.retrieve(path) -> WorkingCopy" }, - { "probe_try", (PyCFunction)probe_try, METH_VARARGS, - "S.probe_try(path, write_lock=False, levels_to_lock=-1)" }, - { "conflicted", (PyCFunction)conflicted, METH_VARARGS, - "S.conflicted(path) -> (text_conflicted, prop_conflicted, tree_conflicted)" }, - { "resolved_conflict", (PyCFunction)resolved_conflict, METH_VARARGS, - "S.resolved_conflict(path, resolve_text, resolve_props, resolve_tree, depth, conflict_choice, notify_func=None, cancel=None)" }, - { "status", (PyCFunction)ra_status, METH_VARARGS, "status(wc_path) -> Status" }, - { NULL, } -}; - -static PyTypeObject Adm_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "wc.WorkingCopy", /* const char *tp_name; For printing, in format "." */ - sizeof(AdmObject), - 0,/* Py_ssize_t tp_basicsize, tp_itemsize; For allocation */ - - /* Methods to implement standard operations */ - - adm_dealloc, /* destructor tp_dealloc; */ - NULL, /* printfunc tp_print; */ - NULL, /* getattrfunc tp_getattr; */ - NULL, /* setattrfunc tp_setattr; */ - NULL, /* cmpfunc tp_compare; */ - adm_repr, /* reprfunc tp_repr; */ - - /* Method suites for standard classes */ - - NULL, /* PyNumberMethods *tp_as_number; */ - NULL, /* PySequenceMethods *tp_as_sequence; */ - NULL, /* PyMappingMethods *tp_as_mapping; */ - - /* More standard operations (here for binary compatibility) */ - - NULL, /* hashfunc tp_hash; */ - NULL, /* ternaryfunc tp_call; */ - adm_repr, /* reprfunc tp_repr; */ - NULL, /* getattrofunc tp_getattro; */ - NULL, /* setattrofunc tp_setattro; */ - - /* Functions to access object as input/output buffer */ - NULL, /* PyBufferProcs *tp_as_buffer; */ - - /* Flags to define presence of optional/expanded features */ - 0, /* long tp_flags; */ - - "Local working copy", /* const char *tp_doc; Documentation string */ - - /* Assigned meaning in release 2.0 */ - /* call function for all accessible objects */ - NULL, /* traverseproc tp_traverse; */ - - /* delete references to contained objects */ - NULL, /* inquiry tp_clear; */ - - /* Assigned meaning in release 2.1 */ - /* rich comparisons */ - NULL, /* richcmpfunc tp_richcompare; */ - - /* weak reference enabler */ - 0, /* Py_ssize_t tp_weaklistoffset; */ - - /* Added in release 2.2 */ - /* Iterators */ - NULL, /* getiterfunc tp_iter; */ - NULL, /* iternextfunc tp_iternext; */ - - /* Attribute descriptor and subclassing stuff */ - adm_methods, /* struct PyMethodDef *tp_methods; */ - NULL, /* struct PyMemberDef *tp_members; */ - NULL, /* struct PyGetSetDef *tp_getset; */ - NULL, /* struct _typeobject *tp_base; */ - NULL, /* PyObject *tp_dict; */ - NULL, /* descrgetfunc tp_descr_get; */ - NULL, /* descrsetfunc tp_descr_set; */ - 0, /* Py_ssize_t tp_dictoffset; */ - NULL, /* initproc tp_init; */ - NULL, /* allocfunc tp_alloc; */ - adm_init, /* newfunc tp_new; */ -}; - -static void committed_queue_dealloc(PyObject *self) -{ - apr_pool_destroy(((CommittedQueueObject *)self)->pool); - PyObject_Del(self); -} - -static PyObject *committed_queue_repr(PyObject *self) -{ - CommittedQueueObject *cqobj = (CommittedQueueObject *)self; - - return PyRepr_FromFormat("", cqobj->queue); -} - -static PyObject *committed_queue_init(PyTypeObject *self, PyObject *args, PyObject *kwargs) -{ - CommittedQueueObject *ret; - char *kwnames[] = { NULL }; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwnames)) - return NULL; - - ret = PyObject_New(CommittedQueueObject, &CommittedQueue_Type); - if (ret == NULL) - return NULL; - - ret->pool = Pool(NULL); - if (ret->pool == NULL) - return NULL; - ret->queue = svn_wc_committed_queue_create(ret->pool); - if (ret->queue == NULL) { - PyObject_Del(ret); - PyErr_NoMemory(); - return NULL; - } - - return (PyObject *)ret; -} - -static PyObject *committed_queue_queue(CommittedQueueObject *self, PyObject *args, PyObject *kwargs) -{ - char *path; - AdmObject *admobj; - PyObject *py_wcprop_changes = Py_None; - bool remove_lock = false, remove_changelist = false; - char *md5_digest = NULL, *sha1_digest = NULL; - bool recurse = false; - apr_pool_t *temp_pool; - apr_array_header_t *wcprop_changes; - int md5_digest_len, sha1_digest_len; - char *kwnames[] = { "path", "adm", "recurse", "wcprop_changes", "remove_lock", "remove_changelist", "md5_digest", "sha1_digest", NULL }; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO!|bObbz#z#", kwnames, - &path, &Adm_Type, &admobj, - &recurse, &py_wcprop_changes, &remove_lock, - &remove_changelist, &md5_digest, &md5_digest_len, - &sha1_digest, &sha1_digest_len)) - return NULL; - - temp_pool = Pool(NULL); - if (temp_pool == NULL) - return NULL; - - if (!py_dict_to_wcprop_changes(py_wcprop_changes, self->pool, &wcprop_changes)) { - apr_pool_destroy(temp_pool); - return NULL; - } - - path = apr_pstrdup(self->pool, path); - if (path == NULL) { - PyErr_NoMemory(); - return NULL; - } - - if (md5_digest != NULL) { - if (md5_digest_len != APR_MD5_DIGESTSIZE) { - PyErr_SetString(PyExc_ValueError, "Invalid size for md5 digest"); - apr_pool_destroy(temp_pool); - return NULL; - } - md5_digest = apr_pstrdup(temp_pool, md5_digest); - if (md5_digest == NULL) { - PyErr_NoMemory(); - return NULL; - } - } - - if (sha1_digest != NULL) { - if (sha1_digest_len != APR_SHA1_DIGESTSIZE) { - PyErr_SetString(PyExc_ValueError, "Invalid size for sha1 digest"); - apr_pool_destroy(temp_pool); - return NULL; - } - sha1_digest = apr_pstrdup(temp_pool, sha1_digest); - if (sha1_digest == NULL) { - PyErr_NoMemory(); - return NULL; - } - } - -#if ONLY_SINCE_SVN(1, 6) - { - svn_checksum_t svn_checksum, *svn_checksum_p = &svn_checksum; - - if (sha1_digest != NULL) { - svn_checksum.digest = (unsigned char *)sha1_digest; - svn_checksum.kind = svn_checksum_sha1; - } else if (md5_digest != NULL) { - svn_checksum.digest = (unsigned char *)md5_digest; - svn_checksum.kind = svn_checksum_md5; - } else { - svn_checksum_p = NULL; - } - RUN_SVN_WITH_POOL(temp_pool, - svn_wc_queue_committed2(self->queue, path, admobj->adm, recurse?TRUE:FALSE, - wcprop_changes, remove_lock?TRUE:FALSE, remove_changelist?TRUE:FALSE, - svn_checksum_p, temp_pool)); - } -#else - RUN_SVN_WITH_POOL(temp_pool, - svn_wc_queue_committed(&self->queue, path, admobj->adm, recurse?TRUE:FALSE, - wcprop_changes, remove_lock?TRUE:FALSE, remove_changelist?TRUE:FALSE, - (unsigned char *)md5_digest, temp_pool)); -#endif - - apr_pool_destroy(temp_pool); - - Py_RETURN_NONE; + return NULL; } -static PyMethodDef committed_queue_methods[] = { - { "queue", (PyCFunction)committed_queue_queue, METH_VARARGS|METH_KEYWORDS, - "S.queue(path, adm, recurse=False, wcprop_changes=[], remove_lock=False, remove_changelist=False, digest=None)" }, - { NULL } -}; - -static PyTypeObject CommittedQueue_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "wc.CommittedQueue", /* const char *tp_name; For printing, in format "." */ - sizeof(CommittedQueueObject), - 0,/* Py_ssize_t tp_basicsize, tp_itemsize; For allocation */ - - /* Methods to implement standard operations */ - - committed_queue_dealloc, /* destructor tp_dealloc; */ - NULL, /* printfunc tp_print; */ - NULL, /* getattrfunc tp_getattr; */ - NULL, /* setattrfunc tp_setattr; */ - NULL, /* cmpfunc tp_compare; */ - committed_queue_repr, /* reprfunc tp_repr; */ - - /* Method suites for standard classes */ - - NULL, /* PyNumberMethods *tp_as_number; */ - NULL, /* PySequenceMethods *tp_as_sequence; */ - NULL, /* PyMappingMethods *tp_as_mapping; */ - - /* More standard operations (here for binary compatibility) */ - - NULL, /* hashfunc tp_hash; */ - NULL, /* ternaryfunc tp_call; */ - NULL, /* reprfunc tp_str; */ - NULL, /* getattrofunc tp_getattro; */ - NULL, /* setattrofunc tp_setattro; */ - - /* Functions to access object as input/output buffer */ - NULL, /* PyBufferProcs *tp_as_buffer; */ - - /* Flags to define presence of optional/expanded features */ - 0, /* long tp_flags; */ +static PyObject *get_actual_target(PyObject *self, PyObject *args) +{ + const char *path; + const char *anchor = NULL, *target = NULL; + apr_pool_t *temp_pool; + PyObject *ret, *py_path; - "Committed queue", /* const char *tp_doc; Documentation string */ + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; - /* Assigned meaning in release 2.0 */ - /* call function for all accessible objects */ - NULL, /* traverseproc tp_traverse; */ + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } - /* delete references to contained objects */ - NULL, /* inquiry tp_clear; */ + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } - /* Assigned meaning in release 2.1 */ - /* rich comparisons */ - NULL, /* richcmpfunc tp_richcompare; */ + RUN_SVN_WITH_POOL(temp_pool, + svn_wc_get_actual_target(path, + &anchor, &target, temp_pool)); - /* weak reference enabler */ - 0, /* Py_ssize_t tp_weaklistoffset; */ + ret = Py_BuildValue("(ss)", anchor, target); - /* Added in release 2.2 */ - /* Iterators */ - NULL, /* getiterfunc tp_iter; */ - NULL, /* iternextfunc tp_iternext; */ + apr_pool_destroy(temp_pool); - /* Attribute descriptor and subclassing stuff */ - committed_queue_methods, /* struct PyMethodDef *tp_methods; */ - NULL, /* struct PyMemberDef *tp_members; */ - NULL, /* struct PyGetSetDef *tp_getset; */ - NULL, /* struct _typeobject *tp_base; */ - NULL, /* PyObject *tp_dict; */ - NULL, /* descrgetfunc tp_descr_get; */ - NULL, /* descrsetfunc tp_descr_set; */ - 0, /* Py_ssize_t tp_dictoffset; */ - NULL, /* initproc tp_init; */ - NULL, /* allocfunc tp_alloc; */ - committed_queue_init, /* newfunc tp_new; */ -}; + return ret; +} /** * Determine the revision status of a specified working copy. @@ -2908,6 +702,7 @@ static PyObject *cleanup_wc(PyObject *self, PyObject *args, PyObject *kwargs) RUN_SVN_WITH_POOL(temp_pool, svn_wc_cleanup2(path, diff3_cmd, py_cancel_check, NULL, temp_pool)); + apr_pool_destroy(temp_pool); Py_RETURN_NONE; @@ -2982,6 +777,216 @@ static PyMethodDef wc_methods[] = { { NULL, } }; +static void committed_queue_dealloc(PyObject *self) +{ + apr_pool_destroy(((CommittedQueueObject *)self)->pool); + PyObject_Del(self); +} + +static PyObject *committed_queue_repr(PyObject *self) +{ + CommittedQueueObject *cqobj = (CommittedQueueObject *)self; + + return PyRepr_FromFormat("", cqobj->queue); +} + +static PyObject *committed_queue_init(PyTypeObject *self, PyObject *args, PyObject *kwargs) +{ + CommittedQueueObject *ret; + char *kwnames[] = { NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwnames)) + return NULL; + + ret = PyObject_New(CommittedQueueObject, &CommittedQueue_Type); + if (ret == NULL) + return NULL; + + ret->pool = Pool(NULL); + if (ret->pool == NULL) + return NULL; + ret->queue = svn_wc_committed_queue_create(ret->pool); + if (ret->queue == NULL) { + PyObject_Del(ret); + PyErr_NoMemory(); + return NULL; + } + + return (PyObject *)ret; +} + +static PyObject *committed_queue_queue(CommittedQueueObject *self, PyObject *args, PyObject *kwargs) +{ + char *path; + PyObject *admobj; + PyObject *py_wcprop_changes = Py_None; + svn_wc_adm_access_t *adm; + bool remove_lock = false, remove_changelist = false; + char *md5_digest = NULL, *sha1_digest = NULL; + bool recurse = false; + apr_pool_t *temp_pool; + apr_array_header_t *wcprop_changes; + int md5_digest_len, sha1_digest_len; + char *kwnames[] = { "path", "adm", "recurse", "wcprop_changes", "remove_lock", "remove_changelist", "md5_digest", "sha1_digest", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO!|bObbz#z#", kwnames, + &path, &Adm_Type, &admobj, + &recurse, &py_wcprop_changes, &remove_lock, + &remove_changelist, &md5_digest, &md5_digest_len, + &sha1_digest, &sha1_digest_len)) + return NULL; + + temp_pool = Pool(NULL); + if (temp_pool == NULL) + return NULL; + + if (!py_dict_to_wcprop_changes(py_wcprop_changes, self->pool, &wcprop_changes)) { + apr_pool_destroy(temp_pool); + return NULL; + } + + path = apr_pstrdup(self->pool, path); + if (path == NULL) { + PyErr_NoMemory(); + return NULL; + } + + if (md5_digest != NULL) { + if (md5_digest_len != APR_MD5_DIGESTSIZE) { + PyErr_SetString(PyExc_ValueError, "Invalid size for md5 digest"); + apr_pool_destroy(temp_pool); + return NULL; + } + md5_digest = apr_pstrdup(temp_pool, md5_digest); + if (md5_digest == NULL) { + PyErr_NoMemory(); + return NULL; + } + } + + if (sha1_digest != NULL) { + if (sha1_digest_len != APR_SHA1_DIGESTSIZE) { + PyErr_SetString(PyExc_ValueError, "Invalid size for sha1 digest"); + apr_pool_destroy(temp_pool); + return NULL; + } + sha1_digest = apr_pstrdup(temp_pool, sha1_digest); + if (sha1_digest == NULL) { + PyErr_NoMemory(); + return NULL; + } + } + + adm = PyObject_GetAdmAccess(admobj); + +#if ONLY_SINCE_SVN(1, 6) + { + svn_checksum_t svn_checksum, *svn_checksum_p = &svn_checksum; + + if (sha1_digest != NULL) { + svn_checksum.digest = (unsigned char *)sha1_digest; + svn_checksum.kind = svn_checksum_sha1; + } else if (md5_digest != NULL) { + svn_checksum.digest = (unsigned char *)md5_digest; + svn_checksum.kind = svn_checksum_md5; + } else { + svn_checksum_p = NULL; + } + RUN_SVN_WITH_POOL(temp_pool, + svn_wc_queue_committed2(self->queue, path, adm, recurse?TRUE:FALSE, + wcprop_changes, remove_lock?TRUE:FALSE, remove_changelist?TRUE:FALSE, + svn_checksum_p, temp_pool)); + } +#else + RUN_SVN_WITH_POOL(temp_pool, + svn_wc_queue_committed(&self->queue, path, adm, recurse?TRUE:FALSE, + wcprop_changes, remove_lock?TRUE:FALSE, remove_changelist?TRUE:FALSE, + (unsigned char *)md5_digest, temp_pool)); +#endif + + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + +static PyMethodDef committed_queue_methods[] = { + { "queue", (PyCFunction)committed_queue_queue, METH_VARARGS|METH_KEYWORDS, + "S.queue(path, adm, recurse=False, wcprop_changes=[], remove_lock=False, remove_changelist=False, digest=None)" }, + { NULL } +}; + +PyTypeObject CommittedQueue_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "wc.CommittedQueue", /* const char *tp_name; For printing, in format "." */ + sizeof(CommittedQueueObject), + 0,/* Py_ssize_t tp_basicsize, tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + committed_queue_dealloc, /* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + NULL, /* cmpfunc tp_compare; */ + committed_queue_repr, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /* Flags to define presence of optional/expanded features */ + 0, /* long tp_flags; */ + + "Committed queue", /* const char *tp_doc; Documentation string */ + + /* Assigned meaning in release 2.0 */ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /* Assigned meaning in release 2.1 */ + /* rich comparisons */ + NULL, /* richcmpfunc tp_richcompare; */ + + /* weak reference enabler */ + 0, /* Py_ssize_t tp_weaklistoffset; */ + + /* Added in release 2.2 */ + /* Iterators */ + NULL, /* getiterfunc tp_iter; */ + NULL, /* iternextfunc tp_iternext; */ + + /* Attribute descriptor and subclassing stuff */ + committed_queue_methods, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + NULL, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + 0, /* Py_ssize_t tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + committed_queue_init, /* newfunc tp_new; */ +}; + + + static PyObject * moduleinit(void) { @@ -3089,7 +1094,7 @@ moduleinit(void) PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_MERGED", svn_wc_conflict_choose_merged); #endif - PyModule_AddObject(mod, "WorkingCopy", (PyObject *)&Adm_Type); + PyModule_AddObject(mod, "Adm", (PyObject *)&Adm_Type); Py_INCREF(&Adm_Type); PyModule_AddObject(mod, "CommittedQueue", (PyObject *)&CommittedQueue_Type); diff --git a/subvertpy/wc.h b/subvertpy/wc.h index 854611dd..22eb2d70 100644 --- a/subvertpy/wc.h +++ b/subvertpy/wc.h @@ -24,7 +24,24 @@ #pragma GCC visibility push(hidden) #endif +bool py_dict_to_wcprop_changes(PyObject *dict, apr_pool_t *pool, apr_array_header_t **ret); void py_wc_notify_func(void *baton, const svn_wc_notify_t *notify, apr_pool_t *pool); +PyObject *py_wc_status2(const svn_wc_status2_t *status); +#if ONLY_SINCE_SVN(1, 5) +extern const svn_ra_reporter3_t py_ra_reporter3; +#endif +extern const svn_ra_reporter2_t py_ra_reporter2; + +#if ONLY_SINCE_SVN(1, 6) +svn_error_t *wc_validator3(void *baton, const char *uuid, const char *url, const char *root_url, apr_pool_t *pool); +#endif +svn_error_t *wc_validator2(void *baton, const char *uuid, const char *url, svn_boolean_t root, apr_pool_t *pool); +svn_wc_committed_queue_t *PyObject_GetCommittedQueue(PyObject *obj); +extern PyTypeObject CommittedQueue_Type; + +/* Provided by wc_adm.h */ +extern PyTypeObject Adm_Type; +svn_wc_adm_access_t *PyObject_GetAdmAccess(PyObject *obj); #ifdef __GNUC__ #pragma GCC visibility pop diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c new file mode 100644 index 00000000..7f520374 --- /dev/null +++ b/subvertpy/wc_adm.c @@ -0,0 +1,2037 @@ +/* + * Copyright © 2008 Jelmer Vernooij + * -*- coding: utf-8 -*- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "editor.h" +#include "wc.h" + +static svn_wc_entry_callbacks2_t py_wc_entry_callbacks2; +static PyTypeObject Entry_Type; +static PyObject *py_entry(const svn_wc_entry_t *entry); + +PyTypeObject Adm_Type; + +typedef struct { + PyObject_VAR_HEAD + svn_wc_adm_access_t *adm; + apr_pool_t *pool; +} AdmObject; + +typedef struct { + PyObject_VAR_HEAD + apr_pool_t *pool; + svn_wc_entry_t entry; +} EntryObject; + +svn_wc_adm_access_t *Adm_GetAdmAccess(PyObject *obj) { + AdmObject *adm_obj = (AdmObject *)obj; + return adm_obj->adm; +} + +#define ADM_CHECK_CLOSED(adm_obj) \ + if (adm_obj->adm == NULL) { \ + PyErr_SetString(PyExc_RuntimeError, "WorkingCopy instance already closed"); \ + return NULL; \ + } + +svn_wc_adm_access_t *PyObject_GetAdmAccess(PyObject *obj) +{ + return ((AdmObject *)obj)->adm; +} + +static PyObject *adm_init(PyTypeObject *self, PyObject *args, PyObject *kwargs) +{ + PyObject *associated, *py_path; + const char *path; + bool write_lock=false; + int depth=0; + svn_wc_adm_access_t *parent_wc; + svn_error_t *err; + AdmObject *ret; + char *kwnames[] = { "associated", "path", "write_lock", "depth", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bi", kwnames, + &associated, &py_path, &write_lock, &depth)) + return NULL; + + ret = PyObject_New(AdmObject, &Adm_Type); + if (ret == NULL) + return NULL; + + ret->pool = Pool(NULL); + if (ret->pool == NULL) { + return NULL; + } + if (associated == Py_None) { + parent_wc = NULL; + } else { + parent_wc = ((AdmObject *)associated)->adm; + } + + path = py_object_to_svn_dirent(py_path, ret->pool); + if (path == NULL) { + Py_DECREF(ret); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + err = svn_wc_adm_open3(&ret->adm, parent_wc, + path, + write_lock, depth, py_cancel_check, NULL, + ret->pool); + Py_END_ALLOW_THREADS + + if (err != NULL) { + handle_svn_error(err); + svn_error_clear(err); + Py_DECREF(ret); + return NULL; + } + + return (PyObject *)ret; +} + +static PyObject *adm_access_path(PyObject *self) +{ + AdmObject *admobj = (AdmObject *)self; + ADM_CHECK_CLOSED(admobj); + return py_object_from_svn_abspath(svn_wc_adm_access_path(admobj->adm)); +} + +static PyObject *adm_locked(PyObject *self) +{ + AdmObject *admobj = (AdmObject *)self; + ADM_CHECK_CLOSED(admobj); + return PyBool_FromLong(svn_wc_adm_locked(admobj->adm)); +} + +static PyObject *adm_prop_get(PyObject *self, PyObject *args) +{ + char *name; + const char *path; + AdmObject *admobj = (AdmObject *)self; + const svn_string_t *value; + apr_pool_t *temp_pool; + PyObject *ret, *py_path; + + if (!PyArg_ParseTuple(args, "sO", &name, &py_path)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_get(&value, name, path, admobj->adm, temp_pool)); + if (value == NULL || value->data == NULL) { + ret = Py_None; + Py_INCREF(ret); + } else { + ret = PyBytes_FromStringAndSize(value->data, value->len); + } + apr_pool_destroy(temp_pool); + return ret; +} + +static PyObject *adm_prop_set(PyObject *self, PyObject *args) +{ + char *name, *value; + const char *path; + AdmObject *admobj = (AdmObject *)self; + bool skip_checks=false; + apr_pool_t *temp_pool; + int vallen; + svn_string_t *cvalue; + PyObject *py_path; + PyObject *notify_func = Py_None; + + if (!PyArg_ParseTuple(args, "sz#O|bO", &name, &value, &vallen, &py_path, &skip_checks, + ¬ify_func)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + if (value == NULL) { + cvalue = NULL; + } else { + cvalue = svn_string_ncreate(value, vallen, temp_pool); + } +#if ONLY_SINCE_SVN(1, 6) + RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_set3(name, cvalue, path, admobj->adm, + skip_checks, py_wc_notify_func, (void *)notify_func, + temp_pool)); +#else + RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_set2(name, cvalue, path, admobj->adm, + skip_checks, temp_pool)); +#endif + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + +static PyObject *adm_entries_read(PyObject *self, PyObject *args) +{ + apr_hash_t *entries; + AdmObject *admobj = (AdmObject *)self; + apr_pool_t *temp_pool; + bool show_hidden=false; + apr_hash_index_t *idx; + const char *key; + apr_ssize_t klen; + svn_wc_entry_t *entry; + PyObject *py_entries, *obj; + + if (!PyArg_ParseTuple(args, "|b", &show_hidden)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) + return NULL; + RUN_SVN_WITH_POOL(temp_pool, svn_wc_entries_read(&entries, admobj->adm, + show_hidden, temp_pool)); + py_entries = PyDict_New(); + if (py_entries == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + idx = apr_hash_first(temp_pool, entries); + while (idx != NULL) { + apr_hash_this(idx, (const void **)&key, &klen, (void **)&entry); + if (entry == NULL) { + obj = Py_None; + Py_INCREF(obj); + } else { + obj = py_entry(entry); + } + PyDict_SetItemString(py_entries, key, obj); + Py_DECREF(obj); + idx = apr_hash_next(idx); + } + apr_pool_destroy(temp_pool); + return py_entries; +} + +static PyObject *adm_walk_entries(PyObject *self, PyObject *args) +{ + const char *path; + PyObject *callbacks; + bool show_hidden=false; + apr_pool_t *temp_pool; + AdmObject *admobj = (AdmObject *)self; + svn_depth_t depth = svn_depth_infinity; + PyObject *py_path; + + if (!PyArg_ParseTuple(args, "OO|bi", &py_path, &callbacks, &show_hidden, &depth)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + +#if ONLY_SINCE_SVN(1, 5) + RUN_SVN_WITH_POOL(temp_pool, svn_wc_walk_entries3( + path, admobj->adm, + &py_wc_entry_callbacks2, (void *)callbacks, + depth, show_hidden, py_cancel_check, NULL, + temp_pool)); +#else + if (depth != svn_depth_infinity) { + PyErr_SetString(PyExc_NotImplementedError, + "depth != infinity not supported for svn < 1.5"); + apr_pool_destroy(temp_pool); + return NULL; + } + RUN_SVN_WITH_POOL(temp_pool, svn_wc_walk_entries2( + path, admobj->adm, + &py_wc_entry_callbacks, (void *)callbacks, + show_hidden, py_cancel_check, NULL, + temp_pool)); +#endif + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + +static PyObject *adm_entry(PyObject *self, PyObject *args) +{ + const char *path; + PyObject *py_path; + bool show_hidden=false; + apr_pool_t *temp_pool; + AdmObject *admobj = (AdmObject *)self; + const svn_wc_entry_t *entry; + PyObject *ret; + + if (!PyArg_ParseTuple(args, "O|b", &py_path, &show_hidden)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + RUN_SVN_WITH_POOL(temp_pool, svn_wc_entry(&entry, path, admobj->adm, show_hidden, temp_pool)); + + if (entry == NULL) { + PyErr_Format(PyExc_KeyError, "No such entry '%s'", path); + ret = NULL; + } else { + ret = py_entry(entry); + } + + apr_pool_destroy(temp_pool); + return ret; +} + +static PyObject *adm_get_prop_diffs(PyObject *self, PyObject *args) +{ + const char *path; + apr_pool_t *temp_pool; + apr_array_header_t *propchanges; + apr_hash_t *original_props; + PyObject *py_path; + AdmObject *admobj = (AdmObject *)self; + svn_prop_t el; + int i; + PyObject *py_propchanges, *py_orig_props, *pyval; + + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_prop_diffs(&propchanges, &original_props, + path, admobj->adm, temp_pool)); + py_propchanges = PyList_New(propchanges->nelts); + if (py_propchanges == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + for (i = 0; i < propchanges->nelts; i++) { + el = APR_ARRAY_IDX(propchanges, i, svn_prop_t); + if (el.value != NULL) + pyval = Py_BuildValue("(sz#)", el.name, el.value->data, el.value->len); + else + pyval = Py_BuildValue("(sO)", el.name, Py_None); + if (pyval == NULL) { + apr_pool_destroy(temp_pool); + Py_DECREF(py_propchanges); + return NULL; + } + if (PyList_SetItem(py_propchanges, i, pyval) != 0) { + Py_DECREF(py_propchanges); + apr_pool_destroy(temp_pool); + return NULL; + } + } + py_orig_props = prop_hash_to_dict(original_props); + apr_pool_destroy(temp_pool); + if (py_orig_props == NULL) { + Py_DECREF(py_propchanges); + return NULL; + } + return Py_BuildValue("(NN)", py_propchanges, py_orig_props); +} + +static PyObject *adm_add(PyObject *self, PyObject *args, PyObject *kwargs) +{ + const char *path; + char *copyfrom_url=NULL; + svn_revnum_t copyfrom_rev=-1; + char *kwnames[] = { "path", "copyfrom_url", "copyfrom_rev", "notify_func", "depth", NULL }; + PyObject *notify_func=Py_None, *py_path; + AdmObject *admobj = (AdmObject *)self; + apr_pool_t *temp_pool; + svn_depth_t depth = svn_depth_infinity; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|zlOi", kwnames, &py_path, + ©from_url, ©from_rev, ¬ify_func, &depth)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + +#if ONLY_SINCE_SVN(1, 6) + RUN_SVN_WITH_POOL(temp_pool, svn_wc_add3( + path, admobj->adm, + depth, svn_uri_canonicalize(copyfrom_url, temp_pool), + copyfrom_rev, py_cancel_check, NULL, + py_wc_notify_func, + (void *)notify_func, + temp_pool)); +#else + if (depth != svn_depth_infinity) { + PyErr_SetString(PyExc_NotImplementedError, "depth != infinity not supported on svn < 1.6"); + apr_pool_destroy(temp_pool); + return NULL; + } + RUN_SVN_WITH_POOL(temp_pool, svn_wc_add2( + path, admobj->adm, copyfrom_url, + copyfrom_rev, py_cancel_check, + py_wc_notify_func, + (void *)notify_func, + temp_pool)); +#endif + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + +static PyObject *adm_copy(PyObject *self, PyObject *args) +{ + AdmObject *admobj = (AdmObject *)self; + char *src, *dst; + PyObject *notify_func=Py_None; + apr_pool_t *temp_pool; + + if (!PyArg_ParseTuple(args, "ss|O", &src, &dst, ¬ify_func)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) + return NULL; + RUN_SVN_WITH_POOL(temp_pool, svn_wc_copy2(src, admobj->adm, dst, + py_cancel_check, NULL, + py_wc_notify_func, (void *)notify_func, + temp_pool)); + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + +static PyObject *adm_delete(PyObject *self, PyObject *args, PyObject *kwargs) +{ + AdmObject *admobj = (AdmObject *)self; + apr_pool_t *temp_pool; + char *kwnames[] = { "path", "notify_func", "keep_local", + NULL }; + const char *path; + PyObject *py_path; + PyObject *notify_func=Py_None; + bool keep_local = false; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Ob:delete", kwnames, + &py_path, ¬ify_func, &keep_local)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + +#if ONLY_SINCE_SVN(1, 5) + RUN_SVN_WITH_POOL(temp_pool, svn_wc_delete3(path, admobj->adm, + py_cancel_check, NULL, + py_wc_notify_func, (void *)notify_func, + keep_local, + temp_pool)); +#else + if (keep_local) { + PyErr_SetString(PyExc_NotImplementedError, + "keep_local not supported on Subversion < 1.5"); + return NULL; + } + + RUN_SVN_WITH_POOL(temp_pool, svn_wc_delete2(path, admobj->adm, + py_cancel_check, NULL, + py_wc_notify_func, (void *)notify_func, + temp_pool)); +#endif + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + +static PyObject *adm_crawl_revisions(PyObject *self, PyObject *args, PyObject *kwargs) +{ + const char *path; + PyObject *reporter; + bool restore_files=true, recurse=true, use_commit_times=true; + PyObject *notify_func=Py_None; + apr_pool_t *temp_pool; + AdmObject *admobj = (AdmObject *)self; + svn_wc_traversal_info_t *traversal_info; + bool depth_compatibility_trick = false; + bool honor_depth_exclude = false; + char *kwnames[] = { "path", "reporter", "restore_files", "recurse", "use_commit_times", "notify_func", "depth_compatibility_trick", "honor_depth_exclude,", NULL }; + PyObject *py_path; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bbbObb", kwnames, &py_path, + &reporter, &restore_files, &recurse, &use_commit_times, + ¬ify_func, &depth_compatibility_trick, &honor_depth_exclude)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + traversal_info = svn_wc_init_traversal_info(temp_pool); +#if ONLY_SINCE_SVN(1, 6) + RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions4(path, admobj->adm, + &py_ra_reporter3, (void *)reporter, + restore_files, recurse?svn_depth_infinity:svn_depth_files, + honor_depth_exclude?TRUE:FALSE, + depth_compatibility_trick?TRUE:FALSE, use_commit_times, + py_wc_notify_func, (void *)notify_func, + traversal_info, temp_pool)); +#elif ONLY_SINCE_SVN(1, 5) + RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions3(path, admobj->adm, + &py_ra_reporter3, (void *)reporter, + restore_files, recurse?svn_depth_infinity:svn_depth_files, + depth_compatibility_trick, use_commit_times, + py_wc_notify_func, (void *)notify_func, + traversal_info, temp_pool)); +#else + RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions2(path, admobj->adm, + &py_ra_reporter2, (void *)reporter, + restore_files, recurse, use_commit_times, + py_wc_notify_func, (void *)notify_func, + traversal_info, temp_pool)); +#endif + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + +static void wc_done_handler(void *self) +{ + AdmObject *admobj = (AdmObject *)self; + + Py_DECREF(admobj); +} + +static PyObject *adm_get_update_editor(PyObject *self, PyObject *args) +{ + char *target; + bool use_commit_times=true, recurse=true; + PyObject * notify_func=Py_None; + char *diff3_cmd=NULL; + const svn_delta_editor_t *editor; + AdmObject *admobj = (AdmObject *)self; + void *edit_baton; + apr_pool_t *pool; + svn_revnum_t *latest_revnum; + svn_error_t *err; + bool allow_unver_obstructions = false; + bool depth_is_sticky = false; + + if (!PyArg_ParseTuple(args, "s|bbOzbb", &target, &use_commit_times, + &recurse, ¬ify_func, &diff3_cmd, &depth_is_sticky, + &allow_unver_obstructions)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + pool = Pool(NULL); + if (pool == NULL) + return NULL; + latest_revnum = (svn_revnum_t *)apr_palloc(pool, sizeof(svn_revnum_t)); + Py_BEGIN_ALLOW_THREADS +#if ONLY_SINCE_SVN(1, 5) + /* FIXME: Support all values of depth */ + /* FIXME: Support fetch_func */ + /* FIXME: Support conflict func */ + err = svn_wc_get_update_editor3(latest_revnum, admobj->adm, target, + use_commit_times, recurse?svn_depth_infinity:svn_depth_files, + depth_is_sticky?TRUE:FALSE, allow_unver_obstructions?TRUE:FALSE, + py_wc_notify_func, (void *)notify_func, + py_cancel_check, NULL, + NULL, NULL, NULL, NULL, + diff3_cmd, NULL, &editor, &edit_baton, + NULL, pool); +#else + if (allow_unver_obstructions) { + PyErr_SetString(PyExc_NotImplementedError, + "allow_unver_obstructions is not supported in svn < 1.5"); + apr_pool_destroy(pool); + PyEval_RestoreThread(_save); + return NULL; + } + if (depth_is_sticky) { + PyErr_SetString(PyExc_NotImplementedError, + "depth_is_sticky is not supported in svn < 1.5"); + apr_pool_destroy(pool); + PyEval_RestoreThread(_save); + return NULL; + } + err = svn_wc_get_update_editor2(latest_revnum, admobj->adm, target, + use_commit_times, recurse, py_wc_notify_func, (void *)notify_func, + py_cancel_check, NULL, diff3_cmd, &editor, &edit_baton, + NULL, pool); +#endif + Py_END_ALLOW_THREADS + if (err != NULL) { + handle_svn_error(err); + svn_error_clear(err); + apr_pool_destroy(pool); + return NULL; + } + Py_INCREF(admobj); + return new_editor_object(NULL, editor, edit_baton, pool, &Editor_Type, + wc_done_handler, admobj, NULL); +} + +static PyObject *adm_has_binary_prop(PyObject *self, PyObject *args) +{ + const char *path; + svn_boolean_t binary; + AdmObject *admobj = (AdmObject *)self; + apr_pool_t *temp_pool; + PyObject *py_path; + + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + RUN_SVN_WITH_POOL(temp_pool, svn_wc_has_binary_prop(&binary, path, admobj->adm, temp_pool)); + + apr_pool_destroy(temp_pool); + + return PyBool_FromLong(binary); +} + +static PyObject *adm_process_committed(PyObject *self, PyObject *args, PyObject *kwargs) +{ + const char *path; + char *rev_date = NULL, *rev_author = NULL; + bool recurse, remove_lock = false; + unsigned char *digest = NULL; + svn_revnum_t new_revnum; + PyObject *py_wcprop_changes = Py_None, *py_path; + apr_array_header_t *wcprop_changes = NULL; + AdmObject *admobj = (AdmObject *)self; + apr_pool_t *temp_pool; + int digest_len; + bool remove_changelist = false; + char *kwnames[] = { "path", "recurse", "new_revnum", "rev_date", "rev_author", + "wcprop_changes", "remove_lock", "digest", "remove_changelist", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oblzz|Obz#b", kwnames, + &py_path, &recurse, &new_revnum, &rev_date, + &rev_author, &py_wcprop_changes, + &remove_lock, &digest, &digest_len, &remove_changelist)) + return NULL; + + PyErr_WarnEx(PyExc_DeprecationWarning, "process_committed is deprecated. Use process_committed_queue instead.", 2); + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + if (!py_dict_to_wcprop_changes(py_wcprop_changes, temp_pool, &wcprop_changes)) { + apr_pool_destroy(temp_pool); + return NULL; + } + +#if ONLY_SINCE_SVN(1, 6) + RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed4( + path, admobj->adm, recurse, new_revnum, + rev_date, rev_author, wcprop_changes, + remove_lock, remove_changelist?TRUE:FALSE, digest, temp_pool)); +#else + if (remove_changelist) { + PyErr_SetString(PyExc_NotImplementedError, "remove_changelist only supported in svn < 1.6"); + apr_pool_destroy(temp_pool); + return NULL; + } + RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed3(path, admobj->adm, recurse, new_revnum, + rev_date, rev_author, wcprop_changes, + remove_lock, digest, temp_pool)); +#endif + + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + +static PyObject *adm_close(PyObject *self) +{ + AdmObject *admobj = (AdmObject *)self; + if (admobj->adm != NULL) { +#if ONLY_SINCE_SVN(1, 6) + apr_pool_t *temp_pool = Pool(NULL); + Py_BEGIN_ALLOW_THREADS + svn_wc_adm_close2(admobj->adm, temp_pool); + apr_pool_destroy(temp_pool); +#else + Py_BEGIN_ALLOW_THREADS + svn_wc_adm_close(admobj->adm); +#endif + Py_END_ALLOW_THREADS + admobj->adm = NULL; + } + + Py_RETURN_NONE; +} + +static void adm_dealloc(PyObject *self) +{ + apr_pool_destroy(((AdmObject *)self)->pool); + PyObject_Del(self); +} + +static PyObject *adm_repr(PyObject *self) +{ + AdmObject *admobj = (AdmObject *)self; + + if (admobj->adm == NULL) { + return PyRepr_FromFormat("", admobj); + } else { + return PyRepr_FromFormat("", + svn_wc_adm_access_path(admobj->adm)); + } +} + +static PyObject *adm_remove_lock(PyObject *self, PyObject *args) +{ + const char *path; + PyObject *py_path; + AdmObject *admobj = (AdmObject *)self; + apr_pool_t *temp_pool; + + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + RUN_SVN_WITH_POOL(temp_pool, svn_wc_remove_lock(path, admobj->adm, temp_pool)) + + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + +static PyObject *get_ancestry(PyObject *self, PyObject *args) +{ + const char *path; + char *url; + svn_revnum_t rev; + apr_pool_t *temp_pool; + PyObject *py_path; + AdmObject *admobj = (AdmObject *)self; + + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_ancestry(&url, &rev, path, admobj->adm, temp_pool)); + + apr_pool_destroy(temp_pool); + + return Py_BuildValue("(si)", url, rev); +} + +static PyObject *maybe_set_repos_root(PyObject *self, PyObject *args) +{ + char *path, *repos; + apr_pool_t *temp_pool; + AdmObject *admobj = (AdmObject *)self; + + if (!PyArg_ParseTuple(args, "ss", &path, &repos)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) + return NULL; + + RUN_SVN_WITH_POOL(temp_pool, svn_wc_maybe_set_repos_root(admobj->adm, path, repos, temp_pool)); + + Py_RETURN_NONE; +} + +static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs) +{ + char *kwnames[] = { "dst_path", "new_base_contents", "new_contents", + "new_base_props", "new_props", "copyfrom_url", "copyfrom_rev", + "notify", NULL }; + AdmObject *admobj = (AdmObject *)self; + apr_pool_t *temp_pool; + char *dst_path, *copyfrom_url = NULL; + svn_revnum_t copyfrom_rev = -1; + PyObject *py_new_base_contents, *py_new_contents, *py_new_base_props, + *py_new_props, *notify = Py_None; + svn_stream_t *new_contents, *new_base_contents; + apr_hash_t *new_props, *new_base_props; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sOOOO|zlO", kwnames, + &dst_path, &py_new_base_contents, &py_new_contents, &py_new_base_props, + &py_new_props, ©from_url, ©from_rev, ¬ify)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) + return NULL; + + new_base_props = prop_dict_to_hash(temp_pool, py_new_base_props); + + new_props = prop_dict_to_hash(temp_pool, py_new_props); + + new_base_contents = new_py_stream(temp_pool, py_new_base_contents); + + new_contents = new_py_stream(temp_pool, py_new_contents); + +#if ONLY_SINCE_SVN(1, 6) + RUN_SVN_WITH_POOL(temp_pool, svn_wc_add_repos_file3(dst_path, admobj->adm, + new_base_contents, + new_contents, + new_base_props, + new_props, + copyfrom_url, copyfrom_rev, + py_cancel_check, NULL, + py_wc_notify_func, notify, temp_pool)); +#else + PyErr_SetString(PyExc_NotImplementedError, + "add_repos_file3 not supported on svn < 1.6"); + apr_pool_destroy(temp_pool); +#endif + + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + +static PyObject *mark_missing_deleted(PyObject *self, PyObject *args) +{ + const char *path; + AdmObject *admobj = (AdmObject *)self; + apr_pool_t *temp_pool; + PyObject *py_path; + + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + RUN_SVN_WITH_POOL(temp_pool, svn_wc_mark_missing_deleted(path, admobj->adm, temp_pool)); + + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + +static PyObject *remove_from_revision_control(PyObject *self, PyObject *args) +{ + char *name; + bool destroy_wf = false, instant_error = false; + AdmObject *admobj = (AdmObject *)self; + apr_pool_t *temp_pool; + + if (!PyArg_ParseTuple(args, "s|bb", &name, &destroy_wf, &instant_error)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) + return NULL; + + RUN_SVN_WITH_POOL(temp_pool, + svn_wc_remove_from_revision_control(admobj->adm, name, + destroy_wf?TRUE:FALSE, instant_error?TRUE:FALSE, py_cancel_check, NULL, temp_pool)); + + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + +static PyObject *relocate(PyObject *self, PyObject *args) +{ + const char *path; + char *from, *to; + AdmObject *admobj = (AdmObject *)self; + apr_pool_t *temp_pool; + bool recurse = true; + PyObject *py_validator = Py_None, *py_path; + + if (!PyArg_ParseTuple(args, "Oss|bO:relocate", &py_path, &from, &to, &recurse, + &py_validator)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + +#if ONLY_SINCE_SVN(1, 6) + RUN_SVN_WITH_POOL(temp_pool, svn_wc_relocate3(path, admobj->adm, from, to, recurse?TRUE:FALSE, wc_validator3, py_validator, temp_pool)); +#else + RUN_SVN_WITH_POOL(temp_pool, svn_wc_relocate2(path, admobj->adm, from, to, recurse?TRUE:FALSE, wc_validator2, py_validator, temp_pool)); +#endif + + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + +static PyObject *crop_tree(PyObject *self, PyObject *args) +{ + char *target; + svn_depth_t depth; + PyObject *notify; + apr_pool_t *temp_pool; + AdmObject *admobj = (AdmObject *)self; + + if (!PyArg_ParseTuple(args, "si|O", &target, &depth, ¬ify)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + +#if ONLY_SINCE_SVN(1, 6) + temp_pool = Pool(NULL); + if (temp_pool == NULL) + return NULL; + + RUN_SVN_WITH_POOL(temp_pool, svn_wc_crop_tree(admobj->adm, + target, depth, py_wc_notify_func, notify, + py_cancel_check, NULL, temp_pool)); + + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +#else + PyErr_SetString(PyExc_NotImplementedError, + "crop_tree only available on subversion < 1.6"); + return NULL; +#endif +} + +static PyObject *translated_stream(PyObject *self, PyObject *args) +{ + char *path, *versioned_file; + StreamObject *ret; + svn_stream_t *stream; + AdmObject *admobj = (AdmObject *)self; + apr_pool_t *stream_pool; + int flags; + + if (!PyArg_ParseTuple(args, "ssi", &path, &versioned_file, &flags)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + +#if ONLY_SINCE_SVN(1, 5) + stream_pool = Pool(NULL); + if (stream_pool == NULL) + return NULL; + + RUN_SVN_WITH_POOL(stream_pool, + svn_wc_translated_stream(&stream, path, versioned_file, admobj->adm, + flags, stream_pool)); + + ret = PyObject_New(StreamObject, &Stream_Type); + if (ret == NULL) + return NULL; + + ret->pool = stream_pool; + ret->closed = FALSE; + ret->stream = stream; + + return (PyObject *)ret; +#else + PyErr_SetString(PyExc_NotImplementedError, + "translated_stream() is only available on Subversion >= 1.5"); + return NULL; +#endif +} + +static PyObject *adm_text_modified(PyObject *self, PyObject *args) +{ + const char *path; + bool force_comparison = false; + apr_pool_t *temp_pool; + svn_boolean_t ret; + AdmObject *admobj = (AdmObject *)self; + PyObject *py_path; + + if (!PyArg_ParseTuple(args, "O|b", &py_path, &force_comparison)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + RUN_SVN_WITH_POOL(temp_pool, + svn_wc_text_modified_p(&ret, path, force_comparison?TRUE:FALSE, admobj->adm, + temp_pool)); + + apr_pool_destroy(temp_pool); + + return PyBool_FromLong(ret); +} + +static PyObject *adm_props_modified(PyObject *self, PyObject *args) +{ + const char *path; + apr_pool_t *temp_pool; + svn_boolean_t ret; + AdmObject *admobj = (AdmObject *)self; + PyObject *py_path; + + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + RUN_SVN_WITH_POOL(temp_pool, + svn_wc_props_modified_p(&ret, path, admobj->adm, temp_pool)); + + apr_pool_destroy(temp_pool); + + return PyBool_FromLong(ret); +} + +static PyObject *adm_process_committed_queue(PyObject *self, PyObject *args) +{ + apr_pool_t *temp_pool; + AdmObject *admobj = (AdmObject *)self; + svn_revnum_t revnum; + char *date, *author; + PyObject *py_queue; + + if (!PyArg_ParseTuple(args, "O!lss", &CommittedQueue_Type, &py_queue, + &revnum, &date, &author)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) + return NULL; + + svn_wc_committed_queue_t *committed_queue = PyObject_GetCommittedQueue(py_queue); + +#if ONLY_SINCE_SVN(1, 5) + RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed_queue(committed_queue, admobj->adm, revnum, date, author, temp_pool)); +#else + { + int i; + for (i = 0; i < py_queue->queue->queue->nelts; i++) { + committed_queue_item_t *cqi = APR_ARRAY_IDX(committed_queue->queue, i, + committed_queue_item_t *); + + RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed3(cqi->path, admobj->adm, + cqi->recurse, revnum, date, author, cqi->wcprop_changes, + cqi->remove_lock, cqi->digest, temp_pool)); + } + } +#endif + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + + +static PyObject *is_wc_root(PyObject *self, PyObject *args) +{ + const char *path; + svn_boolean_t wc_root; + apr_pool_t *temp_pool; + AdmObject *admobj = (AdmObject *)self; + PyObject *py_path; + + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + RUN_SVN_WITH_POOL(temp_pool, + svn_wc_is_wc_root(&wc_root, path, admobj->adm, temp_pool)); + + apr_pool_destroy(temp_pool); + + return PyBool_FromLong(wc_root); +} + +static PyObject *transmit_text_deltas(PyObject *self, PyObject *args) +{ + const char *path; + const char *tempfile; + bool fulltext; + PyObject *editor_obj, *py_digest, *py_path; + unsigned char digest[APR_MD5_DIGESTSIZE]; + apr_pool_t *temp_pool; + AdmObject *admobj = (AdmObject *)self; + PyObject *ret; + + if (!PyArg_ParseTuple(args, "ObO", &py_path, &fulltext, &editor_obj)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + Py_INCREF(editor_obj); + + RUN_SVN_WITH_POOL(temp_pool, + svn_wc_transmit_text_deltas2(&tempfile, digest, + path, admobj->adm, fulltext?TRUE:FALSE, + &py_editor, editor_obj, temp_pool)); + + py_digest = PyBytes_FromStringAndSize((char *)digest, APR_MD5_DIGESTSIZE); + if (py_digest == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + ret = Py_BuildValue("sN", tempfile, py_digest); + if (ret == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + apr_pool_destroy(temp_pool); + + return ret; +} + +static PyObject *transmit_prop_deltas(PyObject *self, PyObject *args) +{ + const char *path; + PyObject *editor_obj; + apr_pool_t *temp_pool; + AdmObject *admobj = (AdmObject *)self; + PyObject *py_path; + EntryObject *py_entry; + + if (!PyArg_ParseTuple(args, "OO!O", &py_path, &Entry_Type, &py_entry, &editor_obj)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + Py_INCREF(editor_obj); + + RUN_SVN_WITH_POOL(temp_pool, + svn_wc_transmit_prop_deltas(path, + admobj->adm, &(py_entry->entry), &py_editor, editor_obj, NULL, temp_pool)); + + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + +static PyObject *retrieve(PyObject *self, PyObject *args) +{ + const char *path; + svn_wc_adm_access_t *result; + PyObject *py_path; + AdmObject *admobj = (AdmObject *)self, *ret; + apr_pool_t *pool; + + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + pool = Pool(NULL); + if (pool == NULL) + return NULL; + + path = py_object_to_svn_dirent(py_path, pool); + if (path == NULL) { + apr_pool_destroy(pool); + return NULL; + } + + RUN_SVN_WITH_POOL(pool, svn_wc_adm_retrieve(&result, admobj->adm, + path, pool)); + + ret = PyObject_New(AdmObject, &Adm_Type); + if (ret == NULL) + return NULL; + + ret->pool = pool; + ret->adm = result; + + return (PyObject *)ret; +} + +static PyObject *probe_retrieve(PyObject *self, PyObject *args) +{ + const char *path; + svn_wc_adm_access_t *result; + AdmObject *admobj = (AdmObject *)self, *ret; + apr_pool_t *pool; + PyObject *py_path; + + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + pool = Pool(NULL); + if (pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, pool); + if (path == NULL) { + apr_pool_destroy(pool); + return NULL; + } + + RUN_SVN_WITH_POOL(pool, svn_wc_adm_probe_retrieve(&result, admobj->adm, + path, pool)); + + ret = PyObject_New(AdmObject, &Adm_Type); + if (ret == NULL) + return NULL; + + ret->pool = pool; + ret->adm = result; + + return (PyObject *)ret; +} + +static PyObject *probe_try(PyObject *self, PyObject *args) +{ + const char *path; + svn_wc_adm_access_t *result = NULL; + AdmObject *admobj = (AdmObject *)self, *ret; + apr_pool_t *pool; + int levels_to_lock = -1; + bool writelock = false; + PyObject *py_path; + + if (!PyArg_ParseTuple(args, "O|bi", &py_path, &writelock, &levels_to_lock)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + pool = Pool(NULL); + if (pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, pool); + if (path == NULL) { + apr_pool_destroy(pool); + return NULL; + } + + RUN_SVN_WITH_POOL(pool, svn_wc_adm_probe_try3(&result, admobj->adm, + path, writelock, levels_to_lock, + py_cancel_check, NULL, pool)); + + if (result == NULL) { + apr_pool_destroy(pool); + Py_RETURN_NONE; + } + + ret = PyObject_New(AdmObject, &Adm_Type); + if (ret == NULL) + return NULL; + + ret->pool = pool; + ret->adm = result; + + return (PyObject *)ret; +} + +static PyObject *resolved_conflict(PyObject *self, PyObject *args) +{ + AdmObject *admobj = (AdmObject *)self; + apr_pool_t *temp_pool; + bool resolve_props, resolve_tree, resolve_text; + int depth; +#if ONLY_SINCE_SVN(1, 5) + svn_wc_conflict_choice_t conflict_choice; +#else + int conflict_choice; +#endif + PyObject *notify_func = Py_None; + const char *path; + PyObject *py_path; + + if (!PyArg_ParseTuple(args, "Obbbii|O", &py_path, &resolve_text, + &resolve_props, &resolve_tree, &depth, + &conflict_choice, ¬ify_func)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + +#if ONLY_SINCE_SVN(1, 6) + RUN_SVN_WITH_POOL(temp_pool, + svn_wc_resolved_conflict4(path, admobj->adm, resolve_text?TRUE:FALSE, + resolve_props?TRUE:FALSE, resolve_tree?TRUE:FALSE, depth, + conflict_choice, py_wc_notify_func, + (void *)notify_func, py_cancel_check, + NULL, temp_pool)); +#elif ONLY_SINCE_SVN(1, 5) + if (resolve_tree) { + PyErr_SetString(PyExc_NotImplementedError, + "resolve_tree not supported with svn < 1.6"); + apr_pool_destroy(temp_pool); + return NULL; + } else { + RUN_SVN_WITH_POOL(temp_pool, + svn_wc_resolved_conflict3(path, admobj->adm, resolve_text?TRUE:FALSE, + resolve_props?TRUE:FALSE, depth, + conflict_choice, py_wc_notify_func, + (void *)notify_func, py_cancel_check, + NULL, temp_pool)); + } +#else + if (resolve_tree) { + PyErr_SetString(PyExc_NotImplementedError, + "resolve_tree not supported with svn < 1.6"); + apr_pool_destroy(temp_pool); + return NULL; + } else if (depth != svn_depth_infinity && depth != svn_depth_files) { + PyErr_SetString(PyExc_NotImplementedError, + "only infinity and files values for depth are supported"); + apr_pool_destroy(temp_pool); + return NULL; + } else if (conflict_choice != 0) { + PyErr_SetString(PyExc_NotImplementedError, + "conflict choice not supported with svn < 1.5"); + apr_pool_destroy(temp_pool); + return NULL; + } else { + RUN_SVN_WITH_POOL(temp_pool, + svn_wc_resolved_conflict2(path, admobj->adm, resolve_text?TRUE:FALSE, + resolve_props?TRUE:FALSE, + (depth == svn_depth_infinity), + py_wc_notify_func, + (void *)notify_func, py_cancel_check, + NULL, + temp_pool)); + } +#endif + + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + +static PyObject *conflicted(PyObject *self, PyObject *args) +{ + const char *path; + apr_pool_t *temp_pool; + PyObject *ret; + AdmObject *admobj = (AdmObject *)self; + svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted; + PyObject *py_path; + + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + +#if ONLY_SINCE_SVN(1, 6) + RUN_SVN_WITH_POOL(temp_pool, svn_wc_conflicted_p2(&text_conflicted, + &prop_conflicted, &tree_conflicted, path, admobj->adm, temp_pool)); + + ret = Py_BuildValue("(bbb)", text_conflicted, prop_conflicted, tree_conflicted); +#else + RUN_SVN_WITH_POOL(temp_pool, svn_wc_conflicted_p(&text_conflicted, + &prop_conflicted, path, admobj->adm, temp_pool)); + + ret = Py_BuildValue("(bbO)", text_conflicted, prop_conflicted, Py_None); +#endif + + apr_pool_destroy(temp_pool); + + return ret; +} + +/** + * Determine the status of a file in the specified working copy. + * + * :return: A status object. + */ +static PyObject *ra_status(PyObject *self, PyObject *args) +{ + const char *path; + svn_wc_status2_t *st; + apr_pool_t *temp_pool; + PyObject *ret; + AdmObject *admobj = (AdmObject *)self; + PyObject *py_path; + + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_dirent(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + RUN_SVN_WITH_POOL(temp_pool, + svn_wc_status2(&st, path, admobj->adm, temp_pool)); + + ret = py_wc_status2(st); + + apr_pool_destroy(temp_pool); + + return (PyObject*)ret; +} + +static PyMethodDef adm_methods[] = { + { "prop_set", adm_prop_set, METH_VARARGS, "S.prop_set(name, value, path, skip_checks=False)" }, + { "access_path", (PyCFunction)adm_access_path, METH_NOARGS, + "S.access_path() -> path\n" + "Returns the base path for this working copy handle." }, + { "prop_get", adm_prop_get, METH_VARARGS, "S.prop_get(name, path) -> value" }, + { "entries_read", adm_entries_read, METH_VARARGS, "S.entries_read(include_hidden=False) -> dict" }, + { "walk_entries", adm_walk_entries, METH_VARARGS, + "S.walk_entries(path, callback, show_hidden=False)\n" + "callback should be a function that takes a path and a wc entry" }, + { "locked", (PyCFunction)adm_locked, METH_NOARGS, + "S.locked() -> bool" }, + { "get_prop_diffs", adm_get_prop_diffs, METH_VARARGS, + "S.get_prop_diffs(path) -> (propchanges, originalprops)" }, + { "add", (PyCFunction)adm_add, METH_VARARGS|METH_KEYWORDS, "S.add(path, copyfrom_url=None, copyfrom_rev=-1, notify_func=None)" }, + { "copy", adm_copy, METH_VARARGS, "S.copy(src_path, dest_path, notify_func=None)" }, + { "delete", (PyCFunction)adm_delete, METH_VARARGS|METH_KEYWORDS, "S.delete(path, notify_func=None, keep_local=False)" }, + { "crawl_revisions", (PyCFunction)adm_crawl_revisions, METH_VARARGS|METH_KEYWORDS, + "S.crawl_revisions(path, reporter, restore_files=True, recurse=True, use_commit_times=True, notify_func=None) -> None" }, + { "get_update_editor", adm_get_update_editor, METH_VARARGS, NULL }, + { "close", (PyCFunction)adm_close, METH_NOARGS, + "S.close()" }, + { "entry", (PyCFunction)adm_entry, METH_VARARGS, + "s.entry(path, show_hidden=False) -> entry" }, + { "process_committed", (PyCFunction)adm_process_committed, METH_VARARGS|METH_KEYWORDS, "S.process_committed(path, recurse, new_revnum, rev_date, rev_author, wcprop_changes=None, remove_lock=False, digest=None)" }, + { "process_committed_queue", (PyCFunction)adm_process_committed_queue, METH_VARARGS, "S.process_committed_queue(queue, new_revnum, rev_date, rev_author)" }, + { "remove_lock", (PyCFunction)adm_remove_lock, METH_VARARGS, "S.remove_lock(path)" }, + { "has_binary_prop", (PyCFunction)adm_has_binary_prop, METH_VARARGS, "S.has_binary_prop(path) -> bool" }, + { "text_modified", (PyCFunction)adm_text_modified, METH_VARARGS, "S.text_modified(filename, force_comparison=False) -> bool" }, + { "props_modified", (PyCFunction)adm_props_modified, METH_VARARGS, "S.props_modified(filename) -> bool" }, + { "get_ancestry", (PyCFunction)get_ancestry, METH_VARARGS, + "S.get_ancestry(path) -> (url, rev)" }, + { "maybe_set_repos_root", (PyCFunction)maybe_set_repos_root, METH_VARARGS, "S.maybe_set_repos_root(path, repos)" }, + { "add_repos_file", (PyCFunction)add_repos_file, METH_KEYWORDS, + "S.add_repos_file(dst_path, new_base_contents, new_contents, new_base_props, new_props, copyfrom_url=None, copyfrom_rev=-1, notify_func=None)" }, + { "mark_missing_deleted", (PyCFunction)mark_missing_deleted, METH_VARARGS, + "S.mark_missing_deleted(path)" }, + { "remove_from_revision_control", (PyCFunction)remove_from_revision_control, METH_VARARGS, + "S.remove_from_revision_control(name, destroy_wf=False, instant_error=False)" }, + { "relocate", (PyCFunction)relocate, METH_VARARGS, + "S.relocate(path, from, to, recurse=TRUE, validator=None)" }, + { "crop_tree", (PyCFunction)crop_tree, METH_VARARGS, + "S.crop_tree(target, depth, notify_func=None, cancel=None)" }, + { "translated_stream", (PyCFunction)translated_stream, METH_VARARGS, + "S.translated_stream(path, versioned_file, flags) -> stream" }, + { "is_wc_root", (PyCFunction)is_wc_root, METH_VARARGS, + "S.is_wc_root(path) -> wc_root" }, + { "transmit_text_deltas", (PyCFunction)transmit_text_deltas, METH_VARARGS, + "S.transmit_text_deltas(fulltext, editor) -> (tempfile, digest)" }, + { "transmit_prop_deltas", (PyCFunction)transmit_prop_deltas, METH_VARARGS, + "S.transmit_prop_deltas(path, entry, editor)" }, + { "probe_retrieve", (PyCFunction)probe_retrieve, METH_VARARGS, + "S.probe_retrieve(path) -> WorkingCopy" }, + { "retrieve", (PyCFunction)retrieve, METH_VARARGS, + "S.retrieve(path) -> WorkingCopy" }, + { "probe_try", (PyCFunction)probe_try, METH_VARARGS, + "S.probe_try(path, write_lock=False, levels_to_lock=-1)" }, + { "conflicted", (PyCFunction)conflicted, METH_VARARGS, + "S.conflicted(path) -> (text_conflicted, prop_conflicted, tree_conflicted)" }, + { "resolved_conflict", (PyCFunction)resolved_conflict, METH_VARARGS, + "S.resolved_conflict(path, resolve_text, resolve_props, resolve_tree, depth, conflict_choice, notify_func=None, cancel=None)" }, + { "status", (PyCFunction)ra_status, METH_VARARGS, "status(wc_path) -> Status" }, + { NULL, } +}; + +PyTypeObject Adm_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "wc.WorkingCopy", /* const char *tp_name; For printing, in format "." */ + sizeof(AdmObject), + 0,/* Py_ssize_t tp_basicsize, tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + adm_dealloc, /* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + NULL, /* cmpfunc tp_compare; */ + adm_repr, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + adm_repr, /* reprfunc tp_repr; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /* Flags to define presence of optional/expanded features */ + 0, /* long tp_flags; */ + + "Local working copy", /* const char *tp_doc; Documentation string */ + + /* Assigned meaning in release 2.0 */ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /* Assigned meaning in release 2.1 */ + /* rich comparisons */ + NULL, /* richcmpfunc tp_richcompare; */ + + /* weak reference enabler */ + 0, /* Py_ssize_t tp_weaklistoffset; */ + + /* Added in release 2.2 */ + /* Iterators */ + NULL, /* getiterfunc tp_iter; */ + NULL, /* iternextfunc tp_iternext; */ + + /* Attribute descriptor and subclassing stuff */ + adm_methods, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + NULL, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + 0, /* Py_ssize_t tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + adm_init, /* newfunc tp_new; */ +}; + +static void entry_dealloc(PyObject *self) +{ + apr_pool_destroy(((EntryObject *)self)->pool); + PyObject_Del(self); +} + +static PyMemberDef entry_members[] = { + { "name", T_STRING, offsetof(EntryObject, entry.name), READONLY, + "Name of the file"}, + { "copyfrom_url", T_STRING, offsetof(EntryObject, entry.copyfrom_url), READONLY, + "Copyfrom location" }, + { "copyfrom_rev", T_LONG, offsetof(EntryObject, entry.copyfrom_rev), READONLY, + "Copyfrom revision" }, + { "uuid", T_STRING, offsetof(EntryObject, entry.uuid), READONLY, + "UUID of repository" }, + { "url", T_STRING, offsetof(EntryObject, entry.url), READONLY, + "URL in repository" }, + { "repos", T_STRING, offsetof(EntryObject, entry.repos), READONLY, + "Canonical repository URL" }, + { "schedule", T_INT, offsetof(EntryObject, entry.schedule), READONLY, + "Scheduling (add, replace, delete, etc)" }, + { "kind", T_INT, offsetof(EntryObject, entry.kind), READONLY, + "Kind of file (file, dir, etc)" }, + { "revision", T_LONG, offsetof(EntryObject, entry.revision), READONLY, + "Base revision", }, + { "cmt_rev", T_LONG, offsetof(EntryObject, entry.cmt_rev), READONLY, + "Last revision this was changed" }, + { "checksum", T_STRING, offsetof(EntryObject, entry.checksum), READONLY, + "Hex MD5 checksum for the untranslated text base file" }, + { "cmt_date", T_LONG, offsetof(EntryObject, entry.cmt_date), READONLY, + "Last date this was changed" }, + { "cmt_author", T_STRING, offsetof(EntryObject, entry.cmt_author), READONLY, + "Last commit author of this item" }, + { NULL, } +}; + +static PyTypeObject Entry_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "wc.Entry", /* const char *tp_name; For printing, in format "." */ + sizeof(EntryObject), + 0,/* Py_ssize_t tp_basicsize, tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + entry_dealloc, /* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + NULL, /* cmpfunc tp_compare; */ + NULL, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /* Flags to define presence of optional/expanded features */ + 0, /* long tp_flags; */ + + NULL, /* const char *tp_doc; Documentation string */ + + /* Assigned meaning in release 2.0 */ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /* Assigned meaning in release 2.1 */ + /* rich comparisons */ + NULL, /* richcmpfunc tp_richcompare; */ + + /* weak reference enabler */ + 0, /* Py_ssize_t tp_weaklistoffset; */ + + /* Added in release 2.2 */ + /* Iterators */ + NULL, /* getiterfunc tp_iter; */ + NULL, /* iternextfunc tp_iternext; */ + + /* Attribute descriptor and subclassing stuff */ + NULL, /* struct PyMethodDef *tp_methods; */ + entry_members, /* struct PyMemberDef *tp_members; */ + +}; + +static PyObject *py_entry(const svn_wc_entry_t *entry) +{ + EntryObject *ret; + + ret = PyObject_New(EntryObject, &Entry_Type); + if (ret == NULL) + return NULL; + + ret->pool = Pool(NULL); + if (ret->pool == NULL) + return NULL; + ret->entry = *svn_wc_entry_dup(entry, ret->pool); + return (PyObject *)ret; +} + +typedef struct { + PyObject_VAR_HEAD + apr_pool_t *pool; + svn_wc_status2_t status; + PyObject *entry; +} StatusObject; + +static void status_dealloc(PyObject *self) +{ + apr_pool_destroy(((StatusObject *)self)->pool); + Py_XDECREF(((StatusObject *)self)->entry); + PyObject_Del(self); +} + +static PyMemberDef status_members[] = { + { "entry", T_OBJECT, offsetof(StatusObject, entry), READONLY, + "Can be NULL if not under version control." }, + { "locked", T_BOOL, offsetof(StatusObject, status.locked), READONLY, + "a directory can be 'locked' if a working copy update was interrupted." }, + { "copied", T_BOOL, offsetof(StatusObject, status.copied), READONLY, + "a file or directory can be 'copied' if it's scheduled for addition-with-history (or part of a subtree that is scheduled as such.)." }, + { "switched", T_BOOL, offsetof(StatusObject, status.switched), READONLY, + "a file or directory can be 'switched' if the switch command has been used." }, + { "url", T_STRING, offsetof(StatusObject, status.url), READONLY, + "URL (actual or expected) in repository" }, + { "revision", T_LONG, offsetof(StatusObject, status.ood_last_cmt_rev), READONLY, + "Set to the youngest committed revision, or SVN_INVALID_REVNUM if not out of date.", }, + { "kind", T_INT, offsetof(StatusObject, status.ood_kind), READONLY, + "Set to the node kind of the youngest commit, or svn_node_none if not out of date.", }, + { "status", T_INT, offsetof(StatusObject, status.text_status), READONLY, + "The status of the entry.", }, + { NULL, } +}; + +static PyTypeObject Status_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "wc.Status", /* const char *tp_name; For printing, in format "." */ + sizeof(StatusObject), + 0,/* Py_ssize_t tp_basicsize, tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + status_dealloc, /* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + NULL, /* cmpfunc tp_compare; */ + NULL, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /* Flags to define presence of optional/expanded features */ + 0, /* long tp_flags; */ + + "Working copy status object", /* const char *tp_doc; Documentation string */ + + /* Assigned meaning in release 2.0 */ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /* Assigned meaning in release 2.1 */ + /* rich comparisons */ + NULL, /* richcmpfunc tp_richcompare; */ + + /* weak reference enabler */ + 0, /* Py_ssize_t tp_weaklistoffset; */ + + /* Added in release 2.2 */ + /* Iterators */ + NULL, /* getiterfunc tp_iter; */ + NULL, /* iternextfunc tp_iternext; */ + + /* Attribute descriptor and subclassing stuff */ + NULL, /* struct PyMethodDef *tp_methods; */ + status_members, /* struct PyMemberDef *tp_members; */ + +}; + +PyObject *py_wc_status2(const svn_wc_status2_t *status) +{ + StatusObject *ret; + svn_wc_status2_t *dup_status; + + ret = PyObject_New(StatusObject, &Status_Type); + if (ret == NULL) + return NULL; + + ret->pool = Pool(NULL); + if (ret->pool == NULL) { + PyObject_Del(ret); + return NULL; + } + + dup_status = svn_wc_dup_status2(status, ret->pool); + if (dup_status == NULL) + { + PyErr_NoMemory(); + return NULL; + } + ret->status = *dup_status; + + ret->entry = py_entry(ret->status.entry); + return (PyObject *)ret; +} + +static svn_error_t *py_wc_found_entry(const char *path, const svn_wc_entry_t *entry, void *walk_baton, apr_pool_t *pool) +{ + PyObject *fn, *ret; + PyObject *callbacks = (PyObject *)walk_baton; + PyGILState_STATE state = PyGILState_Ensure(); + if (PyTuple_Check(callbacks)) { + fn = PyTuple_GET_ITEM(callbacks, 0); + } else { + fn = (PyObject *)walk_baton; + } + ret = PyObject_CallFunction(fn, "sO", path, py_entry(entry)); + CB_CHECK_PYRETVAL(ret); + Py_DECREF(ret); + PyGILState_Release(state); + return NULL; +} + +#if ONLY_SINCE_SVN(1, 5) + +svn_error_t *py_wc_handle_error(const char *path, svn_error_t *err, void *walk_baton, apr_pool_t *pool) +{ + PyObject *fn, *ret; + PyObject *py_err; + PyGILState_STATE state; + PyObject *callbacks = (PyObject *)walk_baton; + if (PyTuple_Check(callbacks)) { + fn = PyTuple_GET_ITEM(callbacks, 1); + } else { + return err; + } + state = PyGILState_Ensure(); + py_err = PyErr_NewSubversionException(err); + ret = PyObject_CallFunction(fn, "sO", path, py_err); + CB_CHECK_PYRETVAL(ret); + Py_DECREF(ret); + PyGILState_Release(state); + Py_DECREF(py_err); + return NULL; +} + +static svn_wc_entry_callbacks2_t py_wc_entry_callbacks2 = { + py_wc_found_entry, + py_wc_handle_error, +}; +#else +static svn_wc_entry_callbacks_t py_wc_entry_callbacks = { + py_wc_found_entry +}; +#endif -- cgit v1.2.3 From f9a17b1db4e356e36d1f2bb638801e46c58a4356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 09:50:59 +0000 Subject: Fix tests. --- subvertpy/client.c | 14 +++++--------- subvertpy/wc.c | 3 --- subvertpy/wc.h | 2 ++ subvertpy/wc_adm.c | 7 ++----- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/subvertpy/client.c b/subvertpy/client.c index 452e0fec..eb7cc750 100644 --- a/subvertpy/client.c +++ b/subvertpy/client.c @@ -1000,9 +1000,6 @@ static PyObject *client_cat(PyObject *self, PyObject *args, PyObject *kwargs) } RUN_SVN_WITH_POOL(temp_pool, svn_client_cat2(stream, path, &c_peg_rev, &c_rev, client->client, temp_pool)); - - ret = Py_None; - Py_INCREF(ret); #endif apr_pool_destroy(temp_pool); @@ -1017,7 +1014,7 @@ static PyObject *client_delete(PyObject *self, PyObject *args) #if ONLY_BEFORE_SVN(1, 7) svn_commit_info_t *commit_info = NULL; #endif - PyObject *ret, *py_revprops; + PyObject *py_revprops; apr_array_header_t *apr_paths; ClientObject *client = (ClientObject *)self; apr_hash_t *hash_revprops; @@ -1074,7 +1071,7 @@ static PyObject *client_delete(PyObject *self, PyObject *args) apr_pool_destroy(temp_pool); - return ret; + Py_RETURN_NONE; } static PyObject *client_mkdir(PyObject *self, PyObject *args, PyObject *kwargs) @@ -1085,7 +1082,6 @@ static PyObject *client_mkdir(PyObject *self, PyObject *args, PyObject *kwargs) #if ONLY_BEFORE_SVN(1, 7) svn_commit_info_t *commit_info = NULL; #endif - PyObject *ret; apr_array_header_t *apr_paths; apr_hash_t *hash_revprops; ClientObject *client = (ClientObject *)self; @@ -1154,7 +1150,7 @@ static PyObject *client_mkdir(PyObject *self, PyObject *args, PyObject *kwargs) apr_pool_destroy(temp_pool); - return ret; + Py_RETURN_NONE; } static PyObject *client_copy(PyObject *self, PyObject *args, PyObject *kwargs) @@ -1167,7 +1163,6 @@ static PyObject *client_copy(PyObject *self, PyObject *args, PyObject *kwargs) apr_pool_t *temp_pool; svn_opt_revision_t c_src_rev; bool copy_as_child = true, make_parents = false; - PyObject *ret; apr_hash_t *revprops; bool ignore_externals = false; bool metadata_only = false; @@ -1291,7 +1286,8 @@ static PyObject *client_copy(PyObject *self, PyObject *args, PyObject *kwargs) INVOKE_COMMIT_CALLBACK(temp_pool, commit_info, callback); #endif apr_pool_destroy(temp_pool); - return ret; + + Py_RETURN_NONE; } static PyObject *client_propset(PyObject *self, PyObject *args) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index b10b812d..dc2ea1ca 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -34,9 +34,6 @@ #define T_BOOL T_BYTE #endif -static PyTypeObject Entry_Type; -static PyTypeObject Status_Type; - #if ONLY_BEFORE_SVN(1, 5) struct svn_wc_committed_queue_t { diff --git a/subvertpy/wc.h b/subvertpy/wc.h index 22eb2d70..388aab79 100644 --- a/subvertpy/wc.h +++ b/subvertpy/wc.h @@ -41,6 +41,8 @@ extern PyTypeObject CommittedQueue_Type; /* Provided by wc_adm.h */ extern PyTypeObject Adm_Type; +extern PyTypeObject Entry_Type; +extern PyTypeObject Status_Type; svn_wc_adm_access_t *PyObject_GetAdmAccess(PyObject *obj); #ifdef __GNUC__ diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index 7f520374..7020789d 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -31,11 +31,8 @@ #include "wc.h" static svn_wc_entry_callbacks2_t py_wc_entry_callbacks2; -static PyTypeObject Entry_Type; static PyObject *py_entry(const svn_wc_entry_t *entry); -PyTypeObject Adm_Type; - typedef struct { PyObject_VAR_HEAD svn_wc_adm_access_t *adm; @@ -1786,7 +1783,7 @@ static PyMemberDef entry_members[] = { { NULL, } }; -static PyTypeObject Entry_Type = { +PyTypeObject Entry_Type = { PyVarObject_HEAD_INIT(NULL, 0) "wc.Entry", /* const char *tp_name; For printing, in format "." */ sizeof(EntryObject), @@ -1897,7 +1894,7 @@ static PyMemberDef status_members[] = { { NULL, } }; -static PyTypeObject Status_Type = { +PyTypeObject Status_Type = { PyVarObject_HEAD_INIT(NULL, 0) "wc.Status", /* const char *tp_name; For printing, in format "." */ sizeof(StatusObject), -- cgit v1.2.3 From a01737170d548501785370feec1343f77a409b91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 09:54:11 +0000 Subject: Document API changes. --- NEWS | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/NEWS b/NEWS index 5e75b2e6..a037e464 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,16 @@ 0.10.2 UNRELEASED + API CHANGES + + * ``subvertpy.wc.WorkingCopy`` has been renamed to + `` subvertpy.wc.Adm``. (Jelmer Vernooij) + + * ``subvertpy.client.mkdir``, ``subvertpy.client.copy``, + ``subvertpy.client.delete``, ``subvertpy.client.commit`` + no longer return the resulting commit but call an + optional callback with commit info. + (Jelmer Vernooij) + 0.10.1 2017-07-19 BUG FIXES -- cgit v1.2.3 From e31211e3d606b88601849bbb410aebee77477190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 09:56:55 +0000 Subject: Ignore deprecation warnings from wc_adm. --- subvertpy/wc_adm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index 7020789d..2d13df69 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -30,6 +30,12 @@ #include "editor.h" #include "wc.h" +/* Suppress warnings for this specific file, as it + * provides backwards compatibility with svn < 1.7 + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + static svn_wc_entry_callbacks2_t py_wc_entry_callbacks2; static PyObject *py_entry(const svn_wc_entry_t *entry); @@ -2032,3 +2038,5 @@ static svn_wc_entry_callbacks_t py_wc_entry_callbacks = { py_wc_found_entry }; #endif + +#pragma GCC diagnostic pop -- cgit v1.2.3 From 44251af280d5a4aa268d1a850ffa7b0ec3405545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 10:38:30 +0000 Subject: Add basic support for creating wc contexts. --- subvertpy/wc.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index dc2ea1ca..84e2ad1d 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -34,6 +34,8 @@ #define T_BOOL T_BYTE #endif +static PyTypeObject Context_Type; + #if ONLY_BEFORE_SVN(1, 5) struct svn_wc_committed_queue_t { @@ -982,7 +984,120 @@ PyTypeObject CommittedQueue_Type = { committed_queue_init, /* newfunc tp_new; */ }; +#if ONLY_SINCE_SVN(1, 7) + +typedef struct { + PyObject_VAR_HEAD + apr_pool_t *pool; + svn_wc_context_t *context; +} ContextObject; + +static PyMethodDef context_methods[] = { + { NULL } +}; + +static void context_dealloc(PyObject *self) +{ + ContextObject *context_obj = (ContextObject *)self; + svn_wc_context_destroy(context_obj->context); + apr_pool_destroy(context_obj->pool); + PyObject_Del(self); +} + +static PyObject *context_init(PyTypeObject *self, PyObject *args, PyObject *kwargs) +{ + ContextObject *ret; + char *kwnames[] = { NULL }; + svn_config_t *config = NULL; + + // TODO(jelmer): Support passing in config + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwnames)) + return NULL; + + ret = PyObject_New(ContextObject, &Context_Type); + if (ret == NULL) + return NULL; + + ret->pool = Pool(NULL); + if (ret->pool == NULL) + return NULL; + RUN_SVN_WITH_POOL(ret->pool, svn_wc_context_create(&ret->context, config, + ret->pool, ret->pool)); + + return (PyObject *)ret; +} + +static PyTypeObject Context_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "wc.Context", /* const char *tp_name; For printing, in format "." */ + sizeof(ContextObject), + 0,/* Py_ssize_t tp_basicsize, tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + context_dealloc, /* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + NULL, /* cmpfunc tp_compare; */ + NULL, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /* Flags to define presence of optional/expanded features */ + 0, /* long tp_flags; */ + + "Context", /* const char *tp_doc; Documentation string */ + + /* Assigned meaning in release 2.0 */ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /* Assigned meaning in release 2.1 */ + /* rich comparisons */ + NULL, /* richcmpfunc tp_richcompare; */ + + /* weak reference enabler */ + 0, /* Py_ssize_t tp_weaklistoffset; */ + + /* Added in release 2.2 */ + /* Iterators */ + NULL, /* getiterfunc tp_iter; */ + NULL, /* iternextfunc tp_iternext; */ + + /* Attribute descriptor and subclassing stuff */ + context_methods, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + NULL, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + 0, /* Py_ssize_t tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + context_init, /* newfunc tp_new; */ +}; + +#endif static PyObject * moduleinit(void) @@ -998,6 +1113,11 @@ moduleinit(void) if (PyType_Ready(&Adm_Type) < 0) return NULL; +#if ONLY_SINCE_SVN(1, 7) + if (PyType_Ready(&Context_Type) < 0) + return NULL; +#endif + if (PyType_Ready(&Editor_Type) < 0) return NULL; @@ -1097,6 +1217,11 @@ moduleinit(void) PyModule_AddObject(mod, "CommittedQueue", (PyObject *)&CommittedQueue_Type); Py_INCREF(&CommittedQueue_Type); +#if ONLY_SINCE_SVN(1, 7) + PyModule_AddObject(mod, "Context", (PyObject *)&Context_Type); + Py_INCREF(&Context_Type); +#endif + return mod; } -- cgit v1.2.3 From 26f3253510ff22af25f7e975229dd9224a2b1459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 10:46:26 +0000 Subject: Add subr module. --- setup.py | 7 ++++- subvertpy/subr.c | 73 ++++++++++++++++++++++++++++++++++++++++++++ subvertpy/tests/__init__.py | 1 + subvertpy/tests/test_subr.py | 18 +++++++++++ subvertpy/util.h | 1 + 5 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 subvertpy/subr.c create mode 100644 subvertpy/tests/test_subr.py diff --git a/setup.py b/setup.py index 4b44cf60..271d0b02 100755 --- a/setup.py +++ b/setup.py @@ -410,7 +410,12 @@ def subvertpy_modules(): "subvertpy.wc", [source_path(n) for n in ["wc.c", "wc_adm.c", "util.c", "editor.c"]], - libraries=["svn_wc-1", "svn_subr-1"]) + libraries=["svn_wc-1", "svn_subr-1"]), + SvnExtension( + "subvertpy.subr", + [source_path(n) + for n in ["util.c", "subr.c"]], + libraries=["svn_subr-1"]), ] diff --git a/subvertpy/subr.c b/subvertpy/subr.c new file mode 100644 index 00000000..59b59cf9 --- /dev/null +++ b/subvertpy/subr.c @@ -0,0 +1,73 @@ +/* + * Copyright © 2017 Jelmer Vernooij + * -*- coding: utf-8 -*- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#include +#include +#include +#include +#include +#include + +#include "util.h" + +static PyMethodDef subr_methods[] = { + { NULL } +}; + +static PyObject * +moduleinit(void) +{ + PyObject *mod; + + apr_initialize(); + +#if PY_MAJOR_VERSION >= 3 + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "subr", /* m_name */ + "subr", /* m_doc */ + -1, /* m_size */ + subr_methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear*/ + NULL, /* m_free */ + }; + mod = PyModule_Create(&moduledef); +#else + mod = Py_InitModule3("subr", subr_methods, "Subversion subr"); +#endif + if (mod == NULL) + return NULL; + + return mod; +} + +#if PY_MAJOR_VERSION >= 3 +PyMODINIT_FUNC +PyInit_subr(void) +{ + return moduleinit(); +} +#else +PyMODINIT_FUNC +initsubr(void) +{ + moduleinit(); +} +#endif diff --git a/subvertpy/tests/__init__.py b/subvertpy/tests/__init__.py index 7fb1e008..6b88fb78 100644 --- a/subvertpy/tests/__init__.py +++ b/subvertpy/tests/__init__.py @@ -442,6 +442,7 @@ def test_suite(): 'ra', 'repos', 'server', + 'subr', 'wc', ] module_names = ['subvertpy.tests.test_' + name for name in names] diff --git a/subvertpy/tests/test_subr.py b/subvertpy/tests/test_subr.py new file mode 100644 index 00000000..0c3f335e --- /dev/null +++ b/subvertpy/tests/test_subr.py @@ -0,0 +1,18 @@ +# Copyright (C) 2017 Jelmer Vernooij + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +"""Subversion subr library tests.""" + +from unittest import SkipTest diff --git a/subvertpy/util.h b/subvertpy/util.h index 6e178377..9b3b1f38 100644 --- a/subvertpy/util.h +++ b/subvertpy/util.h @@ -21,6 +21,7 @@ #define _SUBVERTPY_UTIL_H_ #include +#include /* for svn_stream_t */ #if SVN_VER_MAJOR != 1 #error "only svn 1.x is supported" -- cgit v1.2.3 From 2ae6f129e25244a7a5c13786bf50d5f0785a0665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 10:56:56 +0000 Subject: Add subvertpy.subr.uri_canonicalize. --- subvertpy/subr.c | 59 +++++++++++++++++++++++++++++--------------- subvertpy/tests/test_subr.py | 15 ++++++++++- subvertpy/util.h | 2 +- 3 files changed, 54 insertions(+), 22 deletions(-) diff --git a/subvertpy/subr.c b/subvertpy/subr.c index 59b59cf9..76d70331 100644 --- a/subvertpy/subr.c +++ b/subvertpy/subr.c @@ -25,49 +25,68 @@ #include "util.h" +static PyObject *py_uri_canonicalize(PyObject *self, PyObject *args) +{ + const char *uri; + PyObject *py_uri, *ret; + apr_pool_t *pool; + + if (!PyArg_ParseTuple(args, "O", &py_uri)) + return NULL; + + pool = Pool(NULL); + uri = py_object_to_svn_uri(py_uri, pool); + ret = PyString_FromString(uri); + apr_pool_destroy(pool); + + return ret; +} + static PyMethodDef subr_methods[] = { + { "uri_canonicalize", py_uri_canonicalize, METH_VARARGS, "uri_canonicalize(uri) -> uri\n" + "Canonicalize a URI."}, { NULL } }; static PyObject * moduleinit(void) { - PyObject *mod; + PyObject *mod; - apr_initialize(); + apr_initialize(); #if PY_MAJOR_VERSION >= 3 - static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "subr", /* m_name */ - "subr", /* m_doc */ - -1, /* m_size */ - subr_methods, /* m_methods */ - NULL, /* m_reload */ - NULL, /* m_traverse */ - NULL, /* m_clear*/ - NULL, /* m_free */ - }; - mod = PyModule_Create(&moduledef); + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "subr", /* m_name */ + "subr", /* m_doc */ + -1, /* m_size */ + subr_methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear*/ + NULL, /* m_free */ + }; + mod = PyModule_Create(&moduledef); #else - mod = Py_InitModule3("subr", subr_methods, "Subversion subr"); + mod = Py_InitModule3("subr", subr_methods, "Subversion subr"); #endif - if (mod == NULL) - return NULL; + if (mod == NULL) + return NULL; - return mod; + return mod; } #if PY_MAJOR_VERSION >= 3 PyMODINIT_FUNC PyInit_subr(void) { - return moduleinit(); + return moduleinit(); } #else PyMODINIT_FUNC initsubr(void) { - moduleinit(); + moduleinit(); } #endif diff --git a/subvertpy/tests/test_subr.py b/subvertpy/tests/test_subr.py index 0c3f335e..35cd1c97 100644 --- a/subvertpy/tests/test_subr.py +++ b/subvertpy/tests/test_subr.py @@ -15,4 +15,17 @@ """Subversion subr library tests.""" -from unittest import SkipTest +from unittest import TestCase + +from subvertpy.subr import uri_canonicalize + + +class UriCanonicalizeTests(TestCase): + + def test_canonicalize(self): + self.assertEqual('https://www.example.com', + uri_canonicalize('https://www.example.com/')) + self.assertEqual('https://www.example.com(bla)', + uri_canonicalize('https://www.example.com(bla)')) + self.assertEqual('https://www.example.com/(bla)', + uri_canonicalize('https://www.example.com/(bla%29')) diff --git a/subvertpy/util.h b/subvertpy/util.h index 9b3b1f38..bef2f334 100644 --- a/subvertpy/util.h +++ b/subvertpy/util.h @@ -49,7 +49,7 @@ apr_hash_t *prop_dict_to_hash(apr_pool_t *pool, PyObject *py_props); svn_error_t *py_svn_log_wrapper( void *baton, apr_hash_t *changed_paths, long revision, const char *author, const char *date, const char *message, apr_pool_t *pool); -svn_error_t *py_svn_error(void); +__attribute__((warn_unused_result)) svn_error_t *py_svn_error(void); void PyErr_SetSubversionException(svn_error_t *error); PyTypeObject *PyErr_GetSubversionExceptionTypeObject(void); -- cgit v1.2.3 From 491588a4e190933b01d579f32b56ee5c9570582f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 10:59:05 +0000 Subject: Change to 0.11.0. --- NEWS | 2 +- setup.py | 2 +- subvertpy/__init__.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index a037e464..e9eb9ba3 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -0.10.2 UNRELEASED +0.11.0 UNRELEASED API CHANGES diff --git a/setup.py b/setup.py index 271d0b02..21b0da77 100755 --- a/setup.py +++ b/setup.py @@ -419,7 +419,7 @@ def subvertpy_modules(): ] -subvertpy_version = (0, 10, 2) +subvertpy_version = (0, 11, 0) subvertpy_version_string = ".".join(map(str, subvertpy_version)) diff --git a/subvertpy/__init__.py b/subvertpy/__init__.py index f36af65d..89c1effd 100644 --- a/subvertpy/__init__.py +++ b/subvertpy/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2006-2008 Jelmer Vernooij +# Copyright (C) 2006-2017 Jelmer Vernooij # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by @@ -17,7 +17,7 @@ """Python bindings for Subversion.""" __author__ = "Jelmer Vernooij " -__version__ = (0, 10, 2) +__version__ = (0, 11, 0) NODE_DIR = 2 NODE_FILE = 1 -- cgit v1.2.3 From b607313c3a24d422eca5276f6e56076f22336a94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 11:33:32 +0000 Subject: Add wc.Context.locked. --- subvertpy/tests/test_wc.py | 56 ++++++++++++++++++++++++++++------------------ subvertpy/util.c | 36 +++++++++++++++++++++++++++++ subvertpy/util.h | 1 + subvertpy/wc.c | 28 +++++++++++++++++++++++ 4 files changed, 99 insertions(+), 22 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 940cfc6e..76fd96e1 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -42,7 +42,7 @@ class VersionTest(TestCase): self.assertTrue(wc.api_version() <= wc.version()) -class WorkingCopyTests(TestCase): +class AdmTests(TestCase): def test_get_adm_dir(self): self.assertEqual(b".svn", wc.get_adm_dir()) @@ -100,7 +100,7 @@ class AdmTests(SubversionTestCase): self.make_client("repos", "checkout") self.build_tree({"checkout/bar": b"\x00 \x01"}) self.client_add('checkout/bar') - adm = wc.WorkingCopy(None, "checkout") + adm = wc.Adm(None, "checkout") path = os.path.join(self.test_dir, "checkout/bar") self.assertFalse(adm.has_binary_prop(path)) adm.close() @@ -109,21 +109,21 @@ class AdmTests(SubversionTestCase): repos_url = self.make_client("repos", "checkout") self.build_tree({"checkout/bar": b"\x00 \x01"}) self.client_add('checkout/bar') - adm = wc.WorkingCopy(None, "checkout") + adm = wc.Adm(None, "checkout") self.assertEqual(("%s/bar" % repos_url, 0), adm.get_ancestry("checkout/bar")) adm.close() def test_maybe_set_repos_root(self): repos_url = self.make_client("repos", "checkout") - adm = wc.WorkingCopy(None, "checkout") + adm = wc.Adm(None, "checkout") adm.maybe_set_repos_root( os.path.join(self.test_dir, "checkout"), repos_url) adm.close() def test_add_repos_file(self): self.make_client("repos", "checkout") - adm = wc.WorkingCopy(None, "checkout", True) + adm = wc.Adm(None, "checkout", True) adm.add_repos_file("checkout/bar", BytesIO(b"basecontents"), BytesIO(b"contents"), {}, {}) self.assertEqual(b"basecontents", @@ -133,7 +133,7 @@ class AdmTests(SubversionTestCase): self.make_client("repos", "checkout") self.build_tree({"checkout/bar": b"\x00 \x01"}) self.client_add('checkout/bar') - adm = wc.WorkingCopy(None, "checkout", True) + adm = wc.Adm(None, "checkout", True) os.remove("checkout/bar") adm.mark_missing_deleted("checkout/bar") self.assertFalse(os.path.exists("checkout/bar")) @@ -142,13 +142,13 @@ class AdmTests(SubversionTestCase): self.make_client("repos", "checkout") self.build_tree({"checkout/bar": b"\x00 \x01"}) self.client_add('checkout/bar') - adm = wc.WorkingCopy(None, "checkout", True) + adm = wc.Adm(None, "checkout", True) adm.remove_from_revision_control("bar") self.assertTrue(os.path.exists("checkout/bar")) def test_relocate(self): self.make_client("repos", "checkout") - adm = wc.WorkingCopy(None, "checkout", True) + adm = wc.Adm(None, "checkout", True) adm.relocate("checkout", "file://", "http://") def test_translated_stream(self): @@ -157,7 +157,7 @@ class AdmTests(SubversionTestCase): self.client_add('checkout/bar') self.client_set_prop("checkout/bar", "svn:keywords", "Id\n") self.client_commit("checkout", "foo") - adm = wc.WorkingCopy(None, "checkout", True) + adm = wc.Adm(None, "checkout", True) path = os.path.join(self.test_dir, "checkout/bar") stream = adm.translated_stream(path, path, wc.TRANSLATE_TO_NF) self.assertTrue(stream.read().startswith(b"My id: $Id: ")) @@ -168,7 +168,7 @@ class AdmTests(SubversionTestCase): self.client_add('checkout/bar') self.client_set_prop("checkout/bar", "svn:keywords", "Id\n") self.client_commit("checkout", "foo") - adm = wc.WorkingCopy(None, "checkout") + adm = wc.Adm(None, "checkout") self.assertFalse(adm.text_modified("checkout/bar")) self.build_tree({"checkout/bar": b"gambon"}) self.assertTrue(adm.text_modified("checkout/bar", True)) @@ -179,7 +179,7 @@ class AdmTests(SubversionTestCase): self.client_add('checkout/bar') self.client_set_prop("checkout/bar", "svn:keywords", "Id\n") self.client_commit("checkout", "foo") - adm = wc.WorkingCopy(None, "checkout", True) + adm = wc.Adm(None, "checkout", True) self.assertFalse(adm.props_modified("checkout/bar")) adm.prop_set("aprop", "avalue", "checkout/bar") self.assertTrue(adm.props_modified("checkout/bar")) @@ -188,7 +188,7 @@ class AdmTests(SubversionTestCase): self.make_client("repos", "checkout") self.build_tree({"checkout/bar": b"file"}) self.client_add('checkout/bar') - adm = wc.WorkingCopy(None, "checkout", True) + adm = wc.Adm(None, "checkout", True) adm.prop_set("aprop", "avalue", "checkout/bar") self.assertEqual(adm.prop_get("aprop", "checkout/bar"), "avalue") adm.prop_set("aprop", None, "checkout/bar") @@ -199,26 +199,26 @@ class AdmTests(SubversionTestCase): raise SkipTest("CommittedQueue not available") cq = wc.CommittedQueue() self.make_client("repos", "checkout") - adm = wc.WorkingCopy(None, "checkout", True) + adm = wc.Adm(None, "checkout", True) adm.process_committed_queue(cq, 1, "2010-05-31T08:49:22.430000Z", "jelmer") def test_entry_not_found(self): self.make_client("repos", "checkout") - adm = wc.WorkingCopy(None, "checkout") + adm = wc.Adm(None, "checkout") self.assertRaises(KeyError, adm.entry, "bar") def test_entry(self): self.make_client("repos", "checkout") self.build_tree({"checkout/bar": b"\x00 \x01"}) self.client_add('checkout/bar') - adm = wc.WorkingCopy(None, "checkout") + adm = wc.Adm(None, "checkout") entry = adm.entry("checkout/bar") self.assertEqual("bar", entry.name) self.assertEqual(NODE_FILE, entry.kind) self.assertEqual(0, entry.revision) self.client_commit("checkout", "msg") - adm = wc.WorkingCopy(None, "checkout") + adm = wc.Adm(None, "checkout") entry = adm.entry("checkout/bar") self.assertEqual("bar", entry.name) self.assertEqual(NODE_FILE, entry.kind) @@ -233,7 +233,7 @@ class AdmTests(SubversionTestCase): self.make_client("repos", ".") self.build_tree({"bar": None}) self.client_add('bar') - adm = wc.WorkingCopy(None, ".") + adm = wc.Adm(None, ".") self.assertTrue(adm.is_wc_root(self.test_dir)) self.assertFalse(adm.is_wc_root(os.path.join(self.test_dir, "bar"))) @@ -241,17 +241,17 @@ class AdmTests(SubversionTestCase): self.make_client("repos", "checkout") self.build_tree({"checkout/bar": b"text"}) self.client_add('checkout/bar') - adm = wc.WorkingCopy(None, "checkout") + adm = wc.Adm(None, "checkout") self.assertEqual(wc.STATUS_ADDED, adm.status('bar').status) self.client_commit("checkout", "foo") - adm = wc.WorkingCopy(None, "checkout") + adm = wc.Adm(None, "checkout") self.assertEqual(wc.STATUS_NORMAL, adm.status('bar').status) def test_transmit_text_deltas(self): self.make_client("repos", ".") self.build_tree({"bar": b"blala"}) self.client_add('bar') - adm = wc.WorkingCopy(None, ".", True) + adm = wc.Adm(None, ".", True) class Editor(object): """Editor""" @@ -293,7 +293,7 @@ class AdmTests(SubversionTestCase): self.make_client("repos", "checkout") self.build_tree({"checkout/bar": b"la"}) self.client_add('checkout/bar') - adm = wc.WorkingCopy(None, "checkout", True) + adm = wc.Adm(None, "checkout", True) cq = wc.CommittedQueue() cq.queue(os.path.join(self.test_dir, "checkout/bar"), adm) adm.process_committed_queue(cq, 1, "2010-05-31T08:49:22.430000Z", @@ -307,7 +307,7 @@ class AdmTests(SubversionTestCase): self.make_client("repos", "checkout") self.build_tree({"checkout/bar": b"la"}) self.client_add('checkout/bar') - adm = wc.WorkingCopy(None, "checkout", True) + adm = wc.Adm(None, "checkout", True) try: self.assertIs(None, adm.probe_try(self.test_dir)) except subvertpy.SubversionException as e: @@ -317,3 +317,15 @@ class AdmTests(SubversionTestCase): self.assertEqual( "checkout", adm.probe_try(os.path.join("checkout", "bar")).access_path()) + + +class ContextTests(SubversionTestCase): + + def test_create(self): + context = wc.Context() + self.assertIsInstance(context, wc.Context) + + def test_locked(self): + context = wc.Context() + self.make_client("repos", "checkout") + self.assertEqual((False, False), context.locked("checkout")) diff --git a/subvertpy/util.c b/subvertpy/util.c index 0f82a667..5e4c9170 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -96,6 +96,42 @@ const char *py_object_to_svn_path_or_url(PyObject *obj, apr_pool_t *pool) return ret; } +const char *py_object_to_svn_abspath(PyObject *obj, apr_pool_t *pool) +{ + const char *ret; + PyObject *bytes_obj = NULL; + + if (PyUnicode_Check(obj)) { + bytes_obj = obj = PyUnicode_AsUTF8String(obj); + if (obj == NULL) { + return NULL; + } + } + + if (!PyBytes_Check(obj)) { + PyErr_SetString(PyExc_TypeError, + "URIs need to be UTF-8 bytestrings or unicode strings"); + Py_XDECREF(bytes_obj); + return NULL; + } + + ret = PyBytes_AsString(obj); + ret = apr_pstrdup(pool, ret); + Py_XDECREF(obj); + if (ret == NULL) { + return NULL; + } + if (svn_dirent_is_absolute(ret)) { + return ret; + } else { + const char *absolute; + RUN_SVN_WITH_POOL(pool, svn_dirent_get_absolute(&absolute, ret, pool)); + return absolute; + } +} + + + const char *py_object_to_svn_dirent(PyObject *obj, apr_pool_t *pool) { const char *ret; diff --git a/subvertpy/util.h b/subvertpy/util.h index bef2f334..beb86e4d 100644 --- a/subvertpy/util.h +++ b/subvertpy/util.h @@ -156,6 +156,7 @@ const char *py_object_to_svn_dirent(PyObject *obj, apr_pool_t *pool); const char *py_object_to_svn_relpath(PyObject *obj, apr_pool_t *pool); const char *py_object_to_svn_path_or_url(PyObject *obj, apr_pool_t *pool); char *py_object_to_svn_string(PyObject *obj, apr_pool_t *pool); +const char *py_object_to_svn_abspath(PyObject *obj, apr_pool_t *pool); #define py_object_from_svn_abspath PyBytes_FromString #if PY_MAJOR_VERSION >= 3 diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 84e2ad1d..4f108a9e 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -992,7 +992,35 @@ typedef struct { svn_wc_context_t *context; } ContextObject; +static PyObject *py_wc_context_locked(PyObject *self, PyObject *args) +{ + PyObject* py_path; + const char *path; + apr_pool_t *pool; + svn_wc_context_t *wc_context = ((ContextObject *)self)->context; + svn_boolean_t locked_here, locked; + + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; + + pool = Pool(NULL); + + path = py_object_to_svn_abspath(py_path, pool); + if (path == NULL) { + apr_pool_destroy(pool); + return NULL; + } + + RUN_SVN_WITH_POOL(pool, svn_wc_locked2(&locked_here, &locked, wc_context, path, pool)); + + apr_pool_destroy(pool); + + return Py_BuildValue("(bb)", locked_here?true:false, locked?true:false); +} + static PyMethodDef context_methods[] = { + { "locked", py_wc_context_locked, METH_VARARGS, "locked(path) -> (locked_here, locked)\n" + "Check whether a patch is locked."}, { NULL } }; -- cgit v1.2.3 From 8d67f1eca570b7693cbed8203c21f55f61ede13a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 11:37:24 +0000 Subject: Add wc.Context.check_wc. --- subvertpy/tests/test_wc.py | 5 +++++ subvertpy/wc.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 76fd96e1..3dc3fd32 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -329,3 +329,8 @@ class ContextTests(SubversionTestCase): context = wc.Context() self.make_client("repos", "checkout") self.assertEqual((False, False), context.locked("checkout")) + + def test_check_wc(self): + context = wc.Context() + self.make_client("repos", "checkout") + self.assertIsInstance(context.check_wc("checkout"), int) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 4f108a9e..652ffe1a 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -1018,9 +1018,37 @@ static PyObject *py_wc_context_locked(PyObject *self, PyObject *args) return Py_BuildValue("(bb)", locked_here?true:false, locked?true:false); } +static PyObject *py_wc_context_check_wc(PyObject *self, PyObject *args) +{ + PyObject* py_path; + const char *path; + apr_pool_t *pool; + svn_wc_context_t *wc_context = ((ContextObject *)self)->context; + int wc_format; + + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; + + pool = Pool(NULL); + + path = py_object_to_svn_abspath(py_path, pool); + if (path == NULL) { + apr_pool_destroy(pool); + return NULL; + } + + RUN_SVN_WITH_POOL(pool, svn_wc_check_wc2(&wc_format, wc_context, path, pool)); + + apr_pool_destroy(pool); + + return PyInt_FromLong(wc_format); +} + static PyMethodDef context_methods[] = { { "locked", py_wc_context_locked, METH_VARARGS, "locked(path) -> (locked_here, locked)\n" "Check whether a patch is locked."}, + { "check_wc", py_wc_context_check_wc, METH_VARARGS, "check_wc(path) -> wc_format\n" + "Check format version of a working copy." }, { NULL } }; -- cgit v1.2.3 From 43031d8ddb62eec2fcecee25edfee79e57d98c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 11:42:25 +0000 Subject: Add wc.Context.text_modified. --- subvertpy/tests/test_wc.py | 8 ++++++++ subvertpy/wc.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 3dc3fd32..f8e0aeb2 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -334,3 +334,11 @@ class ContextTests(SubversionTestCase): context = wc.Context() self.make_client("repos", "checkout") self.assertIsInstance(context.check_wc("checkout"), int) + + def test_text_modified(self): + context = wc.Context() + self.make_client("repos", "checkout") + with open('checkout/bla.txt', 'w') as f: + f.write("modified") + self.client_add("checkout/bla.txt") + self.assertTrue(context.text_modified("checkout/bla.txt")) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 652ffe1a..110b2924 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -1044,11 +1044,40 @@ static PyObject *py_wc_context_check_wc(PyObject *self, PyObject *args) return PyInt_FromLong(wc_format); } +static PyObject *py_wc_context_text_modified_p2(PyObject *self, PyObject *args) +{ + PyObject* py_path; + const char *path; + apr_pool_t *pool; + svn_wc_context_t *wc_context = ((ContextObject *)self)->context; + svn_boolean_t modified; + + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; + + pool = Pool(NULL); + + path = py_object_to_svn_abspath(py_path, pool); + if (path == NULL) { + apr_pool_destroy(pool); + return NULL; + } + + RUN_SVN_WITH_POOL(pool, svn_wc_text_modified_p2(&modified, wc_context, + path, FALSE, pool)); + + apr_pool_destroy(pool); + + return PyBool_FromLong(modified); +} + static PyMethodDef context_methods[] = { { "locked", py_wc_context_locked, METH_VARARGS, "locked(path) -> (locked_here, locked)\n" "Check whether a patch is locked."}, { "check_wc", py_wc_context_check_wc, METH_VARARGS, "check_wc(path) -> wc_format\n" "Check format version of a working copy." }, + { "text_modified", py_wc_context_text_modified_p2, METH_VARARGS, "text_modified(path) -> bool\n" + "Check whether text of a file is modified against base." }, { NULL } }; -- cgit v1.2.3 From ee8f97450d8a222afcba7453c28269deebb1e4a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 11:45:36 +0000 Subject: Add wc.Context.props_modified. --- subvertpy/tests/test_wc.py | 8 ++++++++ subvertpy/wc.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index f8e0aeb2..0d430546 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -342,3 +342,11 @@ class ContextTests(SubversionTestCase): f.write("modified") self.client_add("checkout/bla.txt") self.assertTrue(context.text_modified("checkout/bla.txt")) + + def test_props_modified(self): + context = wc.Context() + self.make_client("repos", "checkout") + with open('checkout/bla.txt', 'w') as f: + f.write("modified") + self.client_add("checkout/bla.txt") + self.assertFalse(context.props_modified("checkout/bla.txt")) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 110b2924..a61bcf10 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -1071,6 +1071,34 @@ static PyObject *py_wc_context_text_modified_p2(PyObject *self, PyObject *args) return PyBool_FromLong(modified); } +static PyObject *py_wc_context_props_modified_p2(PyObject *self, PyObject *args) +{ + PyObject* py_path; + const char *path; + apr_pool_t *pool; + svn_wc_context_t *wc_context = ((ContextObject *)self)->context; + svn_boolean_t modified; + + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; + + pool = Pool(NULL); + + path = py_object_to_svn_abspath(py_path, pool); + if (path == NULL) { + apr_pool_destroy(pool); + return NULL; + } + + RUN_SVN_WITH_POOL(pool, svn_wc_props_modified_p2(&modified, wc_context, + path, pool)); + + apr_pool_destroy(pool); + + return PyBool_FromLong(modified); +} + + static PyMethodDef context_methods[] = { { "locked", py_wc_context_locked, METH_VARARGS, "locked(path) -> (locked_here, locked)\n" "Check whether a patch is locked."}, @@ -1078,6 +1106,8 @@ static PyMethodDef context_methods[] = { "Check format version of a working copy." }, { "text_modified", py_wc_context_text_modified_p2, METH_VARARGS, "text_modified(path) -> bool\n" "Check whether text of a file is modified against base." }, + { "props_modified", py_wc_context_props_modified_p2, METH_VARARGS, "props_modified(path) -> bool\n" + "Check whether props of a file are modified against base." }, { NULL } }; -- cgit v1.2.3 From af04e529592658789c020df0d177454884c1d145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 11:49:45 +0000 Subject: Add wc.Context.conflicted. --- subvertpy/tests/test_wc.py | 10 ++++++++++ subvertpy/wc.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 0d430546..768cf9e2 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -350,3 +350,13 @@ class ContextTests(SubversionTestCase): f.write("modified") self.client_add("checkout/bla.txt") self.assertFalse(context.props_modified("checkout/bla.txt")) + + def test_conflicted(self): + context = wc.Context() + self.make_client("repos", "checkout") + with open('checkout/bla.txt', 'w') as f: + f.write("modified") + self.client_add("checkout/bla.txt") + self.assertEqual( + (False, False, False), + context.conflicted("checkout/bla.txt")) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index a61bcf10..279a17ac 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -1098,6 +1098,33 @@ static PyObject *py_wc_context_props_modified_p2(PyObject *self, PyObject *args) return PyBool_FromLong(modified); } +static PyObject *py_wc_context_conflicted(PyObject *self, PyObject *args) +{ + PyObject* py_path; + const char *path; + apr_pool_t *pool; + svn_wc_context_t *wc_context = ((ContextObject *)self)->context; + svn_boolean_t text_conflicted, props_conflicted, tree_conflicted; + + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; + + pool = Pool(NULL); + + path = py_object_to_svn_abspath(py_path, pool); + if (path == NULL) { + apr_pool_destroy(pool); + return NULL; + } + + RUN_SVN_WITH_POOL(pool, svn_wc_conflicted_p3( + &text_conflicted, &props_conflicted, &tree_conflicted, wc_context, + path, pool)); + + apr_pool_destroy(pool); + + return Py_BuildValue("(bbb)", text_conflicted, props_conflicted, tree_conflicted); +} static PyMethodDef context_methods[] = { { "locked", py_wc_context_locked, METH_VARARGS, "locked(path) -> (locked_here, locked)\n" @@ -1108,6 +1135,9 @@ static PyMethodDef context_methods[] = { "Check whether text of a file is modified against base." }, { "props_modified", py_wc_context_props_modified_p2, METH_VARARGS, "props_modified(path) -> bool\n" "Check whether props of a file are modified against base." }, + { "conflicted", py_wc_context_conflicted, METH_VARARGS, + "conflicted(path) -> (text_conflicted, prop_conflicted, tree_conflicted)\n" + "Check whether a path is conflicted." }, { NULL } }; -- cgit v1.2.3 From 9254b97b9c00c0ee22db8ba706f476b9fe620131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 12:06:57 +0000 Subject: Add wc.Context.crawl_revisions. --- subvertpy/tests/test_wc.py | 18 ++++++++++++++++++ subvertpy/wc.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 768cf9e2..b39f921c 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -360,3 +360,21 @@ class ContextTests(SubversionTestCase): self.assertEqual( (False, False, False), context.conflicted("checkout/bla.txt")) + + def test_crawl_revisions(self): + context = wc.Context() + self.make_client("repos", "checkout") + with open('checkout/bla.txt', 'w') as f: + f.write("modified") + self.client_add("checkout/bla.txt") + ret = [] + + class Reporter(object): + def set_path(self, *args): + ret.append(args) + + def finish(self): + pass + context.crawl_revisions("checkout", Reporter()) + + self.assertEqual(ret, [('', 0, 0, None, 3)]) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 279a17ac..09dfb134 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -1126,6 +1126,48 @@ static PyObject *py_wc_context_conflicted(PyObject *self, PyObject *args) return Py_BuildValue("(bbb)", text_conflicted, props_conflicted, tree_conflicted); } +static PyObject *py_wc_context_crawl_revisions(PyObject *self, PyObject *args, PyObject *kwargs) +{ + PyObject* py_path, *py_reporter; + const char *path; + apr_pool_t *pool; + svn_wc_context_t *wc_context = ((ContextObject *)self)->context; + char *kwnames[] = { "path", "reporter", "restore_files", "depth", + "honor_depth_exclude", "depth_compatibility_trick", "use_commit_time", + "cancel", "notify", NULL }; + bool restore_files = false; + int depth = svn_depth_infinity; + bool honor_depth_exclude = true; + bool depth_compatibility_trick = false; + bool use_commit_time = false; + PyObject *notify = Py_None; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bibbbO", kwnames, + &py_path, &py_reporter, &restore_files, + &depth, &honor_depth_exclude, + &depth_compatibility_trick, + &use_commit_time, ¬ify)) { + return NULL; + } + + pool = Pool(NULL); + + path = py_object_to_svn_abspath(py_path, pool); + if (path == NULL) { + apr_pool_destroy(pool); + return NULL; + } + + RUN_SVN_WITH_POOL(pool, svn_wc_crawl_revisions5( + wc_context, path, &py_ra_reporter3, py_reporter, restore_files, + depth, honor_depth_exclude, depth_compatibility_trick, + use_commit_time, py_cancel_check, NULL, py_wc_notify_func, notify, pool)); + + apr_pool_destroy(pool); + + Py_RETURN_NONE; +} + static PyMethodDef context_methods[] = { { "locked", py_wc_context_locked, METH_VARARGS, "locked(path) -> (locked_here, locked)\n" "Check whether a patch is locked."}, @@ -1138,6 +1180,9 @@ static PyMethodDef context_methods[] = { { "conflicted", py_wc_context_conflicted, METH_VARARGS, "conflicted(path) -> (text_conflicted, prop_conflicted, tree_conflicted)\n" "Check whether a path is conflicted." }, + { "crawl_revisions", (PyCFunction)py_wc_context_crawl_revisions, METH_VARARGS|METH_KEYWORDS, + "crawl_revisions(path, reporter, restore_files, depth, honor_depth_exclude, depth_compatibility_trick, use_commit_time, notify)\n" + "Do a depth-first crawl of the working copy." }, { NULL } }; -- cgit v1.2.3 From a4210e5b03432397557851db232757600c2d9abf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 17:34:29 +0000 Subject: Fix tests on pre-1.9 svn. --- subvertpy/client.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/subvertpy/client.c b/subvertpy/client.c index eb7cc750..2e9911dd 100644 --- a/subvertpy/client.c +++ b/subvertpy/client.c @@ -1000,6 +1000,8 @@ static PyObject *client_cat(PyObject *self, PyObject *args, PyObject *kwargs) } RUN_SVN_WITH_POOL(temp_pool, svn_client_cat2(stream, path, &c_peg_rev, &c_rev, client->client, temp_pool)); + ret = Py_None; + Py_INCREF(ret); #endif apr_pool_destroy(temp_pool); -- cgit v1.2.3 From 5078f7c23a0dabddbf5e761c1a896ae79ed4f7e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 18:18:50 +0000 Subject: Fix style. --- subvertpy/tests/test_subr.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/subvertpy/tests/test_subr.py b/subvertpy/tests/test_subr.py index 35cd1c97..b80290f0 100644 --- a/subvertpy/tests/test_subr.py +++ b/subvertpy/tests/test_subr.py @@ -23,9 +23,12 @@ from subvertpy.subr import uri_canonicalize class UriCanonicalizeTests(TestCase): def test_canonicalize(self): - self.assertEqual('https://www.example.com', + self.assertEqual( + 'https://www.example.com', uri_canonicalize('https://www.example.com/')) - self.assertEqual('https://www.example.com(bla)', + self.assertEqual( + 'https://www.example.com(bla)', uri_canonicalize('https://www.example.com(bla)')) - self.assertEqual('https://www.example.com/(bla)', + self.assertEqual( + 'https://www.example.com/(bla)', uri_canonicalize('https://www.example.com/(bla%29')) -- cgit v1.2.3 From 52015e4c21a1c16ad7bf26fc775a2b22a9ff81dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 18:19:02 +0000 Subject: Cope with new return value for client.commit. --- subvertpy/tests/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/subvertpy/tests/__init__.py b/subvertpy/tests/__init__.py index 6b88fb78..faf061b3 100644 --- a/subvertpy/tests/__init__.py +++ b/subvertpy/tests/__init__.py @@ -313,10 +313,13 @@ class SubversionTestCase(TestCaseInTempDir): olddir = os.path.abspath('.') self.next_message = message os.chdir(dir) - info = self.client_ctx.commit(["."], recursive, False) + info = [] + def add_info(*args): + info.append(args) + self.client_ctx.commit(["."], recursive, False, callback=add_info) os.chdir(olddir) - assert info is not None - return info + assert len(info) == 1 + return info[0] def client_add(self, relpath, recursive=True): """Add specified files to working copy. -- cgit v1.2.3 From 7a5b85db730e29fa868cc47e8d9aa187e11f6951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 18:25:02 +0000 Subject: Add py_object_to_adm_abspath. --- subvertpy/tests/test_wc.py | 4 ++-- subvertpy/wc_adm.c | 49 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index b39f921c..5a9d117a 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -88,10 +88,10 @@ class WcTests(SubversionTestCase): self.assertEqual((0, 0, 0, 0), ret) -class AdmTests(SubversionTestCase): +class AdmObjTests(SubversionTestCase): def setUp(self): - super(AdmTests, self).setUp() + super(AdmObjTests, self).setUp() if getattr(wc, "WorkingCopy", None) is None: raise SkipTest( "Subversion 1.7 API for WorkingCopy not yet supported") diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index 2d13df69..d0251203 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -126,6 +126,44 @@ static PyObject *adm_access_path(PyObject *self) return py_object_from_svn_abspath(svn_wc_adm_access_path(admobj->adm)); } +static const char *py_object_to_adm_abspath(PyObject *obj, PyObject *adm, apr_pool_t *pool) +{ + const char *ret; + PyObject *bytes_obj = NULL; + AdmObject *admobj = (AdmObject *)adm; + ADM_CHECK_CLOSED(admobj); + + if (PyUnicode_Check(obj)) { + bytes_obj = obj = PyUnicode_AsUTF8String(obj); + if (obj == NULL) { + return NULL; + } + } + + if (!PyBytes_Check(obj)) { + PyErr_SetString(PyExc_TypeError, + "URIs need to be UTF-8 bytestrings or unicode strings"); + Py_XDECREF(bytes_obj); + return NULL; + } + + ret = PyBytes_AsString(obj); + ret = apr_pstrdup(pool, ret); + Py_XDECREF(obj); + if (ret == NULL) { + return NULL; + } +#if ONLY_SINCE_SVN(1, 7) + if (svn_dirent_is_absolute(ret)) { + return ret; + } else { + return svn_dirent_join(svn_wc_adm_access_path(admobj->adm), ret, pool); + } +#else + return ret; +#endif +} + static PyObject *adm_locked(PyObject *self) { AdmObject *admobj = (AdmObject *)self; @@ -896,6 +934,7 @@ static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs "notify", NULL }; AdmObject *admobj = (AdmObject *)self; apr_pool_t *temp_pool; + PyObject *py_dst_path; char *dst_path, *copyfrom_url = NULL; svn_revnum_t copyfrom_rev = -1; PyObject *py_new_base_contents, *py_new_contents, *py_new_base_props, @@ -903,8 +942,8 @@ static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs svn_stream_t *new_contents, *new_base_contents; apr_hash_t *new_props, *new_base_props; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sOOOO|zlO", kwnames, - &dst_path, &py_new_base_contents, &py_new_contents, &py_new_base_props, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOOOO|zlO", kwnames, + &py_dst_path, &py_new_base_contents, &py_new_contents, &py_new_base_props, &py_new_props, ©from_url, ©from_rev, ¬ify)) return NULL; @@ -922,6 +961,8 @@ static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs new_contents = new_py_stream(temp_pool, py_new_contents); + dst_path = py_object_to_svn_dirent(py_dst_path, temp_pool); + #if ONLY_SINCE_SVN(1, 6) RUN_SVN_WITH_POOL(temp_pool, svn_wc_add_repos_file3(dst_path, admobj->adm, new_base_contents, @@ -1583,7 +1624,7 @@ static PyObject *conflicted(PyObject *self, PyObject *args) * * :return: A status object. */ -static PyObject *ra_status(PyObject *self, PyObject *args) +static PyObject *wc_status(PyObject *self, PyObject *args) { const char *path; svn_wc_status2_t *st; @@ -1679,7 +1720,7 @@ static PyMethodDef adm_methods[] = { "S.conflicted(path) -> (text_conflicted, prop_conflicted, tree_conflicted)" }, { "resolved_conflict", (PyCFunction)resolved_conflict, METH_VARARGS, "S.resolved_conflict(path, resolve_text, resolve_props, resolve_tree, depth, conflict_choice, notify_func=None, cancel=None)" }, - { "status", (PyCFunction)ra_status, METH_VARARGS, "status(wc_path) -> Status" }, + { "status", (PyCFunction)wc_status, METH_VARARGS, "status(wc_path) -> Status" }, { NULL, } }; -- cgit v1.2.3 From 751cc29c3bbbb74425b5cf410fbc253585ae33a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 21:08:40 +0000 Subject: Use PyUnicode for Python 3. --- subvertpy/subr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subvertpy/subr.c b/subvertpy/subr.c index 76d70331..2a2af7b1 100644 --- a/subvertpy/subr.c +++ b/subvertpy/subr.c @@ -36,7 +36,7 @@ static PyObject *py_uri_canonicalize(PyObject *self, PyObject *args) pool = Pool(NULL); uri = py_object_to_svn_uri(py_uri, pool); - ret = PyString_FromString(uri); + ret = PyUnicode_FromString(uri); apr_pool_destroy(pool); return ret; -- cgit v1.2.3 From 38ccb753f661c70f2935f5acf922940e1a974b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 21:12:47 +0000 Subject: FIx python3 compatibility. --- subvertpy/wc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 09dfb134..6b40f62c 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -1041,7 +1041,11 @@ static PyObject *py_wc_context_check_wc(PyObject *self, PyObject *args) apr_pool_destroy(pool); +#if PY_MAJOR_VERSION >= 3 + return PyLong_FromLong(wc_format); +#else return PyInt_FromLong(wc_format); +#endif } static PyObject *py_wc_context_text_modified_p2(PyObject *self, PyObject *args) -- cgit v1.2.3 From e9287b5a19be967eef98c89d40af0fb71a1ac378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 21:40:12 +0000 Subject: Re-enable adm tests on >= 1.7. --- subvertpy/tests/test_wc.py | 22 +++++++++++++--------- subvertpy/wc_adm.c | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 5a9d117a..1dc148e0 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -68,7 +68,7 @@ class AdmTests(TestCase): if wc.api_version() < (1, 5): self.assertRaises( NotImplementedError, wc.match_ignore_list, "foo", []) - return # Skip test + self.skipTest("match_ignore_list not supported with svn < 1.5") self.assertTrue(wc.match_ignore_list("foo", ["f*"])) self.assertTrue(wc.match_ignore_list("foo", ["foo"])) self.assertFalse(wc.match_ignore_list("foo", [])) @@ -90,13 +90,9 @@ class WcTests(SubversionTestCase): class AdmObjTests(SubversionTestCase): - def setUp(self): - super(AdmObjTests, self).setUp() - if getattr(wc, "WorkingCopy", None) is None: - raise SkipTest( - "Subversion 1.7 API for WorkingCopy not yet supported") - def test_has_binary_prop(self): + if wc.api_version() >= (1, 7): + self.skipTest("TODO: doesn't yet work with svn >= 1.7") self.make_client("repos", "checkout") self.build_tree({"checkout/bar": b"\x00 \x01"}) self.client_add('checkout/bar') @@ -122,6 +118,8 @@ class AdmObjTests(SubversionTestCase): adm.close() def test_add_repos_file(self): + if wc.api_version() >= (1, 7): + self.skipTest("TODO: doesn't yet work with svn >= 1.7") self.make_client("repos", "checkout") adm = wc.Adm(None, "checkout", True) adm.add_repos_file("checkout/bar", BytesIO(b"basecontents"), @@ -130,6 +128,8 @@ class AdmObjTests(SubversionTestCase): wc.get_pristine_contents("checkout/bar").read()) def test_mark_missing_deleted(self): + if wc.api_version() >= (1, 7): + self.skipTest("TODO: doesn't yet work with svn >= 1.7") self.make_client("repos", "checkout") self.build_tree({"checkout/bar": b"\x00 \x01"}) self.client_add('checkout/bar') @@ -152,6 +152,8 @@ class AdmObjTests(SubversionTestCase): adm.relocate("checkout", "file://", "http://") def test_translated_stream(self): + if wc.api_version() >= (1, 7): + self.skipTest("TODO: doesn't yet work with svn >= 1.7") self.make_client("repos", "checkout") self.build_tree({"checkout/bar": b"My id: $Id$"}) self.client_add('checkout/bar') @@ -195,8 +197,6 @@ class AdmObjTests(SubversionTestCase): self.assertEqual(adm.prop_get("aprop", "checkout/bar"), None) def test_committed_queue(self): - if getattr(wc, "CommittedQueue", None) is None: - raise SkipTest("CommittedQueue not available") cq = wc.CommittedQueue() self.make_client("repos", "checkout") adm = wc.Adm(None, "checkout", True) @@ -248,6 +248,8 @@ class AdmObjTests(SubversionTestCase): self.assertEqual(wc.STATUS_NORMAL, adm.status('bar').status) def test_transmit_text_deltas(self): + if wc.api_version() >= (1, 7): + self.skipTest("TODO: doesn't yet work with svn >= 1.7") self.make_client("repos", ".") self.build_tree({"bar": b"blala"}) self.client_add('bar') @@ -290,6 +292,8 @@ class AdmObjTests(SubversionTestCase): self.assertEqual(1, bar.revision) def test_process_committed_queue(self): + if wc.api_version() >= (1, 7): + self.skipTest("TODO: doesn't yet work with svn >= 1.7") self.make_client("repos", "checkout") self.build_tree({"checkout/bar": b"la"}) self.client_add('checkout/bar') diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index d0251203..4bbed5ea 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -1643,7 +1643,7 @@ static PyObject *wc_status(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_adm_abspath(py_path, self, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; -- cgit v1.2.3 From b8cede9f521e5c1e73e2a42480915c9098476fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 4 Aug 2017 21:42:52 +0000 Subject: Fix style errors. --- subvertpy/tests/__init__.py | 1 + subvertpy/tests/test_wc.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/subvertpy/tests/__init__.py b/subvertpy/tests/__init__.py index faf061b3..30dfdee2 100644 --- a/subvertpy/tests/__init__.py +++ b/subvertpy/tests/__init__.py @@ -314,6 +314,7 @@ class SubversionTestCase(TestCaseInTempDir): self.next_message = message os.chdir(dir) info = [] + def add_info(*args): info.append(args) self.client_ctx.commit(["."], recursive, False, callback=add_info) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 1dc148e0..fe0abb2f 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -17,7 +17,6 @@ from io import BytesIO import os -from unittest import SkipTest import subvertpy from subvertpy import ( -- cgit v1.2.3 From a701fbce0f6f40854a664ddcb75163511eff9cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 5 Aug 2017 00:22:44 +0000 Subject: Fix has_binary_prop test. --- subvertpy/tests/test_wc.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index fe0abb2f..7889730b 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -90,14 +90,12 @@ class WcTests(SubversionTestCase): class AdmObjTests(SubversionTestCase): def test_has_binary_prop(self): - if wc.api_version() >= (1, 7): - self.skipTest("TODO: doesn't yet work with svn >= 1.7") self.make_client("repos", "checkout") self.build_tree({"checkout/bar": b"\x00 \x01"}) self.client_add('checkout/bar') + self.client_set_prop('checkout/bar', 'svn:mime-type', 'text/bar') adm = wc.Adm(None, "checkout") - path = os.path.join(self.test_dir, "checkout/bar") - self.assertFalse(adm.has_binary_prop(path)) + self.assertFalse(adm.has_binary_prop("checkout/bar")) adm.close() def test_get_ancestry(self): -- cgit v1.2.3 From c54ca2137cbf6b9ecf95477bbd2126d4c2f15937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 5 Aug 2017 00:53:34 +0000 Subject: Make paths unicode. --- subvertpy/util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subvertpy/util.h b/subvertpy/util.h index beb86e4d..1d6819a4 100644 --- a/subvertpy/util.h +++ b/subvertpy/util.h @@ -157,7 +157,7 @@ const char *py_object_to_svn_relpath(PyObject *obj, apr_pool_t *pool); const char *py_object_to_svn_path_or_url(PyObject *obj, apr_pool_t *pool); char *py_object_to_svn_string(PyObject *obj, apr_pool_t *pool); const char *py_object_to_svn_abspath(PyObject *obj, apr_pool_t *pool); -#define py_object_from_svn_abspath PyBytes_FromString +#define py_object_from_svn_abspath PyUnicode_FromString #if PY_MAJOR_VERSION >= 3 #define PyRepr_FromFormat PyUnicode_FromFormat -- cgit v1.2.3 From b5b4e42f00bbe91ebd3d8501c03011158a76cce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 5 Aug 2017 00:55:16 +0000 Subject: Fix other python3 unicode errors. --- subvertpy/tests/test_wc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 7889730b..31939a9d 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -44,13 +44,13 @@ class VersionTest(TestCase): class AdmTests(TestCase): def test_get_adm_dir(self): - self.assertEqual(b".svn", wc.get_adm_dir()) + self.assertEqual(".svn", wc.get_adm_dir()) def test_set_adm_dir(self): old_dir_name = wc.get_adm_dir() try: wc.set_adm_dir(b"_svn") - self.assertEqual(b"_svn", wc.get_adm_dir()) + self.assertEqual("_svn", wc.get_adm_dir()) finally: wc.set_adm_dir(old_dir_name) @@ -189,7 +189,7 @@ class AdmObjTests(SubversionTestCase): self.client_add('checkout/bar') adm = wc.Adm(None, "checkout", True) adm.prop_set("aprop", "avalue", "checkout/bar") - self.assertEqual(adm.prop_get("aprop", "checkout/bar"), "avalue") + self.assertEqual(adm.prop_get("aprop", "checkout/bar"), b"avalue") adm.prop_set("aprop", None, "checkout/bar") self.assertEqual(adm.prop_get("aprop", "checkout/bar"), None) -- cgit v1.2.3 From 29397b9dd461c51bb90382443b0cf2c079635ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 5 Aug 2017 10:13:00 +0000 Subject: Factor out dirent_hash_to_dict. --- subvertpy/_ra.c | 42 +++++++----------------------------------- subvertpy/util.c | 39 +++++++++++++++++++++++++++++++++++++++ subvertpy/util.h | 1 + subvertpy/wc_adm.c | 3 ++- 4 files changed, 49 insertions(+), 36 deletions(-) diff --git a/subvertpy/_ra.c b/subvertpy/_ra.c index 4c14131a..ae9f75e7 100644 --- a/subvertpy/_ra.c +++ b/subvertpy/_ra.c @@ -1475,18 +1475,14 @@ static PyObject *ra_get_dir(PyObject *self, PyObject *args, PyObject *kwargs) { apr_pool_t *temp_pool; apr_hash_t *dirents; - apr_hash_index_t *idx; apr_hash_t *props; svn_revnum_t fetch_rev; - const char *key; RemoteAccessObject *ra = (RemoteAccessObject *)self; - svn_dirent_t *dirent; - apr_ssize_t klen; const char *path; PyObject *py_path; svn_revnum_t revision = -1; unsigned int dirent_fields = 0; - PyObject *py_dirents, *py_props; + PyObject *py_dirents = NULL, *py_props; char *kwnames[] = { "path", "revision", "fields", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|lI:get_dir", kwnames, @@ -1517,45 +1513,21 @@ static PyObject *ra_get_dir(PyObject *self, PyObject *args, PyObject *kwargs) py_dirents = Py_None; Py_INCREF(py_dirents); } else { - py_dirents = PyDict_New(); - if (py_dirents == NULL) { - goto fail; - } - idx = apr_hash_first(temp_pool, dirents); - while (idx != NULL) { - PyObject *item, *pykey; - apr_hash_this(idx, (const void **)&key, &klen, (void **)&dirent); - item = py_dirent(dirent, dirent_fields); - if (item == NULL) { - goto fail_dirents; - } - if (key == NULL) { - pykey = Py_None; - Py_INCREF(pykey); - } else { - pykey = PyUnicode_FromString((char *)key); - } - if (PyDict_SetItem(py_dirents, pykey, item) != 0) { - Py_DECREF(item); - Py_DECREF(pykey); - goto fail_dirents; - } - Py_DECREF(pykey); - Py_DECREF(item); - idx = apr_hash_next(idx); - } + py_dirents = dirent_hash_to_dict(dirents, dirent_fields, temp_pool); + if (py_dirents == NULL) { + goto fail; + } } py_props = prop_hash_to_dict(props); if (py_props == NULL) { - goto fail_dirents; + goto fail; } apr_pool_destroy(temp_pool); return Py_BuildValue("(NlN)", py_dirents, fetch_rev, py_props); -fail_dirents: - Py_DECREF(py_dirents); fail: + Py_XDECREF(py_dirents); apr_pool_destroy(temp_pool); return NULL; } diff --git a/subvertpy/util.c b/subvertpy/util.c index 5e4c9170..208e58ff 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -1137,3 +1137,42 @@ PyTypeObject Stream_Type = { .tp_new = stream_init, /* tp_new tp_new */ }; + +PyObject *dirent_hash_to_dict(apr_hash_t *dirents, unsigned int dirent_fields, apr_pool_t *temp_pool) +{ + svn_dirent_t *dirent; + apr_ssize_t klen; + const char *key; + apr_hash_index_t *idx; + PyObject *py_dirents = PyDict_New(); + + if (py_dirents == NULL) { + return NULL; + } + idx = apr_hash_first(temp_pool, dirents); + while (idx != NULL) { + PyObject *item, *pykey; + apr_hash_this(idx, (const void **)&key, &klen, (void **)&dirent); + item = py_dirent(dirent, dirent_fields); + if (item == NULL) { + Py_DECREF(py_dirents); + return NULL; + } + if (key == NULL) { + pykey = Py_None; + Py_INCREF(pykey); + } else { + pykey = PyUnicode_FromString((char *)key); + } + if (PyDict_SetItem(py_dirents, pykey, item) != 0) { + Py_DECREF(item); + Py_DECREF(pykey); + Py_DECREF(py_dirents); + return NULL; + } + Py_DECREF(pykey); + Py_DECREF(item); + idx = apr_hash_next(idx); + } + return py_dirents; +} diff --git a/subvertpy/util.h b/subvertpy/util.h index 1d6819a4..3e62e4c2 100644 --- a/subvertpy/util.h +++ b/subvertpy/util.h @@ -87,6 +87,7 @@ PyObject *PyErr_NewSubversionException(svn_error_t *error); apr_hash_t *config_hash_from_object(PyObject *config, apr_pool_t *pool); void PyErr_SetAprStatus(apr_status_t status); PyObject *py_dirent(const svn_dirent_t *dirent, int dirent_fields); +PyObject *dirent_hash_to_dict(apr_hash_t *dirents, unsigned int dirent_fields, apr_pool_t *temp_pool); PyObject *PyOS_tmpfile(void); PyObject *pyify_changed_paths(apr_hash_t *changed_paths, bool node_kind, apr_pool_t *pool); bool pyify_log_message( diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index 4bbed5ea..02853329 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -935,7 +935,8 @@ static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs AdmObject *admobj = (AdmObject *)self; apr_pool_t *temp_pool; PyObject *py_dst_path; - char *dst_path, *copyfrom_url = NULL; + const char *dst_path; + char *copyfrom_url = NULL; svn_revnum_t copyfrom_rev = -1; PyObject *py_new_base_contents, *py_new_contents, *py_new_base_props, *py_new_props, *notify = Py_None; -- cgit v1.2.3 From d6bc710752a6e4dd0e984ea0567be5734d84a213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Mon, 7 Aug 2017 10:56:03 +0000 Subject: Ignore .pybuild. --- .bzrignore | 1 + .gitignore | 1 + 2 files changed, 2 insertions(+) diff --git a/.bzrignore b/.bzrignore index 23b37364..befb80e5 100644 --- a/.bzrignore +++ b/.bzrignore @@ -7,3 +7,4 @@ tags .testrepository .noseids apidocs +.pybuild diff --git a/.gitignore b/.gitignore index 3e4ab410..f38e1928 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ apidocs *.so *~ .tox/ +.pybuild -- cgit v1.2.3 From e0b6ee55fea662b289eee7a904cbc8c208d77a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Tue, 8 Aug 2017 22:14:14 +0000 Subject: Add subvertpy.wc.Context.get_update_editor. --- subvertpy/tests/test_wc.py | 6 ++ subvertpy/wc.c | 154 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 147 insertions(+), 13 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 31939a9d..8372f570 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -379,3 +379,9 @@ class ContextTests(SubversionTestCase): context.crawl_revisions("checkout", Reporter()) self.assertEqual(ret, [('', 0, 0, None, 3)]) + + def test_get_update_editor(self): + repos_url = self.make_client("repos", "checkout") + context = wc.Context() + editor = context.get_update_editor("checkout", "") + editor.close() diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 6b40f62c..2534bf5d 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -1137,20 +1137,20 @@ static PyObject *py_wc_context_crawl_revisions(PyObject *self, PyObject *args, P apr_pool_t *pool; svn_wc_context_t *wc_context = ((ContextObject *)self)->context; char *kwnames[] = { "path", "reporter", "restore_files", "depth", - "honor_depth_exclude", "depth_compatibility_trick", "use_commit_time", + "honor_depth_exclude", "depth_compatibility_trick", "use_commit_times", "cancel", "notify", NULL }; bool restore_files = false; int depth = svn_depth_infinity; bool honor_depth_exclude = true; bool depth_compatibility_trick = false; - bool use_commit_time = false; + bool use_commit_times = false; PyObject *notify = Py_None; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bibbbO", kwnames, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bibbbOO", kwnames, &py_path, &py_reporter, &restore_files, &depth, &honor_depth_exclude, &depth_compatibility_trick, - &use_commit_time, ¬ify)) { + &use_commit_times, ¬ify)) { return NULL; } @@ -1165,28 +1165,156 @@ static PyObject *py_wc_context_crawl_revisions(PyObject *self, PyObject *args, P RUN_SVN_WITH_POOL(pool, svn_wc_crawl_revisions5( wc_context, path, &py_ra_reporter3, py_reporter, restore_files, depth, honor_depth_exclude, depth_compatibility_trick, - use_commit_time, py_cancel_check, NULL, py_wc_notify_func, notify, pool)); + use_commit_times, py_cancel_check, NULL, py_wc_notify_func, notify, pool)); apr_pool_destroy(pool); Py_RETURN_NONE; } +static void context_done_handler(void *self) +{ + PyObject *selfobj = (PyObject *)self; + + Py_DECREF(selfobj); +} + +static PyObject *py_wc_context_get_update_editor(PyObject *self, PyObject *args, PyObject *kwargs) +{ + char *kwnames[] = { + "anchor_abspath", "target_basename", "use_commit_times", "depth", + "depth_is_sticky", "allow_unver_obstructions", "adds_as_modification", + "server_performs_filtering", "clean_checkout", "diff3_cmd", + "preserved_exts", "dirents_func", "conflict_func", "external_func", + "notify_func", NULL }; + const svn_delta_editor_t *editor; + void *edit_baton; + char *anchor_abspath; + char *target_basename; + char *diff3_cmd = NULL; + svn_wc_context_t *wc_context = ((ContextObject *)self)->context; + bool use_commit_times = false; + int depth = svn_depth_infinity; + bool depth_is_sticky = false; + bool allow_unver_obstructions = true; + bool adds_as_modification = false; + bool server_performs_filtering = false; + bool clean_checkout = false; + apr_array_header_t *preserved_exts = NULL; + PyObject *py_preserved_exts = Py_None; + PyObject *dirents_func = Py_None; + PyObject *conflict_func = Py_None; + PyObject *external_func = Py_None; + PyObject *notify_func = Py_None; + PyObject *py_anchor_abspath; + apr_pool_t *result_pool, *scratch_pool; + svn_error_t *err; + svn_revnum_t target_revision; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|bibbbbbzOOOOO", kwnames, + &py_anchor_abspath, &target_basename, + &use_commit_times, &depth, + &depth_is_sticky, + &allow_unver_obstructions, + &adds_as_modification, + &server_performs_filtering, + &clean_checkout, &py_preserved_exts, + &dirents_func, &conflict_func, + &external_func, ¬ify_func)) { + return NULL; + } + + if (conflict_func != Py_None) { + // TODO + PyErr_SetString(PyExc_NotImplementedError, + "conflict_func is not currently supported"); + return NULL; + } + + if (external_func != Py_None) { + // TODO + PyErr_SetString(PyExc_NotImplementedError, + "external_func is not currently supported"); + return NULL; + } + + if (dirents_func != Py_None) { + // TODO + PyErr_SetString(PyExc_NotImplementedError, + "dirents_func is not currently supported"); + return NULL; + } + + scratch_pool = Pool(NULL); + + anchor_abspath = py_object_to_svn_abspath(py_anchor_abspath, scratch_pool); + + if (py_preserved_exts != Py_None) { + if (!string_list_to_apr_array(scratch_pool, py_preserved_exts, &preserved_exts)) { + apr_pool_destroy(scratch_pool); + return NULL; + } + } + + result_pool = Pool(NULL); + + Py_BEGIN_ALLOW_THREADS + err = svn_wc_get_update_editor4( + &editor, &edit_baton, &target_revision, wc_context, + anchor_abspath, target_basename, use_commit_times, depth, + depth_is_sticky, allow_unver_obstructions, adds_as_modification, + server_performs_filtering, clean_checkout, diff3_cmd, + preserved_exts, NULL, dirents_func, NULL, conflict_func, NULL, + external_func, py_cancel_check, NULL, py_wc_notify_func, + notify_func, result_pool, scratch_pool); + Py_END_ALLOW_THREADS + + apr_pool_destroy(scratch_pool); + + if (err != NULL) { + handle_svn_error(err); + svn_error_clear(err); + apr_pool_destroy(result_pool); + return NULL; + } + + /* TODO: Also return target_revision ? */ + Py_INCREF(self); + return new_editor_object(NULL, editor, edit_baton, result_pool, &Editor_Type, + context_done_handler, self, NULL); +} + static PyMethodDef context_methods[] = { - { "locked", py_wc_context_locked, METH_VARARGS, "locked(path) -> (locked_here, locked)\n" - "Check whether a patch is locked."}, - { "check_wc", py_wc_context_check_wc, METH_VARARGS, "check_wc(path) -> wc_format\n" + { "locked", py_wc_context_locked, METH_VARARGS, + "locked(path) -> (locked_here, locked)\n" + "Check whether a patch is locked."}, + { "check_wc", py_wc_context_check_wc, METH_VARARGS, + "check_wc(path) -> wc_format\n" "Check format version of a working copy." }, - { "text_modified", py_wc_context_text_modified_p2, METH_VARARGS, "text_modified(path) -> bool\n" + { "text_modified", py_wc_context_text_modified_p2, METH_VARARGS, + "text_modified(path) -> bool\n" "Check whether text of a file is modified against base." }, - { "props_modified", py_wc_context_props_modified_p2, METH_VARARGS, "props_modified(path) -> bool\n" + { "props_modified", py_wc_context_props_modified_p2, METH_VARARGS, + "props_modified(path) -> bool\n" "Check whether props of a file are modified against base." }, { "conflicted", py_wc_context_conflicted, METH_VARARGS, - "conflicted(path) -> (text_conflicted, prop_conflicted, tree_conflicted)\n" + "conflicted(path) -> (text_conflicted, prop_conflicted, " + "tree_conflicted)\n" "Check whether a path is conflicted." }, - { "crawl_revisions", (PyCFunction)py_wc_context_crawl_revisions, METH_VARARGS|METH_KEYWORDS, - "crawl_revisions(path, reporter, restore_files, depth, honor_depth_exclude, depth_compatibility_trick, use_commit_time, notify)\n" + { "crawl_revisions", (PyCFunction)py_wc_context_crawl_revisions, + METH_VARARGS|METH_KEYWORDS, + "crawl_revisions(path, reporter, restore_files, depth, " + "honor_depth_exclude, depth_compatibility_trick, " + "use_commit_time, notify)\n" "Do a depth-first crawl of the working copy." }, + { "get_update_editor", + (PyCFunction)py_wc_context_get_update_editor, + METH_VARARGS|METH_KEYWORDS, + "get_update_editor(anchor_abspath, target_basename, use_commit_time, " + "depth, depth_is_sticky, allow_unver_obstructions, " + "adds_as_modification, server_performs_filtering, clean_checkout, " + "diff3_cmd, dirent_func=None, conflict_func=None, " + "external_func=None) -> target_revnum" }, { NULL } }; -- cgit v1.2.3 From 6e50f93113b6c707e3a46b63031fe69dd6024d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Wed, 9 Aug 2017 00:57:32 +0000 Subject: Add ensure_adm. --- subvertpy/wc.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 2534bf5d..f2076ba7 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -1189,7 +1189,7 @@ static PyObject *py_wc_context_get_update_editor(PyObject *self, PyObject *args, "notify_func", NULL }; const svn_delta_editor_t *editor; void *edit_baton; - char *anchor_abspath; + const char *anchor_abspath; char *target_basename; char *diff3_cmd = NULL; svn_wc_context_t *wc_context = ((ContextObject *)self)->context; @@ -1284,6 +1284,39 @@ static PyObject *py_wc_context_get_update_editor(PyObject *self, PyObject *args, context_done_handler, self, NULL); } +static PyObject *py_wc_context_ensure_adm(PyObject *self, PyObject *args, + PyObject *kwargs) +{ + ContextObject *context_obj = (ContextObject *)self; + char *kwnames[] = { + "local_abspath", "url", "repos_root_url", "repos_uuid", + "revnum", "depth", NULL }; + char *local_abspath; + char *url; + char *repos_root_url; + char *repos_uuid; + int revnum; + int depth; + apr_pool_t *pool; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ssssii", kwnames, + &local_abspath, &url, &repos_root_url, + &repos_uuid, &revnum, &depth)) { + return NULL; + } + + pool = Pool(NULL); + + RUN_SVN_WITH_POOL(pool, svn_wc_ensure_adm4(context_obj->context, + local_abspath, url, + repos_root_url, repos_uuid, + revnum, depth, pool)); + + apr_pool_destroy(pool); + + Py_RETURN_NONE; +} + static PyMethodDef context_methods[] = { { "locked", py_wc_context_locked, METH_VARARGS, "locked(path) -> (locked_here, locked)\n" @@ -1315,6 +1348,10 @@ static PyMethodDef context_methods[] = { "adds_as_modification, server_performs_filtering, clean_checkout, " "diff3_cmd, dirent_func=None, conflict_func=None, " "external_func=None) -> target_revnum" }, + { "ensure_adm", + (PyCFunction)py_wc_context_ensure_adm, + METH_VARARGS|METH_KEYWORDS, + "ensure_adm(local_abspath, url, repos_root_url, repos_uuid, revnum, depth)" }, { NULL } }; -- cgit v1.2.3 From 5bc6b286831ac8ad10536f5abb5a57fefaa145a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Wed, 9 Aug 2017 01:03:22 +0000 Subject: Formatting fixes. --- subvertpy/wc.c | 236 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 128 insertions(+), 108 deletions(-) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index f2076ba7..7ff60ac4 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -39,31 +39,31 @@ static PyTypeObject Context_Type; #if ONLY_BEFORE_SVN(1, 5) struct svn_wc_committed_queue_t { - apr_pool_t *pool; - apr_array_header_t *queue; - svn_boolean_t have_recursive; + apr_pool_t *pool; + apr_array_header_t *queue; + svn_boolean_t have_recursive; }; typedef struct { - const char *path; - svn_wc_adm_access_t *adm_access; - svn_boolean_t recurse; - svn_boolean_t remove_lock; - apr_array_header_t *wcprop_changes; - unsigned char *digest; + const char *path; + svn_wc_adm_access_t *adm_access; + svn_boolean_t recurse; + svn_boolean_t remove_lock; + apr_array_header_t *wcprop_changes; + unsigned char *digest; } committed_queue_item_t; svn_wc_committed_queue_t *svn_wc_committed_queue_create(apr_pool_t *pool) { - svn_wc_committed_queue_t *q; + svn_wc_committed_queue_t *q; - q = apr_palloc(pool, sizeof(*q)); - q->pool = pool; - q->queue = apr_array_make(pool, 1, sizeof(committed_queue_item_t *)); - q->have_recursive = FALSE; + q = apr_palloc(pool, sizeof(*q)); + q->pool = pool; + q->queue = apr_array_make(pool, 1, sizeof(committed_queue_item_t *)); + q->have_recursive = FALSE; - return q; + return q; } svn_error_t *svn_wc_queue_committed(svn_wc_committed_queue_t **queue, @@ -101,9 +101,9 @@ svn_error_t *svn_wc_queue_committed(svn_wc_committed_queue_t **queue, #endif typedef struct { - PyObject_VAR_HEAD - apr_pool_t *pool; - svn_wc_committed_queue_t *queue; + PyObject_VAR_HEAD + apr_pool_t *pool; + svn_wc_committed_queue_t *queue; } CommittedQueueObject; svn_wc_committed_queue_t *PyObject_GetCommittedQueue(PyObject *obj) @@ -112,127 +112,147 @@ svn_wc_committed_queue_t *PyObject_GetCommittedQueue(PyObject *obj) } #if ONLY_SINCE_SVN(1, 5) -static svn_error_t *py_ra_report3_set_path(void *baton, const char *path, svn_revnum_t revision, svn_depth_t depth, int start_empty, const char *lock_token, apr_pool_t *pool) +static svn_error_t *py_ra_report3_set_path(void *baton, const char *path, + svn_revnum_t revision, + svn_depth_t depth, int start_empty, + const char *lock_token, apr_pool_t *pool) { - PyObject *self = (PyObject *)baton, *py_lock_token, *ret; - PyGILState_STATE state = PyGILState_Ensure(); - if (lock_token == NULL) { - py_lock_token = Py_None; - Py_INCREF(py_lock_token); - } else { - py_lock_token = PyBytes_FromString(lock_token); - } - ret = PyObject_CallMethod(self, "set_path", "slbOi", path, revision, start_empty, py_lock_token, depth); - Py_DECREF(py_lock_token); - CB_CHECK_PYRETVAL(ret); - Py_DECREF(ret); - PyGILState_Release(state); - return NULL; + PyObject *self = (PyObject *)baton, *py_lock_token, *ret; + PyGILState_STATE state = PyGILState_Ensure(); + if (lock_token == NULL) { + py_lock_token = Py_None; + Py_INCREF(py_lock_token); + } else { + py_lock_token = PyBytes_FromString(lock_token); + } + ret = PyObject_CallMethod(self, "set_path", "slbOi", path, revision, + start_empty, py_lock_token, depth); + Py_DECREF(py_lock_token); + CB_CHECK_PYRETVAL(ret); + Py_DECREF(ret); + PyGILState_Release(state); + return NULL; } -static svn_error_t *py_ra_report3_link_path(void *report_baton, const char *path, const char *url, svn_revnum_t revision, svn_depth_t depth, int start_empty, const char *lock_token, apr_pool_t *pool) +static svn_error_t *py_ra_report3_link_path(void *report_baton, + const char *path, const char *url, + svn_revnum_t revision, + svn_depth_t depth, int start_empty, + const char *lock_token, apr_pool_t *pool) { - PyObject *self = (PyObject *)report_baton, *ret, *py_lock_token; - PyGILState_STATE state = PyGILState_Ensure(); - if (lock_token == NULL) { - py_lock_token = Py_None; - Py_INCREF(py_lock_token); - } else { - py_lock_token = PyBytes_FromString(lock_token); - } - ret = PyObject_CallMethod(self, "link_path", "sslbOi", path, url, revision, start_empty, py_lock_token, depth); - Py_DECREF(py_lock_token); - CB_CHECK_PYRETVAL(ret); - Py_DECREF(ret); - PyGILState_Release(state); - return NULL; + PyObject *self = (PyObject *)report_baton, *ret, *py_lock_token; + PyGILState_STATE state = PyGILState_Ensure(); + if (lock_token == NULL) { + py_lock_token = Py_None; + Py_INCREF(py_lock_token); + } else { + py_lock_token = PyBytes_FromString(lock_token); + } + ret = PyObject_CallMethod(self, "link_path", "sslbOi", path, url, revision, + start_empty, py_lock_token, depth); + Py_DECREF(py_lock_token); + CB_CHECK_PYRETVAL(ret); + Py_DECREF(ret); + PyGILState_Release(state); + return NULL; } #endif -static svn_error_t *py_ra_report2_set_path(void *baton, const char *path, svn_revnum_t revision, int start_empty, const char *lock_token, apr_pool_t *pool) +static svn_error_t *py_ra_report2_set_path(void *baton, const char *path, + svn_revnum_t revision, + int start_empty, const char *lock_token, + apr_pool_t *pool) { - PyObject *self = (PyObject *)baton, *py_lock_token, *ret; - PyGILState_STATE state = PyGILState_Ensure(); - if (lock_token == NULL) { - py_lock_token = Py_None; - Py_INCREF(py_lock_token); - } else { - py_lock_token = PyBytes_FromString(lock_token); - } - ret = PyObject_CallMethod(self, "set_path", "slbOi", path, revision, start_empty, py_lock_token, svn_depth_infinity); - CB_CHECK_PYRETVAL(ret); - Py_DECREF(ret); - PyGILState_Release(state); - return NULL; + PyObject *self = (PyObject *)baton, *py_lock_token, *ret; + PyGILState_STATE state = PyGILState_Ensure(); + if (lock_token == NULL) { + py_lock_token = Py_None; + Py_INCREF(py_lock_token); + } else { + py_lock_token = PyBytes_FromString(lock_token); + } + ret = PyObject_CallMethod(self, "set_path", "slbOi", path, revision, + start_empty, py_lock_token, svn_depth_infinity); + CB_CHECK_PYRETVAL(ret); + Py_DECREF(ret); + PyGILState_Release(state); + return NULL; } -static svn_error_t *py_ra_report2_link_path(void *report_baton, const char *path, const char *url, svn_revnum_t revision, int start_empty, const char *lock_token, apr_pool_t *pool) +static svn_error_t *py_ra_report2_link_path(void *report_baton, + const char *path, const char *url, + svn_revnum_t revision, + int start_empty, + const char *lock_token, + apr_pool_t *pool) { - PyObject *self = (PyObject *)report_baton, *ret, *py_lock_token; - PyGILState_STATE state = PyGILState_Ensure(); - if (lock_token == NULL) { - py_lock_token = Py_None; - Py_INCREF(py_lock_token); - } else { - py_lock_token = PyBytes_FromString(lock_token); - } - ret = PyObject_CallMethod(self, "link_path", "sslbOi", path, url, revision, start_empty, py_lock_token, svn_depth_infinity); - CB_CHECK_PYRETVAL(ret); - Py_DECREF(ret); - PyGILState_Release(state); - return NULL; + PyObject *self = (PyObject *)report_baton, *ret, *py_lock_token; + PyGILState_STATE state = PyGILState_Ensure(); + if (lock_token == NULL) { + py_lock_token = Py_None; + Py_INCREF(py_lock_token); + } else { + py_lock_token = PyBytes_FromString(lock_token); + } + ret = PyObject_CallMethod(self, "link_path", "sslbOi", path, url, revision, + start_empty, py_lock_token, svn_depth_infinity); + CB_CHECK_PYRETVAL(ret); + Py_DECREF(ret); + PyGILState_Release(state); + return NULL; } -static svn_error_t *py_ra_report_delete_path(void *baton, const char *path, apr_pool_t *pool) +static svn_error_t *py_ra_report_delete_path(void *baton, const char *path, + apr_pool_t *pool) { - PyObject *self = (PyObject *)baton, *ret; - PyGILState_STATE state = PyGILState_Ensure(); - ret = PyObject_CallMethod(self, "delete_path", "s", path); - CB_CHECK_PYRETVAL(ret); - Py_DECREF(ret); - PyGILState_Release(state); - return NULL; + PyObject *self = (PyObject *)baton, *ret; + PyGILState_STATE state = PyGILState_Ensure(); + ret = PyObject_CallMethod(self, "delete_path", "s", path); + CB_CHECK_PYRETVAL(ret); + Py_DECREF(ret); + PyGILState_Release(state); + return NULL; } static svn_error_t *py_ra_report_finish(void *baton, apr_pool_t *pool) { - PyObject *self = (PyObject *)baton, *ret; - PyGILState_STATE state = PyGILState_Ensure(); - ret = PyObject_CallMethod(self, "finish", ""); - CB_CHECK_PYRETVAL(ret); - Py_DECREF(ret); - PyGILState_Release(state); - return NULL; + PyObject *self = (PyObject *)baton, *ret; + PyGILState_STATE state = PyGILState_Ensure(); + ret = PyObject_CallMethod(self, "finish", ""); + CB_CHECK_PYRETVAL(ret); + Py_DECREF(ret); + PyGILState_Release(state); + return NULL; } static svn_error_t *py_ra_report_abort(void *baton, apr_pool_t *pool) { - PyObject *self = (PyObject *)baton, *ret; - PyGILState_STATE state = PyGILState_Ensure(); - ret = PyObject_CallMethod(self, "abort", ""); - CB_CHECK_PYRETVAL(ret); - Py_DECREF(ret); - PyGILState_Release(state); - return NULL; + PyObject *self = (PyObject *)baton, *ret; + PyGILState_STATE state = PyGILState_Ensure(); + ret = PyObject_CallMethod(self, "abort", ""); + CB_CHECK_PYRETVAL(ret); + Py_DECREF(ret); + PyGILState_Release(state); + return NULL; } #if ONLY_SINCE_SVN(1, 5) const svn_ra_reporter3_t py_ra_reporter3 = { - py_ra_report3_set_path, - py_ra_report_delete_path, - py_ra_report3_link_path, - py_ra_report_finish, - py_ra_report_abort, + py_ra_report3_set_path, + py_ra_report_delete_path, + py_ra_report3_link_path, + py_ra_report_finish, + py_ra_report_abort, }; #endif const svn_ra_reporter2_t py_ra_reporter2 = { - py_ra_report2_set_path, - py_ra_report_delete_path, - py_ra_report2_link_path, - py_ra_report_finish, - py_ra_report_abort, + py_ra_report2_set_path, + py_ra_report_delete_path, + py_ra_report2_link_path, + py_ra_report_finish, + py_ra_report_abort, }; -- cgit v1.2.3 From 8abdb160919f1bed5b0e19504a28adfad4be7221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Wed, 9 Aug 2017 01:17:59 +0000 Subject: Fix style error. --- subvertpy/tests/test_wc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 8372f570..468151ff 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -381,7 +381,7 @@ class ContextTests(SubversionTestCase): self.assertEqual(ret, [('', 0, 0, None, 3)]) def test_get_update_editor(self): - repos_url = self.make_client("repos", "checkout") + self.make_client("repos", "checkout") context = wc.Context() editor = context.get_update_editor("checkout", "") editor.close() -- cgit v1.2.3 From f918eed20d319a3c7c7cf32eb26d73da095b591c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Wed, 9 Aug 2017 01:35:27 +0000 Subject: Add subvertpy.wc.Context.status. --- subvertpy/tests/test_wc.py | 7 +++ subvertpy/wc.c | 135 ++++++++++++++++++++++++++++++++++++++++++++- subvertpy/wc.h | 2 +- subvertpy/wc_adm.c | 30 +++++----- 4 files changed, 157 insertions(+), 17 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 468151ff..eb676aee 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -20,6 +20,7 @@ import os import subvertpy from subvertpy import ( + NODE_DIR, NODE_FILE, wc, ) @@ -385,3 +386,9 @@ class ContextTests(SubversionTestCase): context = wc.Context() editor = context.get_update_editor("checkout", "") editor.close() + + def test_status(self): + self.make_client("repos", "checkout") + context = wc.Context() + status = context.status("checkout") + self.assertEqual(NODE_DIR, status.kind) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 7ff60ac4..4f82dce3 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -1337,6 +1337,132 @@ static PyObject *py_wc_context_ensure_adm(PyObject *self, PyObject *args, Py_RETURN_NONE; } +typedef struct { + PyObject_VAR_HEAD + apr_pool_t *pool; + svn_wc_status3_t status; +} Status3Object; + +static void status_dealloc(PyObject *self) +{ + apr_pool_t *pool = ((Status3Object *)self)->pool; + if (pool != NULL) + apr_pool_destroy(pool); + PyObject_Del(self); +} + +static PyMemberDef status_members[] = { + { "kind", T_INT, offsetof(Status3Object, status.kind), READONLY, + "The kind of node as recorded in the working copy." }, + { "depth", T_INT, offsetof(Status3Object, status.depth), READONLY, + "The depth of the node as recorded in the working copy." }, + { "filesize", T_LONG, offsetof(Status3Object, status.filesize), READONLY, + "The actual size of the working file on disk, or SVN_INVALID_FILESIZE" + "if unknown (or if the item isn't a file at all)" }, + { "versioned", T_BOOL, offsetof(Status3Object, status.versioned), READONLY, + "If the path is under version control, versioned is TRUE, " + "otherwise FALSE." }, + /* TODO */ + { NULL } +}; + +static PyTypeObject Status3_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "wc.Status", /* const char *tp_name; For printing, in format "." */ + sizeof(Status3Object), + 0,/* Py_ssize_t tp_basicsize, tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + status_dealloc, /* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + NULL, /* cmpfunc tp_compare; */ + NULL, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /* Flags to define presence of optional/expanded features */ + 0, /* long tp_flags; */ + + NULL, /* const char *tp_doc; Documentation string */ + + /* Assigned meaning in release 2.0 */ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /* Assigned meaning in release 2.1 */ + /* rich comparisons */ + NULL, /* richcmpfunc tp_richcompare; */ + + /* weak reference enabler */ + 0, /* Py_ssize_t tp_weaklistoffset; */ + + /* Added in release 2.2 */ + /* Iterators */ + NULL, /* getiterfunc tp_iter; */ + NULL, /* iternextfunc tp_iternext; */ + + /* Attribute descriptor and subclassing stuff */ + NULL, /* struct PyMethodDef *tp_methods; */ + status_members, /* struct PyMemberDef *tp_members; */ + NULL, /* struct PyGetSetDef *tp_getsetters; */ +}; + +static PyObject *py_wc_status(PyObject *self, PyObject *args, PyObject *kwargs) +{ + ContextObject *context_obj = (ContextObject *)self; + char *kwnames[] = {"path", NULL}; + PyObject *py_path; + Status3Object *ret; + const char *path; + apr_pool_t *scratch_pool, *result_pool; + svn_wc_status3_t* status; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwnames, &py_path)) { + return NULL; + } + + result_pool = Pool(NULL); + scratch_pool = Pool(result_pool); + + path = py_object_to_svn_abspath(py_path, scratch_pool); + + RUN_SVN_WITH_POOL(result_pool, + svn_wc_status3(&status, context_obj->context, path, + result_pool, scratch_pool)); + + apr_pool_destroy(scratch_pool); + + ret = PyObject_New(Status3Object, &Status3_Type); + if (ret == NULL) { + apr_pool_destroy(result_pool); + return NULL; + } + ret->pool = result_pool; + ret->status = *status; + return (PyObject *)ret; +} + static PyMethodDef context_methods[] = { { "locked", py_wc_context_locked, METH_VARARGS, "locked(path) -> (locked_here, locked)\n" @@ -1372,6 +1498,10 @@ static PyMethodDef context_methods[] = { (PyCFunction)py_wc_context_ensure_adm, METH_VARARGS|METH_KEYWORDS, "ensure_adm(local_abspath, url, repos_root_url, repos_uuid, revnum, depth)" }, + { "status", + (PyCFunction)py_wc_status, + METH_VARARGS|METH_KEYWORDS, + "status(path) -> status" }, { NULL } }; @@ -1486,7 +1616,7 @@ moduleinit(void) if (PyType_Ready(&Entry_Type) < 0) return NULL; - if (PyType_Ready(&Status_Type) < 0) + if (PyType_Ready(&Status2_Type) < 0) return NULL; if (PyType_Ready(&Adm_Type) < 0) @@ -1515,6 +1645,9 @@ moduleinit(void) if (PyType_Ready(&CommittedQueue_Type) < 0) return NULL; + if (PyType_Ready(&Status3_Type) < 0) + return NULL; + apr_initialize(); #if PY_MAJOR_VERSION >= 3 diff --git a/subvertpy/wc.h b/subvertpy/wc.h index 388aab79..abf8d647 100644 --- a/subvertpy/wc.h +++ b/subvertpy/wc.h @@ -42,7 +42,7 @@ extern PyTypeObject CommittedQueue_Type; /* Provided by wc_adm.h */ extern PyTypeObject Adm_Type; extern PyTypeObject Entry_Type; -extern PyTypeObject Status_Type; +extern PyTypeObject Status2_Type; svn_wc_adm_access_t *PyObject_GetAdmAccess(PyObject *obj); #ifdef __GNUC__ diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index 02853329..b96b9ffc 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -1913,39 +1913,39 @@ typedef struct { apr_pool_t *pool; svn_wc_status2_t status; PyObject *entry; -} StatusObject; +} Status2Object; static void status_dealloc(PyObject *self) { - apr_pool_destroy(((StatusObject *)self)->pool); - Py_XDECREF(((StatusObject *)self)->entry); + apr_pool_destroy(((Status2Object *)self)->pool); + Py_XDECREF(((Status2Object *)self)->entry); PyObject_Del(self); } static PyMemberDef status_members[] = { - { "entry", T_OBJECT, offsetof(StatusObject, entry), READONLY, + { "entry", T_OBJECT, offsetof(Status2Object, entry), READONLY, "Can be NULL if not under version control." }, - { "locked", T_BOOL, offsetof(StatusObject, status.locked), READONLY, + { "locked", T_BOOL, offsetof(Status2Object, status.locked), READONLY, "a directory can be 'locked' if a working copy update was interrupted." }, - { "copied", T_BOOL, offsetof(StatusObject, status.copied), READONLY, + { "copied", T_BOOL, offsetof(Status2Object, status.copied), READONLY, "a file or directory can be 'copied' if it's scheduled for addition-with-history (or part of a subtree that is scheduled as such.)." }, - { "switched", T_BOOL, offsetof(StatusObject, status.switched), READONLY, + { "switched", T_BOOL, offsetof(Status2Object, status.switched), READONLY, "a file or directory can be 'switched' if the switch command has been used." }, - { "url", T_STRING, offsetof(StatusObject, status.url), READONLY, + { "url", T_STRING, offsetof(Status2Object, status.url), READONLY, "URL (actual or expected) in repository" }, - { "revision", T_LONG, offsetof(StatusObject, status.ood_last_cmt_rev), READONLY, + { "revision", T_LONG, offsetof(Status2Object, status.ood_last_cmt_rev), READONLY, "Set to the youngest committed revision, or SVN_INVALID_REVNUM if not out of date.", }, - { "kind", T_INT, offsetof(StatusObject, status.ood_kind), READONLY, + { "kind", T_INT, offsetof(Status2Object, status.ood_kind), READONLY, "Set to the node kind of the youngest commit, or svn_node_none if not out of date.", }, - { "status", T_INT, offsetof(StatusObject, status.text_status), READONLY, + { "status", T_INT, offsetof(Status2Object, status.text_status), READONLY, "The status of the entry.", }, { NULL, } }; -PyTypeObject Status_Type = { +PyTypeObject Status2_Type = { PyVarObject_HEAD_INIT(NULL, 0) "wc.Status", /* const char *tp_name; For printing, in format "." */ - sizeof(StatusObject), + sizeof(Status2Object), 0,/* Py_ssize_t tp_basicsize, tp_itemsize; For allocation */ /* Methods to implement standard operations */ @@ -2006,10 +2006,10 @@ PyTypeObject Status_Type = { PyObject *py_wc_status2(const svn_wc_status2_t *status) { - StatusObject *ret; + Status2Object *ret; svn_wc_status2_t *dup_status; - ret = PyObject_New(StatusObject, &Status_Type); + ret = PyObject_New(Status2Object, &Status2_Type); if (ret == NULL) return NULL; -- cgit v1.2.3 From 1c09e93dc2d05d84ac2ac78b0d7e7f85c2cd8977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Wed, 9 Aug 2017 19:49:48 +0000 Subject: Add a few more status fields. --- subvertpy/wc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 4f82dce3..2cbbaf85 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -1362,6 +1362,12 @@ static PyMemberDef status_members[] = { { "versioned", T_BOOL, offsetof(Status3Object, status.versioned), READONLY, "If the path is under version control, versioned is TRUE, " "otherwise FALSE." }, + { "repos_uuid", T_STRING, offsetof(Status3Object, status.repos_uuid), READONLY, + "UUID of repository" }, + { "repos_root_url", T_STRING, offsetof(Status3Object, status.repos_root_url), READONLY, + "Repository root URL" }, + { "repos_relpath", T_STRING, offsetof(Status3Object, status.repos_relpath), READONLY, + "Relative path in repository" }, /* TODO */ { NULL } }; @@ -1445,7 +1451,7 @@ static PyObject *py_wc_status(PyObject *self, PyObject *args, PyObject *kwargs) result_pool = Pool(NULL); scratch_pool = Pool(result_pool); - path = py_object_to_svn_abspath(py_path, scratch_pool); + path = py_object_to_svn_dirent(py_path, scratch_pool); RUN_SVN_WITH_POOL(result_pool, svn_wc_status3(&status, context_obj->context, path, -- cgit v1.2.3 From 32322f29b0bcfdeb1fe367dcc81a0bfea2396ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Thu, 10 Aug 2017 00:16:03 +0000 Subject: Add walk_entries. --- subvertpy/tests/test_wc.py | 13 ++++ subvertpy/wc.c | 168 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 144 insertions(+), 37 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index eb676aee..52efac96 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -392,3 +392,16 @@ class ContextTests(SubversionTestCase): context = wc.Context() status = context.status("checkout") self.assertEqual(NODE_DIR, status.kind) + + def test_walk_status(self): + self.make_client("repos", "checkout") + with open('checkout/bla.txt', 'w') as f: + f.write("modified") + self.client_add("checkout/bla.txt") + context = wc.Context() + result = {} + context.walk_status("checkout", result.__setitem__) + self.assertEqual( + set(result.keys()), + {os.path.abspath("checkout"), + os.path.abspath("checkout/bla.txt")}) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 2cbbaf85..d3519326 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -758,42 +758,45 @@ static PyObject *match_ignore_list(PyObject *self, PyObject *args) } static PyMethodDef wc_methods[] = { - { "check_wc", check_wc, METH_VARARGS, "check_wc(path) -> version\n" - "Check whether path contains a Subversion working copy\n" - "return the workdir version"}, - { "cleanup", (PyCFunction)cleanup_wc, METH_VARARGS|METH_KEYWORDS, "cleanup(path, diff3_cmd=None)\n" }, - { "ensure_adm", (PyCFunction)ensure_adm, METH_KEYWORDS|METH_VARARGS, - "ensure_adm(path, uuid, url, repos=None, rev=None)" }, - { "get_adm_dir", (PyCFunction)get_adm_dir, METH_NOARGS, - "get_adm_dir() -> name" }, - { "set_adm_dir", (PyCFunction)set_adm_dir, METH_VARARGS, - "set_adm_dir(name)" }, - { "get_pristine_copy_path", get_pristine_copy_path, METH_VARARGS, - "get_pristine_copy_path(path) -> path" }, - { "get_pristine_contents", get_pristine_contents, METH_VARARGS, - "get_pristine_contents(path) -> stream" }, - { "is_adm_dir", is_adm_dir, METH_VARARGS, - "is_adm_dir(name) -> bool" }, - { "is_normal_prop", is_normal_prop, METH_VARARGS, - "is_normal_prop(name) -> bool" }, - { "is_entry_prop", is_entry_prop, METH_VARARGS, - "is_entry_prop(name) -> bool" }, - { "is_wc_prop", is_wc_prop, METH_VARARGS, - "is_wc_prop(name) -> bool" }, - { "revision_status", (PyCFunction)revision_status, METH_KEYWORDS|METH_VARARGS, "revision_status(wc_path, trail_url=None, committed=False) -> (min_rev, max_rev, switched, modified)" }, - { "version", (PyCFunction)version, METH_NOARGS, - "version() -> (major, minor, patch, tag)\n\n" - "Version of libsvn_wc currently used." - }, - { "api_version", (PyCFunction)api_version, METH_NOARGS, - "api_version() -> (major, minor, patch, tag)\n\n" - "Version of libsvn_wc Subvertpy was compiled against." - }, - { "match_ignore_list", (PyCFunction)match_ignore_list, METH_VARARGS, - "match_ignore_list(str, patterns) -> bool" }, - { "get_actual_target", (PyCFunction)get_actual_target, METH_VARARGS, - "get_actual_target(path) -> (anchor, target)" }, - { NULL, } + { "check_wc", check_wc, METH_VARARGS, "check_wc(path) -> version\n" + "Check whether path contains a Subversion working copy\n" + "return the workdir version"}, + { "cleanup", (PyCFunction)cleanup_wc, + METH_VARARGS|METH_KEYWORDS, "cleanup(path, diff3_cmd=None)\n" }, + { "ensure_adm", (PyCFunction)ensure_adm, METH_KEYWORDS|METH_VARARGS, + "ensure_adm(path, uuid, url, repos=None, rev=None)" }, + { "get_adm_dir", (PyCFunction)get_adm_dir, METH_NOARGS, + "get_adm_dir() -> name" }, + { "set_adm_dir", (PyCFunction)set_adm_dir, METH_VARARGS, + "set_adm_dir(name)" }, + { "get_pristine_copy_path", get_pristine_copy_path, METH_VARARGS, + "get_pristine_copy_path(path) -> path" }, + { "get_pristine_contents", get_pristine_contents, METH_VARARGS, + "get_pristine_contents(path) -> stream" }, + { "is_adm_dir", is_adm_dir, METH_VARARGS, + "is_adm_dir(name) -> bool" }, + { "is_normal_prop", is_normal_prop, METH_VARARGS, + "is_normal_prop(name) -> bool" }, + { "is_entry_prop", is_entry_prop, METH_VARARGS, + "is_entry_prop(name) -> bool" }, + { "is_wc_prop", is_wc_prop, METH_VARARGS, + "is_wc_prop(name) -> bool" }, + { "revision_status", (PyCFunction)revision_status, + METH_KEYWORDS|METH_VARARGS, + "revision_status(wc_path, trail_url=None, committed=False)" + "-> (min_rev, max_rev, switched, modified)" }, + { "version", (PyCFunction)version, METH_NOARGS, + "version() -> (major, minor, patch, tag)\n\n" + "Version of libsvn_wc currently used." + }, + { "api_version", (PyCFunction)api_version, METH_NOARGS, + "api_version() -> (major, minor, patch, tag)\n\n" + "Version of libsvn_wc Subvertpy was compiled against." }, + { "match_ignore_list", (PyCFunction)match_ignore_list, METH_VARARGS, + "match_ignore_list(str, patterns) -> bool" }, + { "get_actual_target", (PyCFunction)get_actual_target, METH_VARARGS, + "get_actual_target(path) -> (anchor, target)" }, + { NULL, } }; static void committed_queue_dealloc(PyObject *self) @@ -1451,7 +1454,7 @@ static PyObject *py_wc_status(PyObject *self, PyObject *args, PyObject *kwargs) result_pool = Pool(NULL); scratch_pool = Pool(result_pool); - path = py_object_to_svn_dirent(py_path, scratch_pool); + path = py_object_to_svn_abspath(py_path, scratch_pool); RUN_SVN_WITH_POOL(result_pool, svn_wc_status3(&status, context_obj->context, path, @@ -1469,6 +1472,92 @@ static PyObject *py_wc_status(PyObject *self, PyObject *args, PyObject *kwargs) return (PyObject *)ret; } +static svn_error_t *py_status_receiver(void *baton, const char *local_abspath, + const svn_wc_status3_t *status, + apr_pool_t *scratch_pool) +{ + Status3Object *py_status; + PyObject *ret; + PyGILState_STATE state; + + if (baton == Py_None) + return NULL; + + state = PyGILState_Ensure(); + + py_status = PyObject_New(Status3Object, &Status3_Type); + if (py_status == NULL) { + PyGILState_Release(state); + return py_svn_error(); + } + py_status->pool = Pool(NULL); + py_status->status = *svn_wc_dup_status3(status, py_status->pool); + + ret = PyObject_CallFunction((PyObject *)baton, "sO", local_abspath, py_status); + Py_DECREF(py_status); + + if (ret == NULL) { + PyGILState_Release(state); + return py_svn_error(); + } + + Py_DECREF(ret); + PyGILState_Release(state); + + return NULL; +} + +static PyObject *py_wc_walk_status(PyObject *self, PyObject *args, PyObject *kwargs) +{ + ContextObject *context_obj = (ContextObject *)self; + char *kwnames[] = {"path", "receiver", "depth", "get_all", "no_ignore", + "ignore_text_mode", "ignore_patterns", NULL}; + PyObject *py_path; + const char *path; + int depth = svn_depth_infinity; + bool get_all = true; + bool no_ignore = false; + bool ignore_text_mode = false; + PyObject *py_ignore_patterns = Py_None; + PyObject *status_func; + apr_array_header_t *ignore_patterns; + apr_pool_t *pool; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|ibbbOO", kwnames, + &py_path, &status_func, &depth, &get_all, &no_ignore, + &ignore_text_mode, &py_ignore_patterns)) { + return NULL; + } + + pool = Pool(NULL); + + path = py_object_to_svn_abspath(py_path, pool); + if (path == NULL) { + apr_pool_destroy(pool); + return NULL; + } + + if (py_ignore_patterns == Py_None) { + ignore_patterns = NULL; + } else { + if (!string_list_to_apr_array(pool, py_ignore_patterns, &ignore_patterns)) { + apr_pool_destroy(pool); + return NULL; + } + } + + RUN_SVN_WITH_POOL(pool, + svn_wc_walk_status(context_obj->context, path, depth, + get_all, no_ignore, ignore_text_mode, + ignore_patterns, py_status_receiver, + status_func, py_cancel_check, NULL, + pool)); + + apr_pool_destroy(pool); + + Py_RETURN_NONE; +} + static PyMethodDef context_methods[] = { { "locked", py_wc_context_locked, METH_VARARGS, "locked(path) -> (locked_here, locked)\n" @@ -1508,6 +1597,11 @@ static PyMethodDef context_methods[] = { (PyCFunction)py_wc_status, METH_VARARGS|METH_KEYWORDS, "status(path) -> status" }, + { "walk_status", + (PyCFunction)py_wc_walk_status, + METH_VARARGS|METH_KEYWORDS, + "walk_status(path, receiver, depth=DEPTH_INFINITY, get_all=True, " + "no_ignore=False, ignore_text_mode=False, ignore_patterns=None)\n" }, { NULL } }; -- cgit v1.2.3 From a01be5d8b848478801aacdeb9691a2ea4a524af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Thu, 10 Aug 2017 00:19:32 +0000 Subject: Canonicalize absolute dirents, too. --- subvertpy/util.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/subvertpy/util.c b/subvertpy/util.c index 208e58ff..e01f4522 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -122,16 +122,14 @@ const char *py_object_to_svn_abspath(PyObject *obj, apr_pool_t *pool) return NULL; } if (svn_dirent_is_absolute(ret)) { - return ret; + return svn_dirent_canonicalize(ret, pool); } else { const char *absolute; RUN_SVN_WITH_POOL(pool, svn_dirent_get_absolute(&absolute, ret, pool)); - return absolute; + return svn_dirent_canonicalize(absolute, pool); } } - - const char *py_object_to_svn_dirent(PyObject *obj, apr_pool_t *pool) { const char *ret; -- cgit v1.2.3 From 613039535094412660e90816928fa3fa5e51535e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Thu, 10 Aug 2017 00:34:18 +0000 Subject: Acquire the GIL before running python code. --- subvertpy/wc.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index d3519326..17e87c59 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -290,11 +290,13 @@ void py_wc_notify_func(void *baton, const svn_wc_notify_t *notify, apr_pool_t *p return; if (notify->err != NULL) { + PyGILState_STATE state = PyGILState_Ensure(); PyObject *excval = PyErr_NewSubversionException(notify->err); ret = PyObject_CallFunction(func, "O", excval); Py_DECREF(excval); Py_XDECREF(ret); /* If ret was NULL, the cancel func should abort the operation. */ + PyGILState_Release(state); } } bool py_dict_to_wcprop_changes(PyObject *dict, apr_pool_t *pool, apr_array_header_t **ret) @@ -339,18 +341,21 @@ bool py_dict_to_wcprop_changes(PyObject *dict, apr_pool_t *pool, apr_array_heade svn_error_t *wc_validator3(void *baton, const char *uuid, const char *url, const char *root_url, apr_pool_t *pool) { PyObject *py_validator = baton, *ret; + PyGILState_STATE state; if (py_validator == Py_None) { return NULL; } - + state = PyGILState_Ensure(); ret = PyObject_CallFunction(py_validator, "sss", uuid, url, root_url); if (ret == NULL) { + PyGILState_Release(state); return py_svn_error(); } Py_DECREF(ret); + PyGILState_Release(state); return NULL; } @@ -359,17 +364,21 @@ svn_error_t *wc_validator3(void *baton, const char *uuid, const char *url, const svn_error_t *wc_validator2(void *baton, const char *uuid, const char *url, svn_boolean_t root, apr_pool_t *pool) { PyObject *py_validator = baton, *ret; + PyGILState_STATE state; if (py_validator == Py_None) { return NULL; } + state = PyGILState_Ensure(); ret = PyObject_CallFunction(py_validator, "ssO", uuid, url, Py_None); if (ret == NULL) { + PyGILState_Release(state); return py_svn_error(); } Py_DECREF(ret); + PyGILState_Release(state); return NULL; } -- cgit v1.2.3 From da94b3bbf3f2255109822ad2dc4b74cd34636500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Thu, 10 Aug 2017 01:28:10 +0000 Subject: Make last argument to ensure_adm optional. --- subvertpy/wc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 17e87c59..56f8420f 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -636,13 +636,13 @@ static PyObject *get_pristine_contents(PyObject *self, PyObject *args) static PyObject *ensure_adm(PyObject *self, PyObject *args, PyObject *kwargs) { const char *path; - char *uuid, *url; + char *uuid, *url = NULL; PyObject *py_path; - char *repos=NULL; - svn_revnum_t rev=-1; + char *repos = NULL; + long rev = -1; apr_pool_t *pool; char *kwnames[] = { "path", "uuid", "url", "repos", "rev", "depth", NULL }; - svn_depth_t depth = svn_depth_infinity; + int depth = svn_depth_infinity; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oss|sli", kwnames, &py_path, &uuid, &url, &repos, &rev, &depth)) @@ -1328,10 +1328,10 @@ static PyObject *py_wc_context_ensure_adm(PyObject *self, PyObject *args, char *repos_root_url; char *repos_uuid; int revnum; - int depth; + int depth = svn_depth_infinity; apr_pool_t *pool; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ssssii", kwnames, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ssssi|i", kwnames, &local_abspath, &url, &repos_root_url, &repos_uuid, &revnum, &depth)) { return NULL; -- cgit v1.2.3 From 7f4d5098de9ed961cf89ceef5f9022fc2e60b010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Thu, 10 Aug 2017 11:07:41 +0000 Subject: Add basic support for locking. --- subvertpy/tests/test_wc.py | 9 ++++++ subvertpy/util.c | 8 ++++++ subvertpy/util.h | 1 + subvertpy/wc.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 52efac96..0fea3c0a 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -405,3 +405,12 @@ class ContextTests(SubversionTestCase): set(result.keys()), {os.path.abspath("checkout"), os.path.abspath("checkout/bla.txt")}) + + def test_locking(self): + self.make_client("repos", "checkout") + with open('checkout/bla.txt', 'w') as f: + f.write("modified") + self.client_add("checkout/bla.txt") + context = wc.Context() + context.add_lock("checkout/bla.txt", ()) + context.remove_lock("checkout/bla.txt") diff --git a/subvertpy/util.c b/subvertpy/util.c index e01f4522..9d6fd3ec 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -1174,3 +1174,11 @@ PyObject *dirent_hash_to_dict(apr_hash_t *dirents, unsigned int dirent_fields, a } return py_dirents; } + +svn_lock_t *py_object_to_svn_lock(PyObject *py_lock, apr_pool_t *pool) +{ + svn_lock_t *ret = svn_lock_create(pool); + + /* TODO */ + return ret; +} diff --git a/subvertpy/util.h b/subvertpy/util.h index 3e62e4c2..755256ba 100644 --- a/subvertpy/util.h +++ b/subvertpy/util.h @@ -158,6 +158,7 @@ const char *py_object_to_svn_relpath(PyObject *obj, apr_pool_t *pool); const char *py_object_to_svn_path_or_url(PyObject *obj, apr_pool_t *pool); char *py_object_to_svn_string(PyObject *obj, apr_pool_t *pool); const char *py_object_to_svn_abspath(PyObject *obj, apr_pool_t *pool); +svn_lock_t *py_object_to_svn_lock(PyObject *py_lock, apr_pool_t *pool); #define py_object_from_svn_abspath PyUnicode_FromString #if PY_MAJOR_VERSION >= 3 diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 56f8420f..af3aaee9 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -1567,6 +1567,70 @@ static PyObject *py_wc_walk_status(PyObject *self, PyObject *args, PyObject *kwa Py_RETURN_NONE; } +static PyObject *py_wc_add_lock(PyObject *self, PyObject *args, PyObject *kwargs) +{ + ContextObject *context_obj = (ContextObject *)self; + PyObject *py_path, *py_lock; + svn_lock_t *lock; + char *kwnames[] = { "path", "lock", NULL }; + const char *path; + apr_pool_t *scratch_pool; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO", kwnames, &py_path, &py_lock)) { + return NULL; + } + + scratch_pool = Pool(NULL); + + path = py_object_to_svn_abspath(py_path, scratch_pool); + if (path == NULL) { + apr_pool_destroy(scratch_pool); + return NULL; + } + + lock = py_object_to_svn_lock(py_lock, scratch_pool); + if (lock == NULL) { + apr_pool_destroy(scratch_pool); + return NULL; + } + + RUN_SVN_WITH_POOL(scratch_pool, + svn_wc_add_lock2(context_obj->context, path, lock, scratch_pool)); + + apr_pool_destroy(scratch_pool); + + Py_RETURN_NONE; +} + +static PyObject *py_wc_remove_lock(PyObject *self, PyObject *args, PyObject *kwargs) +{ + ContextObject *context_obj = (ContextObject *)self; + char *kwnames[] = { "path", NULL }; + PyObject *py_path; + const char *path; + apr_pool_t *scratch_pool; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwnames, &py_path)) { + return NULL; + } + + scratch_pool = Pool(NULL); + + path = py_object_to_svn_abspath(py_path, scratch_pool); + if (path == NULL) { + apr_pool_destroy(scratch_pool); + return NULL; + } + + RUN_SVN_WITH_POOL(scratch_pool, + svn_wc_remove_lock2(context_obj->context, path, + scratch_pool)); + + apr_pool_destroy(scratch_pool); + + Py_RETURN_NONE; +} + static PyMethodDef context_methods[] = { { "locked", py_wc_context_locked, METH_VARARGS, "locked(path) -> (locked_here, locked)\n" @@ -1611,6 +1675,14 @@ static PyMethodDef context_methods[] = { METH_VARARGS|METH_KEYWORDS, "walk_status(path, receiver, depth=DEPTH_INFINITY, get_all=True, " "no_ignore=False, ignore_text_mode=False, ignore_patterns=None)\n" }, + { "add_lock", + (PyCFunction)py_wc_add_lock, + METH_VARARGS|METH_KEYWORDS, + "add_lock(path, lock)" }, + { "remove_lock", + (PyCFunction)py_wc_remove_lock, + METH_VARARGS|METH_KEYWORDS, + "remove_lock(path)" }, { NULL } }; -- cgit v1.2.3 From 7382f10037605d25d12c85b72a46421b4fb7ce38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Thu, 10 Aug 2017 11:09:27 +0000 Subject: Add add_from_disk. --- subvertpy/tests/test_wc.py | 7 +++++++ subvertpy/wc.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 0fea3c0a..25a92fd3 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -414,3 +414,10 @@ class ContextTests(SubversionTestCase): context = wc.Context() context.add_lock("checkout/bla.txt", ()) context.remove_lock("checkout/bla.txt") + + def test_add_from_disk(self): + self.make_client("repos", "checkout") + with open('checkout/bla.txt', 'w') as f: + f.write("modified") + context = wc.Context() + context.add_from_disk('checkout/bla.txt') diff --git a/subvertpy/wc.c b/subvertpy/wc.c index af3aaee9..c322e0de 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -1627,6 +1627,51 @@ static PyObject *py_wc_remove_lock(PyObject *self, PyObject *args, PyObject *kwa scratch_pool)); apr_pool_destroy(scratch_pool); + Py_RETURN_NONE; +} + +static PyObject *py_wc_add_from_disk(PyObject *self, PyObject *args, PyObject *kwargs) +{ + ContextObject *context_obj = (ContextObject *)self; + char *kwnames[] = {"path", "props", "skip_checks", "notify", NULL }; + PyObject *py_path; + const char *path; + bool skip_checks = false; + PyObject *py_props = Py_None; + PyObject *notify_func = Py_None; + apr_pool_t *pool; + apr_hash_t *props; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|ObO", kwnames, + &py_path, &py_props, &skip_checks, ¬ify_func)) { + return NULL; + } + + pool = Pool(NULL); + + path = py_object_to_svn_abspath(py_path, pool); + if (path == NULL) { + apr_pool_destroy(pool); + return NULL; + } + + if (py_props == Py_None) { + props = NULL; + } else { + props = prop_dict_to_hash(pool, py_props); + if (props == NULL) { + apr_pool_destroy(pool); + return NULL; + } + } + + RUN_SVN_WITH_POOL( + pool, svn_wc_add_from_disk3( + context_obj->context, path, props, skip_checks, + notify_func == Py_None?NULL:py_wc_notify_func, + notify_func, pool)); + + apr_pool_destroy(pool); Py_RETURN_NONE; } @@ -1683,6 +1728,10 @@ static PyMethodDef context_methods[] = { (PyCFunction)py_wc_remove_lock, METH_VARARGS|METH_KEYWORDS, "remove_lock(path)" }, + { "add_from_disk", + (PyCFunction)py_wc_add_from_disk, + METH_VARARGS|METH_KEYWORDS, + "add_from_disk(local_abspath, props=None, skip_checks=False, notify=None)" }, { NULL } }; -- cgit v1.2.3 From 52c496283c2437886b9098c30144ca2fbb9c3006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Thu, 10 Aug 2017 22:46:45 +0000 Subject: Add subvertpy.wc.Contect.get_prop_diffs. --- subvertpy/tests/test_wc.py | 7 +++++++ subvertpy/util.c | 30 ++++++++++++++++++++++++++++ subvertpy/util.h | 1 + subvertpy/wc.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++ subvertpy/wc_adm.c | 23 ++-------------------- 5 files changed, 89 insertions(+), 21 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 25a92fd3..ca46fec2 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -421,3 +421,10 @@ class ContextTests(SubversionTestCase): f.write("modified") context = wc.Context() context.add_from_disk('checkout/bla.txt') + + def test_get_prop_diffs(self): + self.make_client("repos", "checkout") + context = wc.Context() + (orig_props, propdelta) = context.get_prop_diffs("checkout") + self.assertEqual({}, orig_props) + self.assertEqual([], propdelta) diff --git a/subvertpy/util.c b/subvertpy/util.c index 9d6fd3ec..d7dadd3f 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -1182,3 +1182,33 @@ svn_lock_t *py_object_to_svn_lock(PyObject *py_lock, apr_pool_t *pool) /* TODO */ return ret; } + +PyObject *propchanges_to_list(const apr_array_header_t *propchanges) +{ + int i; + svn_prop_t el; + PyObject *py_propchanges = PyList_New(propchanges->nelts); + PyObject *pyval; + if (py_propchanges == NULL) { + return NULL; + } + for (i = 0; i < propchanges->nelts; i++) { + el = APR_ARRAY_IDX(propchanges, i, svn_prop_t); + if (el.value != NULL) + pyval = Py_BuildValue("(sz#)", el.name, el.value->data, el.value->len); + else + pyval = Py_BuildValue("(sO)", el.name, Py_None); + if (pyval == NULL) { + Py_DECREF(py_propchanges); + return NULL; + } + if (PyList_SetItem(py_propchanges, i, pyval) != 0) { + Py_DECREF(py_propchanges); + return NULL; + } + } + + return py_propchanges; +} + + diff --git a/subvertpy/util.h b/subvertpy/util.h index 755256ba..60a61700 100644 --- a/subvertpy/util.h +++ b/subvertpy/util.h @@ -160,6 +160,7 @@ char *py_object_to_svn_string(PyObject *obj, apr_pool_t *pool); const char *py_object_to_svn_abspath(PyObject *obj, apr_pool_t *pool); svn_lock_t *py_object_to_svn_lock(PyObject *py_lock, apr_pool_t *pool); #define py_object_from_svn_abspath PyUnicode_FromString +PyObject *propchanges_to_list(const apr_array_header_t *propchanges); #if PY_MAJOR_VERSION >= 3 #define PyRepr_FromFormat PyUnicode_FromFormat diff --git a/subvertpy/wc.c b/subvertpy/wc.c index c322e0de..cb8800ba 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -1676,6 +1676,51 @@ static PyObject *py_wc_add_from_disk(PyObject *self, PyObject *args, PyObject *k Py_RETURN_NONE; } +static PyObject *py_wc_get_prop_diffs(PyObject *self, PyObject *args, PyObject *kwargs) +{ + ContextObject *context_obj = (ContextObject *)self; + PyObject *py_path, *py_orig_props, *py_propchanges; + apr_pool_t *pool; + char *kwnames[] = {"path", NULL}; + apr_hash_t *original_props; + apr_array_header_t *propchanges; + const char *path; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwnames, &py_path)) { + return NULL; + } + + pool = Pool(NULL); + + path = py_object_to_svn_abspath(py_path, pool); + if (path == NULL) { + apr_pool_destroy(pool); + return NULL; + } + + RUN_SVN_WITH_POOL(pool, svn_wc_get_prop_diffs2(&propchanges, + &original_props, + context_obj->context, + path, pool, pool)); + + py_orig_props = prop_hash_to_dict(original_props); + if (py_orig_props == NULL) { + apr_pool_destroy(pool); + return NULL; + } + + py_propchanges = propchanges_to_list(propchanges); + if (py_propchanges == NULL) { + apr_pool_destroy(pool); + Py_DECREF(py_propchanges); + return NULL; + } + + apr_pool_destroy(pool); + + return Py_BuildValue("NN", py_orig_props, py_propchanges); +} + static PyMethodDef context_methods[] = { { "locked", py_wc_context_locked, METH_VARARGS, "locked(path) -> (locked_here, locked)\n" @@ -1732,6 +1777,10 @@ static PyMethodDef context_methods[] = { (PyCFunction)py_wc_add_from_disk, METH_VARARGS|METH_KEYWORDS, "add_from_disk(local_abspath, props=None, skip_checks=False, notify=None)" }, + { "get_prop_diffs", + (PyCFunction)py_wc_get_prop_diffs, + METH_VARARGS|METH_KEYWORDS, + "get_prop_diffs(path) -> (changes orig_props)" }, { NULL } }; diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index b96b9ffc..8ec8aadf 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -395,9 +395,7 @@ static PyObject *adm_get_prop_diffs(PyObject *self, PyObject *args) apr_hash_t *original_props; PyObject *py_path; AdmObject *admobj = (AdmObject *)self; - svn_prop_t el; - int i; - PyObject *py_propchanges, *py_orig_props, *pyval; + PyObject *py_propchanges, *py_orig_props; if (!PyArg_ParseTuple(args, "O", &py_path)) return NULL; @@ -416,28 +414,11 @@ static PyObject *adm_get_prop_diffs(PyObject *self, PyObject *args) } RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_prop_diffs(&propchanges, &original_props, path, admobj->adm, temp_pool)); - py_propchanges = PyList_New(propchanges->nelts); + py_propchanges = propchanges_to_list(propchanges); if (py_propchanges == NULL) { apr_pool_destroy(temp_pool); return NULL; } - for (i = 0; i < propchanges->nelts; i++) { - el = APR_ARRAY_IDX(propchanges, i, svn_prop_t); - if (el.value != NULL) - pyval = Py_BuildValue("(sz#)", el.name, el.value->data, el.value->len); - else - pyval = Py_BuildValue("(sO)", el.name, Py_None); - if (pyval == NULL) { - apr_pool_destroy(temp_pool); - Py_DECREF(py_propchanges); - return NULL; - } - if (PyList_SetItem(py_propchanges, i, pyval) != 0) { - Py_DECREF(py_propchanges); - apr_pool_destroy(temp_pool); - return NULL; - } - } py_orig_props = prop_hash_to_dict(original_props); apr_pool_destroy(temp_pool); if (py_orig_props == NULL) { -- cgit v1.2.3 From 7d1b7350b7ae1875af15b460c5e35419c11a16e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 11 Aug 2017 01:34:21 +0000 Subject: Add get_switch_editor. --- subvertpy/wc_adm.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 109 insertions(+), 9 deletions(-) diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index 8ec8aadf..02c70d86 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -568,7 +568,9 @@ static PyObject *adm_crawl_revisions(PyObject *self, PyObject *args, PyObject *k svn_wc_traversal_info_t *traversal_info; bool depth_compatibility_trick = false; bool honor_depth_exclude = false; - char *kwnames[] = { "path", "reporter", "restore_files", "recurse", "use_commit_times", "notify_func", "depth_compatibility_trick", "honor_depth_exclude,", NULL }; + char *kwnames[] = { "path", "reporter", "restore_files", "recurse", + "use_commit_times", "notify_func", "depth_compatibility_trick", + "honor_depth_exclude,", NULL }; PyObject *py_path; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bbbObb", kwnames, &py_path, @@ -623,10 +625,10 @@ static void wc_done_handler(void *self) Py_DECREF(admobj); } -static PyObject *adm_get_update_editor(PyObject *self, PyObject *args) +static PyObject *adm_get_switch_editor(PyObject *self, PyObject *args, PyObject *kwargs) { char *target; - bool use_commit_times=true, recurse=true; + bool use_commit_times=true; PyObject * notify_func=Py_None; char *diff3_cmd=NULL; const svn_delta_editor_t *editor; @@ -637,10 +639,18 @@ static PyObject *adm_get_update_editor(PyObject *self, PyObject *args) svn_error_t *err; bool allow_unver_obstructions = false; bool depth_is_sticky = false; + int depth = svn_depth_infinity; + char *switch_url; + PyObject *py_target; + char *kwnames[] = { + "target", "switch_url", "use_commit_times", "depth", "notify_func", + "diff3_cmd", "depth_is_sticky", "allow_unver_obstructions", NULL }; - if (!PyArg_ParseTuple(args, "s|bbOzbb", &target, &use_commit_times, - &recurse, ¬ify_func, &diff3_cmd, &depth_is_sticky, - &allow_unver_obstructions)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|biOzbb", kwnames, + &py_target, &switch_url, &use_commit_times, + &depth, ¬ify_func, &diff3_cmd, + &depth_is_sticky, + &allow_unver_obstructions)) return NULL; ADM_CHECK_CLOSED(admobj); @@ -648,14 +658,101 @@ static PyObject *adm_get_update_editor(PyObject *self, PyObject *args) pool = Pool(NULL); if (pool == NULL) return NULL; + + target = py_object_to_svn_string(py_target, pool); + if (target == NULL) { + apr_pool_destroy(pool); + return NULL; + } + latest_revnum = (svn_revnum_t *)apr_palloc(pool, sizeof(svn_revnum_t)); + Py_BEGIN_ALLOW_THREADS +#if ONLY_SINCE_SVN(1, 5) + /* FIXME: Support fetch_func */ + /* FIXME: Support conflict func */ + err = svn_wc_get_switch_editor3(latest_revnum, admobj->adm, target, switch_url, + use_commit_times, depth, + depth_is_sticky?TRUE:FALSE, allow_unver_obstructions?TRUE:FALSE, + py_wc_notify_func, (void *)notify_func, + py_cancel_check, NULL, + NULL, NULL, diff3_cmd, NULL, &editor, + &edit_baton, NULL, pool); +#else + if (allow_unver_obstructions) { + PyErr_SetString(PyExc_NotImplementedError, + "allow_unver_obstructions is not supported in svn < 1.5"); + apr_pool_destroy(pool); + PyEval_RestoreThread(_save); + return NULL; + } + if (depth_is_sticky) { + PyErr_SetString(PyExc_NotImplementedError, + "depth_is_sticky is not supported in svn < 1.5"); + apr_pool_destroy(pool); + PyEval_RestoreThread(_save); + return NULL; + } + err = svn_wc_get_switch_editor2(latest_revnum, admobj->adm, target, switch_url, + use_commit_times, recurse, py_wc_notify_func, (void *)notify_func, + py_cancel_check, NULL, diff3_cmd, &editor, &edit_baton, + NULL, pool); +#endif + Py_END_ALLOW_THREADS + if (err != NULL) { + handle_svn_error(err); + svn_error_clear(err); + apr_pool_destroy(pool); + return NULL; + } + Py_INCREF(admobj); + return new_editor_object(NULL, editor, edit_baton, pool, &Editor_Type, + wc_done_handler, admobj, NULL); +} + +static PyObject *adm_get_update_editor(PyObject *self, PyObject *args, PyObject *kwargs) +{ + char *target; + bool use_commit_times=true; + PyObject * notify_func=Py_None; + char *diff3_cmd=NULL; + const svn_delta_editor_t *editor; + AdmObject *admobj = (AdmObject *)self; + void *edit_baton; + apr_pool_t *pool; + svn_revnum_t *latest_revnum; + svn_error_t *err; + bool allow_unver_obstructions = false; + bool depth_is_sticky = false; + int depth = svn_depth_infinity; + PyObject *py_target; + char *kwnames[] = { + "target", "use_commit_times", "depth", "notify_func", + "diff3_cmd", "depth_is_sticky", "allow_unver_obstructions", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|biOzbb", kwnames, + &py_target, &use_commit_times, + &depth, ¬ify_func, &diff3_cmd, + &depth_is_sticky, + &allow_unver_obstructions)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + pool = Pool(NULL); + if (pool == NULL) + return NULL; + + target = py_object_to_svn_string(py_target, pool); + if (target == NULL) { + apr_pool_destroy(pool); + return NULL; + } latest_revnum = (svn_revnum_t *)apr_palloc(pool, sizeof(svn_revnum_t)); Py_BEGIN_ALLOW_THREADS #if ONLY_SINCE_SVN(1, 5) - /* FIXME: Support all values of depth */ /* FIXME: Support fetch_func */ /* FIXME: Support conflict func */ err = svn_wc_get_update_editor3(latest_revnum, admobj->adm, target, - use_commit_times, recurse?svn_depth_infinity:svn_depth_files, + use_commit_times, depth, depth_is_sticky?TRUE:FALSE, allow_unver_obstructions?TRUE:FALSE, py_wc_notify_func, (void *)notify_func, py_cancel_check, NULL, @@ -1660,7 +1757,10 @@ static PyMethodDef adm_methods[] = { { "delete", (PyCFunction)adm_delete, METH_VARARGS|METH_KEYWORDS, "S.delete(path, notify_func=None, keep_local=False)" }, { "crawl_revisions", (PyCFunction)adm_crawl_revisions, METH_VARARGS|METH_KEYWORDS, "S.crawl_revisions(path, reporter, restore_files=True, recurse=True, use_commit_times=True, notify_func=None) -> None" }, - { "get_update_editor", adm_get_update_editor, METH_VARARGS, NULL }, + { "get_update_editor", (PyCFunction)adm_get_update_editor, + METH_VARARGS|METH_KEYWORDS, NULL }, + { "get_switch_editor", (PyCFunction)adm_get_switch_editor, + METH_VARARGS|METH_KEYWORDS, NULL }, { "close", (PyCFunction)adm_close, METH_NOARGS, "S.close()" }, { "entry", (PyCFunction)adm_entry, METH_VARARGS, -- cgit v1.2.3 From ec5a1cf9570a7938748d90387e98e8998c4ead11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 11 Aug 2017 01:42:04 +0000 Subject: Fix support for copyfrom_url in add(). --- subvertpy/wc_adm.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index 02c70d86..023c23a0 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -431,16 +431,17 @@ static PyObject *adm_get_prop_diffs(PyObject *self, PyObject *args) static PyObject *adm_add(PyObject *self, PyObject *args, PyObject *kwargs) { const char *path; - char *copyfrom_url=NULL; + const char *copyfrom_url = NULL; svn_revnum_t copyfrom_rev=-1; char *kwnames[] = { "path", "copyfrom_url", "copyfrom_rev", "notify_func", "depth", NULL }; PyObject *notify_func=Py_None, *py_path; AdmObject *admobj = (AdmObject *)self; apr_pool_t *temp_pool; svn_depth_t depth = svn_depth_infinity; + PyObject *py_copyfrom_url = Py_None; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|zlOi", kwnames, &py_path, - ©from_url, ©from_rev, ¬ify_func, &depth)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OlOi", kwnames, &py_path, + &py_copyfrom_url, ©from_rev, ¬ify_func, &depth)) return NULL; ADM_CHECK_CLOSED(admobj); @@ -456,10 +457,20 @@ static PyObject *adm_add(PyObject *self, PyObject *args, PyObject *kwargs) return NULL; } + if (py_copyfrom_url != Py_None) { + copyfrom_url = py_object_to_svn_uri(py_copyfrom_url, temp_pool); + if (copyfrom_url == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + } else { + copyfrom_url = NULL; + } + #if ONLY_SINCE_SVN(1, 6) RUN_SVN_WITH_POOL(temp_pool, svn_wc_add3( path, admobj->adm, - depth, svn_uri_canonicalize(copyfrom_url, temp_pool), + depth, copyfrom_url, copyfrom_rev, py_cancel_check, NULL, py_wc_notify_func, (void *)notify_func, -- cgit v1.2.3 From 89e552fbbbda4ea3f5d3d270aef1ddd5646514a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 11 Aug 2017 01:54:41 +0000 Subject: Fix svn.client copy. --- subvertpy/client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subvertpy/client.c b/subvertpy/client.c index 2e9911dd..a19e407f 100644 --- a/subvertpy/client.c +++ b/subvertpy/client.c @@ -1259,7 +1259,7 @@ static PyObject *client_copy(PyObject *self, PyObject *args, PyObject *kwargs) apr_pool_destroy(temp_pool); return NULL; } - APR_ARRAY_IDX(src_paths, 0, svn_client_copy_source_t *) = &src; + APR_ARRAY_PUSH(src_paths, svn_client_copy_source_t *) = &src; #endif #if ONLY_SINCE_SVN(1, 9) RUN_SVN_WITH_POOL(temp_pool, svn_client_copy7(src_paths, -- cgit v1.2.3 From 5c904d135f9d0d7a7b55ca55b36621815bbd559c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 11 Aug 2017 02:20:37 +0000 Subject: Support svn < 1.9. --- subvertpy/wc.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index cb8800ba..59e0e023 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -1665,11 +1665,32 @@ static PyObject *py_wc_add_from_disk(PyObject *self, PyObject *args, PyObject *k } } +#if ONLY_SINCE_SVN(1, 9) RUN_SVN_WITH_POOL( pool, svn_wc_add_from_disk3( context_obj->context, path, props, skip_checks, notify_func == Py_None?NULL:py_wc_notify_func, notify_func, pool)); +#else + if (props != NULL) { + PyErr_SetString(PyExc_NotImplementedError, + "props argument only supported on svn >= 1.9"); + apr_pool_destroy(pool); + return NULL; + } + + if (skip_checks) { + PyErr_SetString(PyExc_NotImplementedError, + "skip_checks argument only supported on svn >= 1.9"); + apr_pool_destroy(pool); + return NULL; + } + RUN_SVN_WITH_POOL( + pool, svn_wc_add_from_disk( + context_obj->context, path, + notify_func == Py_None?NULL:py_wc_notify_func, + notify_func, pool)); +#endif apr_pool_destroy(pool); -- cgit v1.2.3 From fa1f674660cca09d12b28e45ae6cd64dee4fcc8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 11 Aug 2017 02:20:57 +0000 Subject: Pass things in the canonicalized form. --- subvertpy/_ra.c | 12 ++++++++++-- subvertpy/client.c | 11 ++++++++--- subvertpy/repos.c | 2 +- subvertpy/wc_adm.c | 13 ++++++++++--- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/subvertpy/_ra.c b/subvertpy/_ra.c index ae9f75e7..1aecc762 100644 --- a/subvertpy/_ra.c +++ b/subvertpy/_ra.c @@ -1044,10 +1044,11 @@ static PyObject *ra_do_switch(PyObject *self, PyObject *args) void *report_baton; apr_pool_t *temp_pool, *result_pool; ReporterObject *ret; + PyObject *py_switch_url; svn_error_t *err; - if (!PyArg_ParseTuple(args, "lsbsO|bb:do_switch", &revision_to_update_to, &update_target, - &recurse, &switch_url, &update_editor, &send_copyfrom_args, &ignore_ancestry)) + if (!PyArg_ParseTuple(args, "lsbOO|bb:do_switch", &revision_to_update_to, &update_target, + &recurse, &py_switch_url, &update_editor, &send_copyfrom_args, &ignore_ancestry)) return NULL; if (ra_check_busy(ra)) return NULL; @@ -1058,6 +1059,13 @@ static PyObject *ra_do_switch(PyObject *self, PyObject *args) return NULL; } + switch_url = py_object_to_svn_uri(py_switch_url, temp_pool); + if (switch_url == NULL) { + apr_pool_destroy(temp_pool); + ra->busy = false; + return NULL; + } + result_pool = Pool(NULL); if (result_pool == NULL) { apr_pool_destroy(temp_pool); diff --git a/subvertpy/client.c b/subvertpy/client.c index a19e407f..a86614f5 100644 --- a/subvertpy/client.c +++ b/subvertpy/client.c @@ -1016,7 +1016,7 @@ static PyObject *client_delete(PyObject *self, PyObject *args) #if ONLY_BEFORE_SVN(1, 7) svn_commit_info_t *commit_info = NULL; #endif - PyObject *py_revprops; + PyObject *py_revprops = Py_None; apr_array_header_t *apr_paths; ClientObject *client = (ClientObject *)self; apr_hash_t *hash_revprops; @@ -1366,9 +1366,9 @@ static PyObject *client_propget(PyObject *self, PyObject *args) PyObject *peg_revision = Py_None; PyObject *revision; ClientObject *client = (ClientObject *)self; - PyObject *ret; + PyObject *ret, *py_target; - if (!PyArg_ParseTuple(args, "ssO|Ob", &propname, &target, &peg_revision, + if (!PyArg_ParseTuple(args, "sOO|Ob", &propname, &py_target, &peg_revision, &revision, &recurse)) return NULL; if (!to_opt_revision(peg_revision, &c_peg_rev)) @@ -1378,6 +1378,11 @@ static PyObject *client_propget(PyObject *self, PyObject *args) temp_pool = Pool(NULL); if (temp_pool == NULL) return NULL; + target = py_object_to_svn_abspath(py_target, temp_pool); + if (target == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } #if ONLY_SINCE_SVN(1, 8) /* FIXME: Support changelists */ /* FIXME: Support actual_revnum */ diff --git a/subvertpy/repos.c b/subvertpy/repos.c index e6a41c15..44c48416 100644 --- a/subvertpy/repos.c +++ b/subvertpy/repos.c @@ -64,7 +64,7 @@ static PyObject *repos_create(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_string(py_path, pool); + path = py_object_to_svn_dirent(py_path, pool); if (path == NULL) { apr_pool_destroy(pool); return NULL; diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index 023c23a0..a5e5a5c0 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -652,13 +652,13 @@ static PyObject *adm_get_switch_editor(PyObject *self, PyObject *args, PyObject bool depth_is_sticky = false; int depth = svn_depth_infinity; char *switch_url; - PyObject *py_target; + PyObject *py_target, *py_switch_url; char *kwnames[] = { "target", "switch_url", "use_commit_times", "depth", "notify_func", "diff3_cmd", "depth_is_sticky", "allow_unver_obstructions", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|biOzbb", kwnames, - &py_target, &switch_url, &use_commit_times, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|biOzbb", kwnames, + &py_target, &py_switch_url, &use_commit_times, &depth, ¬ify_func, &diff3_cmd, &depth_is_sticky, &allow_unver_obstructions)) @@ -675,6 +675,13 @@ static PyObject *adm_get_switch_editor(PyObject *self, PyObject *args, PyObject apr_pool_destroy(pool); return NULL; } + + switch_url = py_object_to_svn_uri(py_switch_url, pool); + if (switch_url == NULL) { + apr_pool_destroy(pool); + return NULL; + } + latest_revnum = (svn_revnum_t *)apr_palloc(pool, sizeof(svn_revnum_t)); Py_BEGIN_ALLOW_THREADS #if ONLY_SINCE_SVN(1, 5) -- cgit v1.2.3 From 6297b817671d1c0dbf21823b0a2cb0e9204870ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 12 Aug 2017 01:55:47 +0000 Subject: Fix warnings. --- subvertpy/_ra.c | 2 +- subvertpy/client.c | 2 +- subvertpy/wc_adm.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/subvertpy/_ra.c b/subvertpy/_ra.c index 1aecc762..508a4e7f 100644 --- a/subvertpy/_ra.c +++ b/subvertpy/_ra.c @@ -1038,7 +1038,7 @@ static PyObject *ra_do_switch(PyObject *self, PyObject *args) bool recurse; bool send_copyfrom_args = false; bool ignore_ancestry = true; - char *switch_url; + const char *switch_url; PyObject *update_editor; const REPORTER_T *reporter; void *report_baton; diff --git a/subvertpy/client.c b/subvertpy/client.c index a86614f5..aaf63346 100644 --- a/subvertpy/client.c +++ b/subvertpy/client.c @@ -1362,7 +1362,7 @@ static PyObject *client_propget(PyObject *self, PyObject *args) bool recurse = false; char *propname; apr_pool_t *temp_pool; - char *target; + const char *target; PyObject *peg_revision = Py_None; PyObject *revision; ClientObject *client = (ClientObject *)self; diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index a5e5a5c0..3ecbe142 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -651,7 +651,7 @@ static PyObject *adm_get_switch_editor(PyObject *self, PyObject *args, PyObject bool allow_unver_obstructions = false; bool depth_is_sticky = false; int depth = svn_depth_infinity; - char *switch_url; + const char *switch_url; PyObject *py_target, *py_switch_url; char *kwnames[] = { "target", "switch_url", "use_commit_times", "depth", "notify_func", -- cgit v1.2.3 From 57d81893f8133644248bce4d3c9fd183c25a6bf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Thu, 10 May 2018 01:42:29 +0100 Subject: Opt into apt updates in travis. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 5951c598..f16a4518 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,8 @@ language: python dist: trusty +addons: + apt: + update: true python: - "2.7" - "3.4" -- cgit v1.2.3 From b4b93d315a19f68d4c86459e94d35a3dc0c37175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Tue, 22 May 2018 21:07:44 +0100 Subject: Fix compilation against svn 1.5. --- subvertpy/_ra.c | 4 ++++ subvertpy/client.c | 11 ++++++++--- subvertpy/repos.c | 6 +++++- subvertpy/util.h | 2 ++ subvertpy/wc.c | 14 +++++++++++--- subvertpy/wc.h | 2 +- subvertpy/wc_adm.c | 23 +++++++++++++++++------ 7 files changed, 48 insertions(+), 14 deletions(-) diff --git a/subvertpy/_ra.c b/subvertpy/_ra.c index 508a4e7f..f89e5d0e 100644 --- a/subvertpy/_ra.c +++ b/subvertpy/_ra.c @@ -1432,8 +1432,10 @@ static PyObject *ra_change_rev_prop(PyObject *self, PyObject *args) int vallen, oldvallen = -2; apr_pool_t *temp_pool; svn_string_t *val_string; +#if ONLY_SINCE_SVN(1, 7) const svn_string_t *old_val_string; const svn_string_t *const *old_val_string_p; +#endif if (!PyArg_ParseTuple(args, "lss#|z#:change_rev_prop", &rev, &name, &value, &vallen, &oldvalue, &oldvallen)) @@ -3301,7 +3303,9 @@ static PyObject *get_platform_specific_client_providers(PyObject *self) return pylist; #else PyObject *pylist = PyList_New(0); +#if defined(WIN32) || defined(__CYGWIN__) || defined(SVN_KEYCHAIN_PROVIDER_AVAILABLE) PyObject *provider = NULL; +#endif if (pylist == NULL) { Py_DECREF(pylist); diff --git a/subvertpy/client.c b/subvertpy/client.c index aaf63346..e85c5be7 100644 --- a/subvertpy/client.c +++ b/subvertpy/client.c @@ -72,6 +72,7 @@ typedef struct { #define INVOKE_COMMIT_CALLBACK(pool, commit_info, callback) \ { \ + PyObject *ret; \ PyObject *py_commit_info = py_commit_info_tuple(commit_info); \ if (py_commit_info == NULL) { \ apr_pool_destroy(pool); \ @@ -755,6 +756,7 @@ static PyObject *client_checkout(PyObject *self, PyObject *args, PyObject *kwarg return PyLong_FromLong(result_rev); } +#if ONLY_SINCE_SVN(1, 7) static svn_error_t *py_commit_callback2(const svn_commit_info_t *commit_info, void *callback, apr_pool_t *pool) { PyObject *py_callback = callback; @@ -786,6 +788,7 @@ static svn_error_t *py_commit_callback2(const svn_commit_info_t *commit_info, return NULL; } +#endif static PyObject *client_commit(PyObject *self, PyObject *args, PyObject *kwargs) { @@ -954,7 +957,6 @@ static PyObject *client_cat(PyObject *self, PyObject *args, PyObject *kwargs) svn_stream_t *stream; bool expand_keywords = true; PyObject *py_stream, *py_path, *ret; - apr_hash_t *props = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|OOb", kwnames, &py_path, &py_stream, &rev, &peg_rev, &expand_keywords)) return NULL; @@ -982,6 +984,8 @@ static PyObject *client_cat(PyObject *self, PyObject *args, PyObject *kwargs) } #if ONLY_SINCE_SVN(1, 9) + { + apr_hash_t *props = NULL; RUN_SVN_WITH_POOL(temp_pool, svn_client_cat3( &props, stream, path, &c_peg_rev, &c_rev, expand_keywords, client->client, temp_pool, temp_pool)); @@ -991,6 +995,7 @@ static PyObject *client_cat(PyObject *self, PyObject *args, PyObject *kwargs) apr_pool_destroy(temp_pool); return NULL; } + } #else if (!expand_keywords) { PyErr_SetString(PyExc_NotImplementedError, @@ -1186,7 +1191,7 @@ static PyObject *client_copy(PyObject *self, PyObject *args, PyObject *kwargs) svn_client_copy_source_t src; #endif - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss|ObbbbOOO", kwnames, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss|ObbbObbO", kwnames, &src_path, &dst_path, &src_rev, ©_as_child, &make_parents, &ignore_externals, &py_revprops, &metadata_only, &pin_externals, &callback)) @@ -1242,7 +1247,7 @@ static PyObject *client_copy(PyObject *self, PyObject *args, PyObject *kwargs) apr_pool_destroy(temp_pool); return NULL; } - if (pin_externals != Py_None) { + if (pin_externals) { PyErr_SetString(PyExc_NotImplementedError, "pin_externals not supported in svn < 1.9"); apr_pool_destroy(temp_pool); diff --git a/subvertpy/repos.c b/subvertpy/repos.c index 44c48416..43279683 100644 --- a/subvertpy/repos.c +++ b/subvertpy/repos.c @@ -39,7 +39,7 @@ typedef struct { static PyObject *repos_create(PyObject *self, PyObject *args) { - char *path; + const char *path; PyObject *config=Py_None, *fs_config=Py_None, *py_path; svn_repos_t *repos = NULL; apr_pool_t *pool; @@ -499,6 +499,7 @@ static PyObject *repos_verify(RepositoryObject *self, PyObject *args) Py_RETURN_NONE; } +#if ONLY_SINCE_SVN(1, 6) 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; @@ -527,6 +528,7 @@ static PyObject *repos_pack(RepositoryObject *self, PyObject *args) Py_RETURN_NONE; } +#endif static PyMethodDef repos_methods[] = { { "load_fs", (PyCFunction)repos_load_fs, METH_VARARGS|METH_KEYWORDS, NULL }, @@ -534,7 +536,9 @@ static PyMethodDef repos_methods[] = { { "has_capability", (PyCFunction)repos_has_capability, METH_VARARGS, NULL }, { "verify_fs", (PyCFunction)repos_verify, METH_VARARGS, "S.verify_repos(feedback_stream, start_revnum, end_revnum)" }, +#if ONLY_SINCE_SVN(1, 6) { "pack_fs", (PyCFunction)repos_pack, METH_VARARGS, NULL }, +#endif { NULL, } }; diff --git a/subvertpy/util.h b/subvertpy/util.h index 60a61700..b2bd1d07 100644 --- a/subvertpy/util.h +++ b/subvertpy/util.h @@ -150,6 +150,8 @@ svn_relpath_canonicalize(const char *relpath, const char * svn_dirent_canonicalize(const char *dirent, apr_pool_t *result_pool); +#define svn_dirent_get_absolute svn_path_get_absolute +#define svn_dirent_is_absolute svn_path_is_url #endif const char *py_object_to_svn_uri(PyObject *obj, apr_pool_t *pool); diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 59e0e023..fe7005ad 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -34,8 +34,6 @@ #define T_BOOL T_BYTE #endif -static PyTypeObject Context_Type; - #if ONLY_BEFORE_SVN(1, 5) struct svn_wc_committed_queue_t { @@ -577,8 +575,8 @@ static PyObject *get_pristine_contents(PyObject *self, PyObject *args) apr_pool_t *temp_pool; PyObject *py_path; #if ONLY_SINCE_SVN(1, 6) - apr_pool_t *stream_pool; StreamObject *ret; + apr_pool_t *stream_pool; svn_stream_t *stream; #else PyObject *ret; @@ -598,6 +596,12 @@ static PyObject *get_pristine_contents(PyObject *self, PyObject *args) apr_pool_destroy(stream_pool); return NULL; } +#else + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } +#endif path = py_object_to_svn_dirent(py_path, temp_pool); if (path == NULL) { @@ -605,6 +609,7 @@ static PyObject *get_pristine_contents(PyObject *self, PyObject *args) return NULL; } +#if ONLY_SINCE_SVN(1, 6) RUN_SVN_WITH_POOL(stream_pool, svn_wc_get_pristine_contents(&stream, path, stream_pool, temp_pool)); apr_pool_destroy(temp_pool); @@ -1017,6 +1022,7 @@ PyTypeObject CommittedQueue_Type = { }; #if ONLY_SINCE_SVN(1, 7) +static PyTypeObject Context_Type; typedef struct { PyObject_VAR_HEAD @@ -1945,8 +1951,10 @@ moduleinit(void) if (PyType_Ready(&CommittedQueue_Type) < 0) return NULL; +#if ONLY_SINCE_SVN(1, 7) if (PyType_Ready(&Status3_Type) < 0) return NULL; +#endif apr_initialize(); diff --git a/subvertpy/wc.h b/subvertpy/wc.h index abf8d647..558c38dd 100644 --- a/subvertpy/wc.h +++ b/subvertpy/wc.h @@ -26,7 +26,7 @@ bool py_dict_to_wcprop_changes(PyObject *dict, apr_pool_t *pool, apr_array_header_t **ret); void py_wc_notify_func(void *baton, const svn_wc_notify_t *notify, apr_pool_t *pool); -PyObject *py_wc_status2(const svn_wc_status2_t *status); +PyObject *py_wc_status2(svn_wc_status2_t *status); #if ONLY_SINCE_SVN(1, 5) extern const svn_ra_reporter3_t py_ra_reporter3; #endif diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index 3ecbe142..ab8cf306 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -484,6 +484,7 @@ static PyObject *adm_add(PyObject *self, PyObject *args, PyObject *kwargs) RUN_SVN_WITH_POOL(temp_pool, svn_wc_add2( path, admobj->adm, copyfrom_url, copyfrom_rev, py_cancel_check, + NULL, py_wc_notify_func, (void *)notify_func, temp_pool)); @@ -1031,13 +1032,15 @@ static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs AdmObject *admobj = (AdmObject *)self; apr_pool_t *temp_pool; PyObject *py_dst_path; - const char *dst_path; char *copyfrom_url = NULL; svn_revnum_t copyfrom_rev = -1; PyObject *py_new_base_contents, *py_new_contents, *py_new_base_props, *py_new_props, *notify = Py_None; +#if ONLY_SINCE_SVN(1, 6) + const char *dst_path; svn_stream_t *new_contents, *new_base_contents; apr_hash_t *new_props, *new_base_props; +#endif if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOOOO|zlO", kwnames, &py_dst_path, &py_new_base_contents, &py_new_contents, &py_new_base_props, @@ -1050,6 +1053,7 @@ static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs if (temp_pool == NULL) return NULL; +#if ONLY_SINCE_SVN(1, 6) new_base_props = prop_dict_to_hash(temp_pool, py_new_base_props); new_props = prop_dict_to_hash(temp_pool, py_new_props); @@ -1060,7 +1064,6 @@ static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs dst_path = py_object_to_svn_dirent(py_dst_path, temp_pool); -#if ONLY_SINCE_SVN(1, 6) RUN_SVN_WITH_POOL(temp_pool, svn_wc_add_repos_file3(dst_path, admobj->adm, new_base_contents, new_contents, @@ -1072,7 +1075,6 @@ static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs #else PyErr_SetString(PyExc_NotImplementedError, "add_repos_file3 not supported on svn < 1.6"); - apr_pool_destroy(temp_pool); #endif apr_pool_destroy(temp_pool); @@ -1177,7 +1179,9 @@ static PyObject *crop_tree(PyObject *self, PyObject *args) char *target; svn_depth_t depth; PyObject *notify; +#if ONLY_SINCE_SVN(1, 6) apr_pool_t *temp_pool; +#endif AdmObject *admobj = (AdmObject *)self; if (!PyArg_ParseTuple(args, "si|O", &target, &depth, ¬ify)) @@ -1680,8 +1684,13 @@ static PyObject *conflicted(PyObject *self, PyObject *args) apr_pool_t *temp_pool; PyObject *ret; AdmObject *admobj = (AdmObject *)self; - svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted; + svn_boolean_t text_conflicted, prop_conflicted; PyObject *py_path; +#if ONLY_BEFORE_SVN(1, 6) + const svn_wc_entry_t *entry; +#else + svn_boolean_t tree_conflicted; +#endif if (!PyArg_ParseTuple(args, "O", &py_path)) return NULL; @@ -1705,8 +1714,10 @@ static PyObject *conflicted(PyObject *self, PyObject *args) ret = Py_BuildValue("(bbb)", text_conflicted, prop_conflicted, tree_conflicted); #else + RUN_SVN_WITH_POOL(temp_pool, svn_wc_entry(&entry, path, admobj->adm, TRUE, temp_pool)); + RUN_SVN_WITH_POOL(temp_pool, svn_wc_conflicted_p(&text_conflicted, - &prop_conflicted, path, admobj->adm, temp_pool)); + &prop_conflicted, path, entry, temp_pool)); ret = Py_BuildValue("(bbO)", text_conflicted, prop_conflicted, Py_None); #endif @@ -2103,7 +2114,7 @@ PyTypeObject Status2_Type = { }; -PyObject *py_wc_status2(const svn_wc_status2_t *status) +PyObject *py_wc_status2(svn_wc_status2_t *status) { Status2Object *ret; svn_wc_status2_t *dup_status; -- cgit v1.2.3 From e110a8bd9a6ba3c0ec22eacb43309144dd046846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Tue, 22 May 2018 21:18:02 +0100 Subject: Attempt to build against multiple svn versions. --- .travis.yml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f16a4518..7ee5ad51 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,16 +3,31 @@ dist: trusty addons: apt: update: true + packages: libapr1-dev apache2-dev libaprutil1-dev libdb5.3-dev liblz4-dev libsasl2-dev libperl-dev libserf-dev libsqlite3-dev libtool python-all-dev libutf8proc-dev python: - "2.7" - "3.4" - "3.5" - "3.6" +env: + - SVN_VERSION=1.10.0 + - SVN_VERSION=1.9.7 + - SVN_VERSION=1.8.19 + - SVN_VERSION=1.7.19 + - SVN_VERSION=1.6.21 + - SVN_VERSION=1.5.9 script: - make check - make style install: - travis_retry pip install -U pip coverage codecov flake8 before_install: - - sudo apt-get update -qq - - sudo apt-get install -y libsvn-dev + - wget http://www-eu.apache.org/dist/subversion/subversion-${SVN_VERSION}.tar.gz + - tar xvfz subversion-${SVN_VERSION}.tar.gz + - cd subversion-${SVN_VERSION} + - ./configure + - make + - sudo make install +after_success: + - python -m coverage combine + - codecov -- cgit v1.2.3 From 6e3bc12eec13a8ae12e526305f69b5a44ac5f5dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Tue, 22 May 2018 21:20:59 +0100 Subject: Drop libutf8proc-dev. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7ee5ad51..f12a1f3b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ dist: trusty addons: apt: update: true - packages: libapr1-dev apache2-dev libaprutil1-dev libdb5.3-dev liblz4-dev libsasl2-dev libperl-dev libserf-dev libsqlite3-dev libtool python-all-dev libutf8proc-dev + packages: libapr1-dev apache2-dev libaprutil1-dev libdb5.3-dev liblz4-dev libsasl2-dev libperl-dev libserf-dev libsqlite3-dev libtool python-all-dev python: - "2.7" - "3.4" -- cgit v1.2.3 From 6f484be7ff834ecd08f58d140c48f752bd0fc85c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Tue, 22 May 2018 21:39:27 +0100 Subject: Download from archive. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f12a1f3b..eccccd3c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ script: install: - travis_retry pip install -U pip coverage codecov flake8 before_install: - - wget http://www-eu.apache.org/dist/subversion/subversion-${SVN_VERSION}.tar.gz + - wget https://archive.apache.org/dist/subversion/subversion-${SVN_VERSION}.tar.gz - tar xvfz subversion-${SVN_VERSION}.tar.gz - cd subversion-${SVN_VERSION} - ./configure -- cgit v1.2.3 From 86894499b9576713dbf03e0a4a60e5fb33368bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Tue, 22 May 2018 22:07:30 +0100 Subject: Go back to subvertpy after building subversion. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index eccccd3c..ef46edd6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,7 @@ before_install: - ./configure - make - sudo make install + - cd .. after_success: - python -m coverage combine - codecov -- cgit v1.2.3 From b6ee0437798a9114555a7466eb146af9890ad842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Tue, 22 May 2018 22:13:16 +0100 Subject: Build svn 1.10 with --with-lz4=internal. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ef46edd6..c43d656a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ python: - "3.6" env: - SVN_VERSION=1.10.0 + SVN_OPTIONS="--with-lz4=internal" - SVN_VERSION=1.9.7 - SVN_VERSION=1.8.19 - SVN_VERSION=1.7.19 @@ -25,7 +26,7 @@ before_install: - wget https://archive.apache.org/dist/subversion/subversion-${SVN_VERSION}.tar.gz - tar xvfz subversion-${SVN_VERSION}.tar.gz - cd subversion-${SVN_VERSION} - - ./configure + - ./configure ${SVN_OPTIONS} - make - sudo make install - cd .. -- cgit v1.2.3 From bb93938fa4c9eecb670824f34e40d7b776765403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Tue, 22 May 2018 22:14:35 +0100 Subject: Install libneon for older versions of svn. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c43d656a..e2a03420 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ dist: trusty addons: apt: update: true - packages: libapr1-dev apache2-dev libaprutil1-dev libdb5.3-dev liblz4-dev libsasl2-dev libperl-dev libserf-dev libsqlite3-dev libtool python-all-dev + packages: libapr1-dev apache2-dev libaprutil1-dev libdb5.3-dev liblz4-dev libsasl2-dev libperl-dev libserf-dev libsqlite3-dev libtool python-all-dev libneon27-gnutls-dev python: - "2.7" - "3.4" -- cgit v1.2.3 From aaf73b85a2b2d68a0a9efffa0ca517752c1ae004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Tue, 22 May 2018 22:15:34 +0100 Subject: Don't build against apache2-dev. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e2a03420..38b6de5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ dist: trusty addons: apt: update: true - packages: libapr1-dev apache2-dev libaprutil1-dev libdb5.3-dev liblz4-dev libsasl2-dev libperl-dev libserf-dev libsqlite3-dev libtool python-all-dev libneon27-gnutls-dev + packages: libapr1-dev libaprutil1-dev libdb5.3-dev liblz4-dev libsasl2-dev libperl-dev libserf-dev libsqlite3-dev libtool python-all-dev libneon27-gnutls-dev python: - "2.7" - "3.4" -- cgit v1.2.3 From f931d1b6d17f36dba0fbbe2556455c9cc60675c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Tue, 22 May 2018 22:16:14 +0100 Subject: Run ldconfig after svn install. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 38b6de5f..85b251df 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,7 @@ before_install: - ./configure ${SVN_OPTIONS} - make - sudo make install + - sudo ldconfig -v - cd .. after_success: - python -m coverage combine -- cgit v1.2.3 From 78dabf5163757b57917c5d9b1000d967be2a540e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Tue, 22 May 2018 22:19:56 +0100 Subject: Skip 1.10 build for now - it requires libutf8proc-dev. --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 85b251df..6ac583f5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,9 @@ python: - "3.5" - "3.6" env: - - SVN_VERSION=1.10.0 - SVN_OPTIONS="--with-lz4=internal" +# SVN 1.10 requires libutf8proc, which isn't available on trusty :( +# - SVN_VERSION=1.10.0 +# SVN_OPTIONS="--with-lz4=internal" - SVN_VERSION=1.9.7 - SVN_VERSION=1.8.19 - SVN_VERSION=1.7.19 -- cgit v1.2.3 From 88f8920302a2d21c561e827dfae1358d3b7822a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Tue, 22 May 2018 22:24:54 +0100 Subject: Only check style in known directories. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index dff0eb45..5bff979b 100644 --- a/Makefile +++ b/Makefile @@ -38,4 +38,4 @@ pydoctor: $(PYDOCTOR) $(PYDOCTOR_OPTIONS) --introspect-c-modules -c subvertpy.cfg --make-html style: - $(FLAKE8) --exclude=build,.git,build-pypy,.tox + $(FLAKE8) --exclude=build,.git,build-pypy,.tox subvertpy bin -- cgit v1.2.3 From feaa24919245b9a25b05587d0d1826555bfaab97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Tue, 22 May 2018 22:25:27 +0100 Subject: Various fixes for running on svn 1.5. --- subvertpy/properties.py | 10 +++++----- subvertpy/ra_svn.py | 6 +++--- subvertpy/repos.c | 9 ++++++--- subvertpy/tests/test_marshall.py | 16 ++++++++-------- subvertpy/tests/test_repos.py | 5 ++++- subvertpy/tests/test_subr.py | 2 +- subvertpy/tests/test_wc.py | 10 +++++++++- subvertpy/util.c | 2 +- subvertpy/wc_adm.c | 4 ++++ 9 files changed, 41 insertions(+), 23 deletions(-) diff --git a/subvertpy/properties.py b/subvertpy/properties.py index 567f60cc..8f256f5f 100644 --- a/subvertpy/properties.py +++ b/subvertpy/properties.py @@ -85,10 +85,10 @@ def parse_externals_description(base_url, val): def is_url(u): return ("://" in u) ret = {} - for l in val.splitlines(): - if l == "" or l[0] == "#": + for line in val.splitlines(): + if line == "" or line[0] == "#": continue - pts = l.rsplit(None, 3) + pts = line.rsplit(None, 3) if len(pts) == 4: if pts[0] == "-r": # -r X URL DIR revno = int(pts[1]) @@ -137,8 +137,8 @@ def parse_mergeinfo_property(text): :param text: Property contents """ ret = {} - for l in text.splitlines(): - (path, ranges) = l.rsplit(":", 1) + for line in text.splitlines(): + (path, ranges) = line.rsplit(":", 1) assert path.startswith("/") ret[path] = [] for range in ranges.split(","): diff --git a/subvertpy/ra_svn.py b/subvertpy/ra_svn.py index 3e61e283..af6f2f14 100644 --- a/subvertpy/ra_svn.py +++ b/subvertpy/ra_svn.py @@ -745,7 +745,7 @@ class SVNClient(SVNConnection): self.send_msg([literal("switch"), args]) self._recv_ack() return Reporter(self, update_editor) - except: + except BaseException: self.busy = False raise @@ -766,7 +766,7 @@ class SVNClient(SVNConnection): self.send_msg([literal("update"), args]) self._recv_ack() return Reporter(self, update_editor) - except: + except BaseException: self.busy = False raise @@ -787,7 +787,7 @@ class SVNClient(SVNConnection): self.send_msg([literal("diff"), args]) self._recv_ack() return Reporter(self, diff_editor) - except: + except BaseException: self.busy = False raise diff --git a/subvertpy/repos.c b/subvertpy/repos.c index 43279683..61006ab8 100644 --- a/subvertpy/repos.c +++ b/subvertpy/repos.c @@ -511,9 +511,11 @@ static svn_error_t *py_pack_notify(void *baton, apr_int64_t shard, svn_fs_pack_n Py_DECREF(ret); return NULL; } +#endif static PyObject *repos_pack(RepositoryObject *self, PyObject *args) { +#if ONLY_SINCE_SVN(1, 6) apr_pool_t *temp_pool; PyObject *notify_func = Py_None; if (!PyArg_ParseTuple(args, "|O", ¬ify_func)) @@ -527,8 +529,11 @@ static PyObject *repos_pack(RepositoryObject *self, PyObject *args) apr_pool_destroy(temp_pool); Py_RETURN_NONE; -} +#else + PyErr_SetString(PyExc_NotImplementedError, "pack_fs is only supported in Subversion >= 1.6"); + return NULL; #endif +} static PyMethodDef repos_methods[] = { { "load_fs", (PyCFunction)repos_load_fs, METH_VARARGS|METH_KEYWORDS, NULL }, @@ -536,9 +541,7 @@ static PyMethodDef repos_methods[] = { { "has_capability", (PyCFunction)repos_has_capability, METH_VARARGS, NULL }, { "verify_fs", (PyCFunction)repos_verify, METH_VARARGS, "S.verify_repos(feedback_stream, start_revnum, end_revnum)" }, -#if ONLY_SINCE_SVN(1, 6) { "pack_fs", (PyCFunction)repos_pack, METH_VARARGS, NULL }, -#endif { NULL, } }; diff --git a/subvertpy/tests/test_marshall.py b/subvertpy/tests/test_marshall.py index 751276f2..47d659dc 100644 --- a/subvertpy/tests/test_marshall.py +++ b/subvertpy/tests/test_marshall.py @@ -28,20 +28,20 @@ from subvertpy.tests import TestCase class TestMarshalling(TestCase): def test_literal_txt(self): - l = literal("foo") - self.assertEqual("foo", l.txt) + line = literal("foo") + self.assertEqual("foo", line.txt) def test_literal_str(self): - l = literal("foo bar") - self.assertEqual("foo bar", l.__str__()) + line = literal("foo bar") + self.assertEqual("foo bar", line.__str__()) def test_literal_rep(self): - l = literal("foo bar") - self.assertEqual("foo bar", l.__repr__()) + line = literal("foo bar") + self.assertEqual("foo bar", line.__repr__()) def test_marshall_error(self): - e = MarshallError("bla bla") - self.assertEqual("bla bla", e.__str__()) + err = MarshallError("bla bla") + self.assertEqual("bla bla", err.__str__()) def test_marshall_int(self): self.assertEqual(b"1 ", marshall(1)) diff --git a/subvertpy/tests/test_repos.py b/subvertpy/tests/test_repos.py index ed5f2067..d748df91 100644 --- a/subvertpy/tests/test_repos.py +++ b/subvertpy/tests/test_repos.py @@ -117,7 +117,10 @@ class TestRepository(TestCaseInTempDir): def test_pack_fs(self): r = repos.create(os.path.join(self.test_dir, "foo")) - r.pack_fs() + if repos.api_version() < (1, 6): + self.assertRaises(NotImplementedError, r.pack_fs) + else: + r.pack_fs() def test_paths_changed(self): repos.create(os.path.join(self.test_dir, "foo")) diff --git a/subvertpy/tests/test_subr.py b/subvertpy/tests/test_subr.py index b80290f0..a7c517dd 100644 --- a/subvertpy/tests/test_subr.py +++ b/subvertpy/tests/test_subr.py @@ -24,7 +24,7 @@ class UriCanonicalizeTests(TestCase): def test_canonicalize(self): self.assertEqual( - 'https://www.example.com', + 'https://www.example.com/', uri_canonicalize('https://www.example.com/')) self.assertEqual( 'https://www.example.com(bla)', diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index ca46fec2..b236dea0 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -160,7 +160,10 @@ class AdmObjTests(SubversionTestCase): adm = wc.Adm(None, "checkout", True) path = os.path.join(self.test_dir, "checkout/bar") stream = adm.translated_stream(path, path, wc.TRANSLATE_TO_NF) - self.assertTrue(stream.read().startswith(b"My id: $Id: ")) + if wc.api_version() < (1, 6): + self.assertRaises(NotImplementedError, stream.read) + else: + self.assertTrue(stream.read().startswith(b"My id: $Id: ")) def test_text_modified(self): self.make_client("repos", "checkout") @@ -323,6 +326,11 @@ class AdmObjTests(SubversionTestCase): class ContextTests(SubversionTestCase): + def setUp(self): + super(ContextTests, self).setUp() + if wc.api_version() < (1, 7): + self.skipTest("context API not available on Subversion < 1.7") + def test_create(self): context = wc.Context() self.assertIsInstance(context, wc.Context) diff --git a/subvertpy/util.c b/subvertpy/util.c index d7dadd3f..2700c65e 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -47,7 +47,7 @@ const char * svn_uri_canonicalize(const char *uri, apr_pool_t *result_pool) { - return svn_path_canonicalize(uri, result_pool); + return svn_path_uri_from_iri(uri, result_pool); } const char * diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index ab8cf306..437a5442 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -2007,6 +2007,10 @@ static PyObject *py_entry(const svn_wc_entry_t *entry) { EntryObject *ret; + if (entry == NULL) { + Py_RETURN_NONE; + } + ret = PyObject_New(EntryObject, &Entry_Type); if (ret == NULL) return NULL; -- cgit v1.2.3 From f4fb41fa46d848842d19c02d546726fa395f5159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Wed, 23 May 2018 03:06:58 +0100 Subject: Various fixes for URL canonicalization. --- subvertpy/_ra.c | 12 ++++++------ subvertpy/tests/test_subr.py | 2 +- subvertpy/tests/test_wc.py | 2 +- subvertpy/util.c | 40 ++++++++++++++++++++-------------------- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/subvertpy/_ra.c b/subvertpy/_ra.c index f89e5d0e..4f5125d4 100644 --- a/subvertpy/_ra.c +++ b/subvertpy/_ra.c @@ -1059,12 +1059,12 @@ static PyObject *ra_do_switch(PyObject *self, PyObject *args) return NULL; } - switch_url = py_object_to_svn_uri(py_switch_url, temp_pool); - if (switch_url == NULL) { - apr_pool_destroy(temp_pool); - ra->busy = false; - return NULL; - } + switch_url = py_object_to_svn_uri(py_switch_url, temp_pool); + if (switch_url == NULL) { + apr_pool_destroy(temp_pool); + ra->busy = false; + return NULL; + } result_pool = Pool(NULL); if (result_pool == NULL) { diff --git a/subvertpy/tests/test_subr.py b/subvertpy/tests/test_subr.py index a7c517dd..b80290f0 100644 --- a/subvertpy/tests/test_subr.py +++ b/subvertpy/tests/test_subr.py @@ -24,7 +24,7 @@ class UriCanonicalizeTests(TestCase): def test_canonicalize(self): self.assertEqual( - 'https://www.example.com/', + 'https://www.example.com', uri_canonicalize('https://www.example.com/')) self.assertEqual( 'https://www.example.com(bla)', diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index b236dea0..56036712 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -288,7 +288,7 @@ class AdmObjTests(SubversionTestCase): self.assertEqual("bar", bar.name) self.assertEqual(NODE_FILE, bar.kind) self.assertEqual(wc.SCHEDULE_NORMAL, bar.schedule) - self.assertIs(None, bar.checksum) + self.assertEqual('52419dba51d65210e87bf52dc1072145', bar.checksum) self.assertEqual(1, bar.cmt_rev) self.assertEqual(1, bar.revision) diff --git a/subvertpy/util.c b/subvertpy/util.c index 2700c65e..7536f613 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -47,7 +47,7 @@ const char * svn_uri_canonicalize(const char *uri, apr_pool_t *result_pool) { - return svn_path_uri_from_iri(uri, result_pool); + return svn_path_canonicalize(uri, result_pool); } const char * @@ -180,26 +180,26 @@ char *py_object_to_svn_string(PyObject *obj, apr_pool_t *pool) const char *py_object_to_svn_uri(PyObject *obj, apr_pool_t *pool) { - const char *ret; - PyObject *bytes_obj = NULL; + const char *ret; + PyObject *bytes_obj = NULL; - if (PyUnicode_Check(obj)) { - bytes_obj = obj = PyUnicode_AsUTF8String(obj); - if (obj == NULL) { - return NULL; - } - } + if (PyUnicode_Check(obj)) { + bytes_obj = obj = PyUnicode_AsUTF8String(obj); + if (obj == NULL) { + return NULL; + } + } - if (PyBytes_Check(obj)) { - ret = svn_uri_canonicalize(PyBytes_AsString(obj), pool); - Py_XDECREF(bytes_obj); - return ret; - } else { - PyErr_SetString(PyExc_TypeError, - "URIs need to be UTF-8 bytestrings or unicode strings"); - Py_XDECREF(bytes_obj); - return NULL; - } + if (PyBytes_Check(obj)) { + ret = svn_uri_canonicalize(PyBytes_AsString(obj), pool); + Py_XDECREF(bytes_obj); + return ret; + } else { + PyErr_SetString(PyExc_TypeError, + "URIs need to be UTF-8 bytestrings or unicode strings"); + Py_XDECREF(bytes_obj); + return NULL; + } } const char *py_object_to_svn_relpath(PyObject *obj, apr_pool_t *pool) @@ -503,7 +503,7 @@ PyObject *prop_hash_to_dict(apr_hash_t *props) py_key = Py_None; Py_INCREF(py_key); } else { - py_key = PyUnicode_FromString(key); + py_key = PyBytes_FromStringAndSize(key, klen); } if (PyDict_SetItem(py_props, py_key, py_val) != 0) { Py_DECREF(py_key); -- cgit v1.2.3 From fb3a38809bc02eb945e13089fcb3f60a314a75f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Wed, 23 May 2018 20:05:07 +0100 Subject: Use length for string when it is known. --- subvertpy/util.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/subvertpy/util.c b/subvertpy/util.c index 7536f613..af16aecd 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -503,7 +503,7 @@ PyObject *prop_hash_to_dict(apr_hash_t *props) py_key = Py_None; Py_INCREF(py_key); } else { - py_key = PyBytes_FromStringAndSize(key, klen); + py_key = PyUnicode_FromStringAndSize(key, klen); } if (PyDict_SetItem(py_props, py_key, py_val) != 0) { Py_DECREF(py_key); @@ -1160,7 +1160,7 @@ PyObject *dirent_hash_to_dict(apr_hash_t *dirents, unsigned int dirent_fields, a pykey = Py_None; Py_INCREF(pykey); } else { - pykey = PyUnicode_FromString((char *)key); + pykey = PyUnicode_FromStringAndSize(key, klen); } if (PyDict_SetItem(py_dirents, pykey, item) != 0) { Py_DECREF(item); -- cgit v1.2.3 From 3c46528da5144d2d09b0167562adad852152be3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Wed, 23 May 2018 20:27:59 +0100 Subject: Add some tests for path manipulation functions. --- subvertpy/subr.c | 38 ++++++++++++++++++++++++++++++++++++++ subvertpy/tests/test_subr.py | 32 +++++++++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/subvertpy/subr.c b/subvertpy/subr.c index 2a2af7b1..6bf2aa26 100644 --- a/subvertpy/subr.c +++ b/subvertpy/subr.c @@ -42,9 +42,47 @@ static PyObject *py_uri_canonicalize(PyObject *self, PyObject *args) return ret; } +static PyObject *py_dirent_canonicalize(PyObject *self, PyObject *args) +{ + const char *dirent; + PyObject *py_dirent, *ret; + apr_pool_t *pool; + + if (!PyArg_ParseTuple(args, "O", &py_dirent)) + return NULL; + + pool = Pool(NULL); + dirent = py_object_to_svn_dirent(py_dirent, pool); + ret = PyUnicode_FromString(dirent); + apr_pool_destroy(pool); + + return ret; +} + +static PyObject *py_abspath(PyObject *self, PyObject *args) +{ + const char *path; + PyObject *py_path, *ret; + apr_pool_t *pool; + + if (!PyArg_ParseTuple(args, "O", &py_path)) + return NULL; + + pool = Pool(NULL); + path = py_object_to_svn_abspath(py_path, pool); + ret = PyUnicode_FromString(path); + apr_pool_destroy(pool); + + return ret; +} + static PyMethodDef subr_methods[] = { { "uri_canonicalize", py_uri_canonicalize, METH_VARARGS, "uri_canonicalize(uri) -> uri\n" "Canonicalize a URI."}, + { "dirent_canonicalize", py_dirent_canonicalize, METH_VARARGS, "dirent_canonicalize(dirent) -> dirent\n" + "Canonicalize a dirent path."}, + { "abspath", py_abspath, METH_VARARGS, "abspath(path) -> path\n" + "Return the absolute version of a path."}, { NULL } }; diff --git a/subvertpy/tests/test_subr.py b/subvertpy/tests/test_subr.py index b80290f0..c471e65f 100644 --- a/subvertpy/tests/test_subr.py +++ b/subvertpy/tests/test_subr.py @@ -15,9 +15,14 @@ """Subversion subr library tests.""" +import os from unittest import TestCase -from subvertpy.subr import uri_canonicalize +from subvertpy.subr import ( + uri_canonicalize, + dirent_canonicalize, + abspath, + ) class UriCanonicalizeTests(TestCase): @@ -32,3 +37,28 @@ class UriCanonicalizeTests(TestCase): self.assertEqual( 'https://www.example.com/(bla)', uri_canonicalize('https://www.example.com/(bla%29')) + + +class DirentCanonicalizeTests(TestCase): + + def test_canonicalize(self): + self.assertEqual( + '/foo/bar', + dirent_canonicalize('/foo/bar')) + self.assertEqual( + '/foo/bar', + dirent_canonicalize('/foo//bar')) + + +class AbspathTests(TestCase): + + def test_abspath(self): + self.assertEqual( + '/foo/bar', + abspath('/foo//bar')) + self.assertEqual( + os.path.join(os.getcwd(), 'bar'), + abspath('bar')) + self.assertEqual( + os.path.join(os.getcwd(), 'bar', 'foo'), + abspath('bar/foo')) -- cgit v1.2.3 From 0b4ae45ca3b60a6b0509a9131e17d14ddd4e55f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Wed, 23 May 2018 20:35:32 +0100 Subject: Pass in absolute paths in a few more places. --- subvertpy/tests/test_wc.py | 10 ++++++++-- subvertpy/wc.c | 4 ++-- subvertpy/wc_adm.c | 14 ++++++++------ 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 56036712..d1fdde37 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -118,6 +118,8 @@ class AdmObjTests(SubversionTestCase): def test_add_repos_file(self): if wc.api_version() >= (1, 7): self.skipTest("TODO: doesn't yet work with svn >= 1.7") + if wc.api_version() < (1, 6): + self.skipTest("doesn't work with svn < 1.6") self.make_client("repos", "checkout") adm = wc.Adm(None, "checkout", True) adm.add_repos_file("checkout/bar", BytesIO(b"basecontents"), @@ -243,10 +245,14 @@ class AdmObjTests(SubversionTestCase): self.build_tree({"checkout/bar": b"text"}) self.client_add('checkout/bar') adm = wc.Adm(None, "checkout") - self.assertEqual(wc.STATUS_ADDED, adm.status('bar').status) + self.assertEqual( + wc.STATUS_ADDED, + adm.status('checkout/bar').status) self.client_commit("checkout", "foo") adm = wc.Adm(None, "checkout") - self.assertEqual(wc.STATUS_NORMAL, adm.status('bar').status) + self.assertEqual( + wc.STATUS_NORMAL, + adm.status('checkout/bar').status) def test_transmit_text_deltas(self): if wc.api_version() >= (1, 7): diff --git a/subvertpy/wc.c b/subvertpy/wc.c index fe7005ad..744bca57 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -554,7 +554,7 @@ static PyObject *get_pristine_copy_path(PyObject *self, PyObject *args) if (pool == NULL) return NULL; - path = py_object_to_svn_dirent(py_path, pool); + path = py_object_to_svn_abspath(py_path, pool); if (path == NULL) { apr_pool_destroy(pool); return NULL; @@ -603,7 +603,7 @@ static PyObject *get_pristine_contents(PyObject *self, PyObject *args) } #endif - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index 437a5442..00cc26fb 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -1030,13 +1030,13 @@ static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs "new_base_props", "new_props", "copyfrom_url", "copyfrom_rev", "notify", NULL }; AdmObject *admobj = (AdmObject *)self; - apr_pool_t *temp_pool; PyObject *py_dst_path; char *copyfrom_url = NULL; svn_revnum_t copyfrom_rev = -1; PyObject *py_new_base_contents, *py_new_contents, *py_new_base_props, *py_new_props, *notify = Py_None; #if ONLY_SINCE_SVN(1, 6) + apr_pool_t *temp_pool; const char *dst_path; svn_stream_t *new_contents, *new_base_contents; apr_hash_t *new_props, *new_base_props; @@ -1049,11 +1049,11 @@ static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs ADM_CHECK_CLOSED(admobj); +#if ONLY_SINCE_SVN(1, 6) temp_pool = Pool(NULL); if (temp_pool == NULL) return NULL; -#if ONLY_SINCE_SVN(1, 6) new_base_props = prop_dict_to_hash(temp_pool, py_new_base_props); new_props = prop_dict_to_hash(temp_pool, py_new_props); @@ -1072,14 +1072,16 @@ static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs copyfrom_url, copyfrom_rev, py_cancel_check, NULL, py_wc_notify_func, notify, temp_pool)); -#else - PyErr_SetString(PyExc_NotImplementedError, - "add_repos_file3 not supported on svn < 1.6"); -#endif apr_pool_destroy(temp_pool); Py_RETURN_NONE; + +#else + PyErr_SetString(PyExc_NotImplementedError, + "add_repos_file3 not supported on svn < 1.6"); + return NULL; +#endif } static PyObject *mark_missing_deleted(PyObject *self, PyObject *args) -- cgit v1.2.3 From 6144310936a4b6bd2920f8c3c83e78d231a1b409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Wed, 23 May 2018 21:59:02 +0100 Subject: Add valgrind targets. --- Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Makefile b/Makefile index 5bff979b..a28bc5ac 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,12 @@ check:: build-inplace gdb-check:: $(MAKE) check DEBUGGER="gdb --args" +valgrind-check-python3: + PYTHONMALLOC=malloc $(MAKE) check PYTHON=python DEBUGGER="valgrind --suppressions=/usr/lib/valgrind/python3.supp" + +valgrind-check-python2: + PYTHONMALLOC=malloc $(MAKE) check PYTHON=python3 DEBUGGER="valgrind --suppressions=/usr/lib/valgrind/python.supp" + check-one:: $(MAKE) check TEST_OPTIONS=-f -- cgit v1.2.3 From f7325341e4c0c6dd60f4926f0907e7e19136f073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Wed, 23 May 2018 21:59:32 +0100 Subject: Pass on full path. --- subvertpy/wc.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 744bca57..062acc32 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "util.h" #include "editor.h" @@ -579,6 +580,9 @@ static PyObject *get_pristine_contents(PyObject *self, PyObject *args) apr_pool_t *stream_pool; svn_stream_t *stream; #else +#if PY_MAJOR_VERSION >= 3 + int fd; +#endif PyObject *ret; const char *pristine_path; #endif @@ -632,7 +636,17 @@ static PyObject *get_pristine_contents(PyObject *self, PyObject *args) if (temp_pool == NULL) return NULL; RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_pristine_copy_path(path, &pristine_path, temp_pool)); +#if PY_MAJOR_VERSION >= 3 + fd = open(pristine_path, O_RDONLY); + if (fd < 0) { + PyErr_SetFromErrno(PyExc_IOError); + apr_pool_destroy(temp_pool); + return NULL; + } + ret = PyFile_FromFd(fd, pristine_path, "rb", -1, NULL, NULL, NULL, true); +#else ret = PyFile_FromString((char *)pristine_path, "rb"); +#endif apr_pool_destroy(temp_pool); return ret; #endif @@ -853,9 +867,9 @@ static PyObject *committed_queue_init(PyTypeObject *self, PyObject *args, PyObje static PyObject *committed_queue_queue(CommittedQueueObject *self, PyObject *args, PyObject *kwargs) { - char *path; + const char *path; PyObject *admobj; - PyObject *py_wcprop_changes = Py_None; + PyObject *py_wcprop_changes = Py_None, *py_path; svn_wc_adm_access_t *adm; bool remove_lock = false, remove_changelist = false; char *md5_digest = NULL, *sha1_digest = NULL; @@ -865,8 +879,8 @@ static PyObject *committed_queue_queue(CommittedQueueObject *self, PyObject *arg int md5_digest_len, sha1_digest_len; char *kwnames[] = { "path", "adm", "recurse", "wcprop_changes", "remove_lock", "remove_changelist", "md5_digest", "sha1_digest", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO!|bObbz#z#", kwnames, - &path, &Adm_Type, &admobj, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO!|bObbz#z#", kwnames, + &py_path, &Adm_Type, &admobj, &recurse, &py_wcprop_changes, &remove_lock, &remove_changelist, &md5_digest, &md5_digest_len, &sha1_digest, &sha1_digest_len)) @@ -881,9 +895,9 @@ static PyObject *committed_queue_queue(CommittedQueueObject *self, PyObject *arg return NULL; } - path = apr_pstrdup(self->pool, path); + path = py_object_to_svn_abspath(py_path, self->pool); if (path == NULL) { - PyErr_NoMemory(); + apr_pool_destroy(temp_pool); return NULL; } -- cgit v1.2.3 From 014a1d84404000a88ba89d93e6b60549ca55412e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Wed, 23 May 2018 21:59:41 +0100 Subject: Fix Python3 compatibility. --- subvertpy/util.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/subvertpy/util.c b/subvertpy/util.c index af16aecd..4cc833b8 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -296,9 +296,7 @@ PyObject *PyErr_NewSubversionException(svn_error_t *error) if (error->child != NULL) { PyTypeObject *cls = PyErr_GetSubversionExceptionTypeObject(); PyObject *args = PyErr_NewSubversionException(error->child); - child = cls->tp_new(cls, args, NULL); - if (cls->tp_init != NULL) - cls->tp_init(child, args, NULL); + child = PyObject_CallObject(cls, args); Py_DECREF(cls); Py_DECREF(args); } else { @@ -307,7 +305,7 @@ PyObject *PyErr_NewSubversionException(svn_error_t *error) } #if ONLY_SINCE_SVN(1, 4) - message = svn_err_best_message(error, buf, sizeof(buf)); + message = svn_err_best_message(error, buf, sizeof(buf)-1); #else message = error->message; #endif -- cgit v1.2.3 From 9d5a27cc6f6354169b910b6e34e64be40e9b102e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Wed, 23 May 2018 22:24:00 +0100 Subject: Fix reference counting. --- subvertpy/util.c | 52 +++++++++++++++++++++++++++++----------------------- subvertpy/wc.c | 11 +++++++++++ subvertpy/wc_adm.c | 7 ++++--- 3 files changed, 44 insertions(+), 26 deletions(-) diff --git a/subvertpy/util.c b/subvertpy/util.c index 4cc833b8..f2d5cd66 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -69,19 +69,20 @@ svn_dirent_canonicalize(const char *dirent, const char *py_object_to_svn_path_or_url(PyObject *obj, apr_pool_t *pool) { const char *ret; - PyObject *bytes_obj = NULL; if (PyUnicode_Check(obj)) { - bytes_obj = obj = PyUnicode_AsUTF8String(obj); + obj = PyUnicode_AsUTF8String(obj); if (obj == NULL) { return NULL; } + } else { + Py_INCREF(obj); } if (!PyBytes_Check(obj)) { PyErr_SetString(PyExc_TypeError, "URIs need to be UTF-8 bytestrings or unicode strings"); - Py_XDECREF(bytes_obj); + Py_DECREF(obj); return NULL; } @@ -92,32 +93,33 @@ const char *py_object_to_svn_path_or_url(PyObject *obj, apr_pool_t *pool) ret = svn_dirent_canonicalize(ret, pool); } - Py_XDECREF(bytes_obj); + Py_DECREF(obj); return ret; } const char *py_object_to_svn_abspath(PyObject *obj, apr_pool_t *pool) { const char *ret; - PyObject *bytes_obj = NULL; if (PyUnicode_Check(obj)) { - bytes_obj = obj = PyUnicode_AsUTF8String(obj); + obj = PyUnicode_AsUTF8String(obj); if (obj == NULL) { return NULL; } + } else { + Py_INCREF(obj); } if (!PyBytes_Check(obj)) { PyErr_SetString(PyExc_TypeError, "URIs need to be UTF-8 bytestrings or unicode strings"); - Py_XDECREF(bytes_obj); + Py_DECREF(obj); return NULL; } ret = PyBytes_AsString(obj); ret = apr_pstrdup(pool, ret); - Py_XDECREF(obj); + Py_DECREF(obj); if (ret == NULL) { return NULL; } @@ -125,7 +127,7 @@ const char *py_object_to_svn_abspath(PyObject *obj, apr_pool_t *pool) return svn_dirent_canonicalize(ret, pool); } else { const char *absolute; - RUN_SVN_WITH_POOL(pool, svn_dirent_get_absolute(&absolute, ret, pool)); + RUN_SVN(svn_dirent_get_absolute(&absolute, ret, pool)) return svn_dirent_canonicalize(absolute, pool); } } @@ -133,23 +135,24 @@ const char *py_object_to_svn_abspath(PyObject *obj, apr_pool_t *pool) const char *py_object_to_svn_dirent(PyObject *obj, apr_pool_t *pool) { const char *ret; - PyObject *bytes_obj = NULL; if (PyUnicode_Check(obj)) { - bytes_obj = obj = PyUnicode_AsUTF8String(obj); + obj = PyUnicode_AsUTF8String(obj); if (obj == NULL) { return NULL; } + } else { + Py_INCREF(obj); } if (PyBytes_Check(obj)) { ret = svn_dirent_canonicalize(PyBytes_AsString(obj), pool); - Py_XDECREF(bytes_obj); + Py_DECREF(obj); return ret; } else { PyErr_SetString(PyExc_TypeError, "URIs need to be UTF-8 bytestrings or unicode strings"); - Py_XDECREF(bytes_obj); + Py_DECREF(obj); return NULL; } } @@ -157,23 +160,24 @@ const char *py_object_to_svn_dirent(PyObject *obj, apr_pool_t *pool) char *py_object_to_svn_string(PyObject *obj, apr_pool_t *pool) { char *ret; - PyObject *bytes_obj = NULL; if (PyUnicode_Check(obj)) { - bytes_obj = obj = PyUnicode_AsUTF8String(obj); + obj = PyUnicode_AsUTF8String(obj); if (obj == NULL) { return NULL; } + } else { + Py_INCREF(obj); } if (PyBytes_Check(obj)) { ret = apr_pstrdup(pool, PyBytes_AsString(obj)); - Py_XDECREF(bytes_obj); + Py_DECREF(obj); return ret; } else { PyErr_SetString(PyExc_TypeError, "URIs need to be UTF-8 bytestrings or unicode strings"); - Py_XDECREF(bytes_obj); + Py_DECREF(obj); return NULL; } } @@ -181,23 +185,24 @@ char *py_object_to_svn_string(PyObject *obj, apr_pool_t *pool) const char *py_object_to_svn_uri(PyObject *obj, apr_pool_t *pool) { const char *ret; - PyObject *bytes_obj = NULL; if (PyUnicode_Check(obj)) { - bytes_obj = obj = PyUnicode_AsUTF8String(obj); + obj = PyUnicode_AsUTF8String(obj); if (obj == NULL) { return NULL; } - } + } else { + Py_INCREF(obj); + } if (PyBytes_Check(obj)) { ret = svn_uri_canonicalize(PyBytes_AsString(obj), pool); - Py_XDECREF(bytes_obj); + Py_DECREF(obj); return ret; } else { PyErr_SetString(PyExc_TypeError, "URIs need to be UTF-8 bytestrings or unicode strings"); - Py_XDECREF(bytes_obj); + Py_DECREF(obj); return NULL; } } @@ -296,7 +301,7 @@ PyObject *PyErr_NewSubversionException(svn_error_t *error) if (error->child != NULL) { PyTypeObject *cls = PyErr_GetSubversionExceptionTypeObject(); PyObject *args = PyErr_NewSubversionException(error->child); - child = PyObject_CallObject(cls, args); + child = PyObject_CallObject((PyObject *)cls, args); Py_DECREF(cls); Py_DECREF(args); } else { @@ -905,6 +910,7 @@ static apr_hash_t *get_default_config(void) pool = Pool(NULL); RUN_SVN_WITH_POOL(pool, svn_config_get_config(&default_config, NULL, pool)); + /* TODO(jelmer): Deal with pool */ initialised = true; } diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 062acc32..1179bcd6 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -1481,9 +1481,20 @@ static PyObject *py_wc_status(PyObject *self, PyObject *args, PyObject *kwargs) } result_pool = Pool(NULL); + if (result_pool == NULL) { + return NULL; + } scratch_pool = Pool(result_pool); + if (scratch_pool == NULL) { + apr_pool_destroy(result_pool); + return NULL; + } path = py_object_to_svn_abspath(py_path, scratch_pool); + if (path == NULL) { + apr_pool_destroy(result_pool); + return NULL; + } RUN_SVN_WITH_POOL(result_pool, svn_wc_status3(&status, context_obj->context, path, diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index 00cc26fb..fec8d675 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -129,21 +129,22 @@ static PyObject *adm_access_path(PyObject *self) static const char *py_object_to_adm_abspath(PyObject *obj, PyObject *adm, apr_pool_t *pool) { const char *ret; - PyObject *bytes_obj = NULL; AdmObject *admobj = (AdmObject *)adm; ADM_CHECK_CLOSED(admobj); if (PyUnicode_Check(obj)) { - bytes_obj = obj = PyUnicode_AsUTF8String(obj); + obj = PyUnicode_AsUTF8String(obj); if (obj == NULL) { return NULL; } + } else { + Py_INCREF(obj); } if (!PyBytes_Check(obj)) { PyErr_SetString(PyExc_TypeError, "URIs need to be UTF-8 bytestrings or unicode strings"); - Py_XDECREF(bytes_obj); + Py_DECREF(obj); return NULL; } -- cgit v1.2.3 From f18306bd36b9cebd3413186be75a4d5b9c4a6f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Wed, 23 May 2018 23:08:52 +0100 Subject: Fix another test. --- subvertpy/client.c | 3 +++ subvertpy/tests/test_client.py | 5 +++-- subvertpy/tests/test_wc.py | 2 +- subvertpy/wc.c | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/subvertpy/client.c b/subvertpy/client.c index e85c5be7..86b587ce 100644 --- a/subvertpy/client.c +++ b/subvertpy/client.c @@ -157,6 +157,9 @@ static bool to_opt_revision(PyObject *arg, svn_opt_revision_t *ret) char *text; if (PyUnicode_Check(arg)) { arg = PyUnicode_AsUTF8String(arg); + if (arg == NULL) { + return false; + } } else { Py_INCREF(arg); } diff --git a/subvertpy/tests/test_client.py b/subvertpy/tests/test_client.py index a1a0534d..b84f86c8 100644 --- a/subvertpy/tests/test_client.py +++ b/subvertpy/tests/test_client.py @@ -217,7 +217,7 @@ class TestClient(SubversionTestCase): self.client.log_msg_func = lambda c: commit_msg_1 self.client.commit(["dc"]) commit_1_dt = datetime.utcnow() - self.client.log(cb, "dc/foo") + self.client.log(cb, "dc/foo", start_rev="HEAD", end_rev=1) self.assertEqual(1, len(log_entries)) self.assertEqual(None, log_entries[0]["changed_paths"]) self.assertEqual(1, log_entries[0]["revision"]) @@ -232,7 +232,8 @@ class TestClient(SubversionTestCase): self.client.commit(["dc"]) commit_2_dt = datetime.utcnow() log_entries = [] - self.client.log(cb, "dc/foo", discover_changed_paths=True) + self.client.log(cb, "dc/foo", start_rev="HEAD", end_rev=1, + discover_changed_paths=True) self.assertEqual(2, len(log_entries)) self.assertLogEntryChangedPathsEquals(["/foo", "/bar"], log_entries[0]) self.assertEqual(2, log_entries[0]["revision"]) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index d1fdde37..6c4f6e62 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -306,7 +306,7 @@ class AdmObjTests(SubversionTestCase): self.client_add('checkout/bar') adm = wc.Adm(None, "checkout", True) cq = wc.CommittedQueue() - cq.queue(os.path.join(self.test_dir, "checkout/bar"), adm) + cq.queue("checkout/bar", adm) adm.process_committed_queue(cq, 1, "2010-05-31T08:49:22.430000Z", "jelmer") bar = adm.entry("checkout/bar") diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 1179bcd6..500ed5dc 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -895,7 +895,7 @@ static PyObject *committed_queue_queue(CommittedQueueObject *self, PyObject *arg return NULL; } - path = py_object_to_svn_abspath(py_path, self->pool); + path = py_object_to_svn_dirent(py_path, self->pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; -- cgit v1.2.3 From 5f2aa6b25cc548e98c55f06572355005d1ec1c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Thu, 24 May 2018 01:07:35 +0100 Subject: Simplify prop conversion handling. --- subvertpy/_ra.c | 23 +++++++++++------------ subvertpy/util.c | 16 ++++------------ 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/subvertpy/_ra.c b/subvertpy/_ra.c index 4f5125d4..f02c5d13 100644 --- a/subvertpy/_ra.c +++ b/subvertpy/_ra.c @@ -1358,11 +1358,6 @@ static PyObject *get_commit_editor(PyObject *self, PyObject *args, PyObject *kwa } } - if (!PyDict_Check(revprops)) { - PyErr_SetString(PyExc_TypeError, "Expected dictionary with revision properties"); - goto fail_prep; - } - if (ra_check_busy(ra)) goto fail_prep; @@ -1371,7 +1366,7 @@ static PyObject *get_commit_editor(PyObject *self, PyObject *args, PyObject *kwa #if ONLY_SINCE_SVN(1, 5) hash_revprops = prop_dict_to_hash(pool, revprops); if (hash_revprops == NULL) { - goto fail_prep2; + goto fail_prep; } Py_BEGIN_ALLOW_THREADS err = svn_ra_get_commit_editor3(ra->ra, &editor, @@ -1379,21 +1374,26 @@ static PyObject *get_commit_editor(PyObject *self, PyObject *args, PyObject *kwa hash_revprops, py_commit_callback, commit_callback, hash_lock_tokens, keep_locks, pool); #else + if (!PyDict_Check(revprops)) { + PyErr_SetString(PyExc_TypeError, "props should be dictionary"); + goto fail_prep; + } + /* Check that revprops has only one member named SVN_PROP_REVISION_LOG */ if (PyDict_Size(revprops) != 1) { PyErr_SetString(PyExc_ValueError, "Only svn:log can be set with Subversion 1.4"); - goto fail_prep2; + goto fail_prep; } py_log_msg = PyDict_GetItemString(revprops, SVN_PROP_REVISION_LOG); if (py_log_msg == NULL) { PyErr_SetString(PyExc_ValueError, "Only svn:log can be set with Subversion 1.4."); - goto fail_prep2; + goto fail_prep; } log_msg = py_object_to_svn_string(py_log_msg, pool); if (log_msg == NULL) { - goto fail_prep2; + goto fail_prep; } Py_BEGIN_ALLOW_THREADS @@ -1407,17 +1407,16 @@ static PyObject *get_commit_editor(PyObject *self, PyObject *args, PyObject *kwa if (err != NULL) { handle_svn_error(err); svn_error_clear(err); - goto fail_prep2; + goto fail_prep; } Py_INCREF(ra); return new_editor_object(NULL, editor, edit_baton, pool, &Editor_Type, ra_done_handler, ra, commit_callback); -fail_prep2: +fail_prep: Py_DECREF(commit_callback); ra->busy = false; -fail_prep: apr_pool_destroy(pool); fail_pool: return NULL; diff --git a/subvertpy/util.c b/subvertpy/util.c index f2d5cd66..f44d5310 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -549,6 +549,7 @@ apr_hash_t *prop_dict_to_hash(apr_pool_t *pool, PyObject *py_props) while (PyDict_Next(py_props, &idx, &k, &v)) { char *key; + Py_ssize_t key_size; if (PyUnicode_Check(k)) { k = PyUnicode_AsUTF8String(k); } else { @@ -568,26 +569,17 @@ apr_hash_t *prop_dict_to_hash(apr_pool_t *pool, PyObject *py_props) Py_INCREF(v); } - if (!PyBytes_Check(v)) { + if (PyBytes_AsStringAndSize(k, &key, &key_size)) { PyErr_SetString(PyExc_TypeError, "property value should be unicode or byte string"); Py_DECREF(k); Py_DECREF(v); return NULL; - } - - key = apr_pmemdup(pool, PyBytes_AsString(k), PyBytes_Size(k)); - if (key == NULL) { - PyErr_SetString(PyExc_TypeError, - "property value should be unicode or byte string"); - Py_DECREF(k); - Py_DECREF(v); - return NULL; - } + } val_string = svn_string_ncreate(PyBytes_AsString(v), PyBytes_Size(v), pool); - apr_hash_set(hash_props, key, PyBytes_Size(k), val_string); + apr_hash_set(hash_props, key, key_size, val_string); Py_DECREF(k); Py_DECREF(v); } -- cgit v1.2.3 From 74ffacf5e0f382dec0d8a9cad071ba4af2986a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Thu, 24 May 2018 12:13:57 +0100 Subject: Paths are unicode. --- subvertpy/_ra.c | 8 ++-- subvertpy/util.c | 137 ++++++++++++++++++++++++++++++++++++------------------- 2 files changed, 93 insertions(+), 52 deletions(-) diff --git a/subvertpy/_ra.c b/subvertpy/_ra.c index f02c5d13..c9bc18b7 100644 --- a/subvertpy/_ra.c +++ b/subvertpy/_ra.c @@ -1522,10 +1522,10 @@ static PyObject *ra_get_dir(PyObject *self, PyObject *args, PyObject *kwargs) py_dirents = Py_None; Py_INCREF(py_dirents); } else { - py_dirents = dirent_hash_to_dict(dirents, dirent_fields, temp_pool); - if (py_dirents == NULL) { - goto fail; - } + py_dirents = dirent_hash_to_dict(dirents, dirent_fields, temp_pool); + if (py_dirents == NULL) { + goto fail; + } } py_props = prop_hash_to_dict(props); diff --git a/subvertpy/util.c b/subvertpy/util.c index f44d5310..f9289718 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -548,8 +548,8 @@ apr_hash_t *prop_dict_to_hash(apr_pool_t *pool, PyObject *py_props) } while (PyDict_Next(py_props, &idx, &k, &v)) { - char *key; - Py_ssize_t key_size; + char *key, *val; + Py_ssize_t key_size, val_size; if (PyUnicode_Check(k)) { k = PyUnicode_AsUTF8String(k); } else { @@ -570,15 +570,22 @@ apr_hash_t *prop_dict_to_hash(apr_pool_t *pool, PyObject *py_props) } if (PyBytes_AsStringAndSize(k, &key, &key_size)) { + PyErr_SetString(PyExc_TypeError, + "property key should be unicode or byte string"); + Py_DECREF(k); + Py_DECREF(v); + return NULL; + } + + if (PyBytes_AsStringAndSize(v, &val, &val_size)) { PyErr_SetString(PyExc_TypeError, "property value should be unicode or byte string"); Py_DECREF(k); Py_DECREF(v); return NULL; - } + } - val_string = svn_string_ncreate(PyBytes_AsString(v), - PyBytes_Size(v), pool); + val_string = svn_string_ncreate(val, val_size, pool); apr_hash_set(hash_props, key, key_size, val_string); Py_DECREF(k); Py_DECREF(v); @@ -588,11 +595,11 @@ apr_hash_t *prop_dict_to_hash(apr_pool_t *pool, PyObject *py_props) } #if PY_MAJOR_VERSION >= 3 -#define SOURCEPATH_FORMAT3 "(Czl)" -#define SOURCEPATH_FORMAT4 "(Czli)" +#define SOURCEPATH_FORMAT3 "(CNl)" +#define SOURCEPATH_FORMAT4 "(CNli)" #else -#define SOURCEPATH_FORMAT3 "(czl)" -#define SOURCEPATH_FORMAT4 "(czli)" +#define SOURCEPATH_FORMAT3 "(cNl)" +#define SOURCEPATH_FORMAT4 "(cNli)" #endif PyObject *pyify_changed_paths(apr_hash_t *changed_paths, bool node_kind, apr_pool_t *pool) @@ -613,13 +620,21 @@ PyObject *pyify_changed_paths(apr_hash_t *changed_paths, bool node_kind, apr_poo } for (idx = apr_hash_first(pool, changed_paths); idx != NULL; idx = apr_hash_next(idx)) { + PyObject *py_copyfrom_path, *py_key; + apr_hash_this(idx, (const void **)&key, &klen, (void **)&val); + if (val->copyfrom_path == NULL) { + py_copyfrom_path = Py_None; + Py_INCREF(Py_None); + } else { + py_copyfrom_path = PyUnicode_FromString(val->copyfrom_path); + } if (node_kind) { - pyval = Py_BuildValue(SOURCEPATH_FORMAT4, val->action, val->copyfrom_path, + pyval = Py_BuildValue(SOURCEPATH_FORMAT4, val->action, py_copyfrom_path, val->copyfrom_rev, svn_node_unknown); } else { - pyval = Py_BuildValue(SOURCEPATH_FORMAT3, val->action, val->copyfrom_path, + pyval = Py_BuildValue(SOURCEPATH_FORMAT3, val->action, py_copyfrom_path, val->copyfrom_rev); } if (pyval == NULL) { @@ -632,11 +647,21 @@ PyObject *pyify_changed_paths(apr_hash_t *changed_paths, bool node_kind, apr_poo Py_DECREF(py_changed_paths); return NULL; } - if (PyDict_SetItemString(py_changed_paths, key, pyval) != 0) { + + py_key = PyUnicode_FromString(key); + if (py_key == NULL) { + Py_DECREF(pyval); + Py_DECREF(py_changed_paths); + return NULL; + } + + if (PyDict_SetItem(py_changed_paths, py_key, pyval) != 0) { Py_DECREF(py_changed_paths); + Py_DECREF(py_key); Py_DECREF(pyval); return NULL; } + Py_DECREF(py_key); Py_DECREF(pyval); } } @@ -663,8 +688,15 @@ PyObject *pyify_changed_paths2(apr_hash_t *changed_paths, apr_pool_t *pool) } for (idx = apr_hash_first(pool, changed_paths); idx != NULL; idx = apr_hash_next(idx)) { + PyObject *py_key, *py_copyfrom_path; apr_hash_this(idx, (const void **)&key, &klen, (void **)&val); - pyval = Py_BuildValue(SOURCEPATH_FORMAT4, val->action, val->copyfrom_path, + if (val->copyfrom_path == NULL) { + py_copyfrom_path = Py_None; + Py_INCREF(Py_None); + } else { + py_copyfrom_path = PyUnicode_FromString(val->copyfrom_path); + } + pyval = Py_BuildValue(SOURCEPATH_FORMAT4, val->action, py_copyfrom_path, val->copyfrom_rev, val->node_kind); if (pyval == NULL) { Py_DECREF(py_changed_paths); @@ -676,11 +708,20 @@ PyObject *pyify_changed_paths2(apr_hash_t *changed_paths, apr_pool_t *pool) Py_DECREF(pyval); return NULL; } - if (PyDict_SetItemString(py_changed_paths, key, pyval) != 0) { + py_key = PyUnicode_FromString(key); + if (py_key == NULL) { + Py_DECREF(py_changed_paths); Py_DECREF(pyval); + return NULL; + } + + if (PyDict_SetItem(py_changed_paths, py_key, pyval) != 0) { + Py_DECREF(pyval); + Py_DECREF(py_key); Py_DECREF(py_changed_paths); return NULL; } + Py_DECREF(py_key); Py_DECREF(pyval); } } @@ -1134,41 +1175,41 @@ PyTypeObject Stream_Type = { PyObject *dirent_hash_to_dict(apr_hash_t *dirents, unsigned int dirent_fields, apr_pool_t *temp_pool) { - svn_dirent_t *dirent; - apr_ssize_t klen; - const char *key; - apr_hash_index_t *idx; - PyObject *py_dirents = PyDict_New(); + svn_dirent_t *dirent; + apr_ssize_t klen; + const char *key; + apr_hash_index_t *idx; + PyObject *py_dirents = PyDict_New(); - if (py_dirents == NULL) { - return NULL; - } - idx = apr_hash_first(temp_pool, dirents); - while (idx != NULL) { - PyObject *item, *pykey; - apr_hash_this(idx, (const void **)&key, &klen, (void **)&dirent); - item = py_dirent(dirent, dirent_fields); - if (item == NULL) { - Py_DECREF(py_dirents); - return NULL; - } - if (key == NULL) { - pykey = Py_None; - Py_INCREF(pykey); - } else { - pykey = PyUnicode_FromStringAndSize(key, klen); - } - if (PyDict_SetItem(py_dirents, pykey, item) != 0) { - Py_DECREF(item); - Py_DECREF(pykey); - Py_DECREF(py_dirents); - return NULL; - } - Py_DECREF(pykey); - Py_DECREF(item); - idx = apr_hash_next(idx); - } - return py_dirents; + if (py_dirents == NULL) { + return NULL; + } + idx = apr_hash_first(temp_pool, dirents); + while (idx != NULL) { + PyObject *item, *pykey; + apr_hash_this(idx, (const void **)&key, &klen, (void **)&dirent); + item = py_dirent(dirent, dirent_fields); + if (item == NULL) { + Py_DECREF(py_dirents); + return NULL; + } + if (key == NULL) { + pykey = Py_None; + Py_INCREF(pykey); + } else { + pykey = PyUnicode_FromStringAndSize(key, klen); + } + if (PyDict_SetItem(py_dirents, pykey, item) != 0) { + Py_DECREF(item); + Py_DECREF(pykey); + Py_DECREF(py_dirents); + return NULL; + } + Py_DECREF(pykey); + Py_DECREF(item); + idx = apr_hash_next(idx); + } + return py_dirents; } svn_lock_t *py_object_to_svn_lock(PyObject *py_lock, apr_pool_t *pool) -- cgit v1.2.3 From c3efe3d8d6c629e4675e876bb57e572df1bbacac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Thu, 24 May 2018 22:00:58 +0100 Subject: Proper lock handling. --- subvertpy/tests/test_wc.py | 8 ++++-- subvertpy/util.c | 10 ------- subvertpy/util.h | 1 - subvertpy/wc.c | 68 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 73 insertions(+), 14 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 6c4f6e62..3a608a72 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -426,15 +426,19 @@ class ContextTests(SubversionTestCase): f.write("modified") self.client_add("checkout/bla.txt") context = wc.Context() - context.add_lock("checkout/bla.txt", ()) - context.remove_lock("checkout/bla.txt") + self.assertEqual((False, False), context.locked("checkout")) + context.add_lock("checkout", ()) + self.assertEqual((True, True), context.locked("checkout")) + context.remove_lock("checkout") def test_add_from_disk(self): self.make_client("repos", "checkout") with open('checkout/bla.txt', 'w') as f: f.write("modified") context = wc.Context() + context.add_lock("checkout", ()) context.add_from_disk('checkout/bla.txt') + context.remove_lock("checkout") def test_get_prop_diffs(self): self.make_client("repos", "checkout") diff --git a/subvertpy/util.c b/subvertpy/util.c index f9289718..c0a9a18e 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -1212,14 +1212,6 @@ PyObject *dirent_hash_to_dict(apr_hash_t *dirents, unsigned int dirent_fields, a return py_dirents; } -svn_lock_t *py_object_to_svn_lock(PyObject *py_lock, apr_pool_t *pool) -{ - svn_lock_t *ret = svn_lock_create(pool); - - /* TODO */ - return ret; -} - PyObject *propchanges_to_list(const apr_array_header_t *propchanges) { int i; @@ -1247,5 +1239,3 @@ PyObject *propchanges_to_list(const apr_array_header_t *propchanges) return py_propchanges; } - - diff --git a/subvertpy/util.h b/subvertpy/util.h index b2bd1d07..b58af954 100644 --- a/subvertpy/util.h +++ b/subvertpy/util.h @@ -160,7 +160,6 @@ const char *py_object_to_svn_relpath(PyObject *obj, apr_pool_t *pool); const char *py_object_to_svn_path_or_url(PyObject *obj, apr_pool_t *pool); char *py_object_to_svn_string(PyObject *obj, apr_pool_t *pool); const char *py_object_to_svn_abspath(PyObject *obj, apr_pool_t *pool); -svn_lock_t *py_object_to_svn_lock(PyObject *py_lock, apr_pool_t *pool); #define py_object_from_svn_abspath PyUnicode_FromString PyObject *propchanges_to_list(const apr_array_header_t *propchanges); diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 500ed5dc..da6511d0 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -35,6 +35,13 @@ #define T_BOOL T_BYTE #endif +typedef struct { + PyObject_HEAD + svn_lock_t *lock; + apr_pool_t *pool; +} LockObject; +extern PyTypeObject Lock_Type; + #if ONLY_BEFORE_SVN(1, 5) struct svn_wc_committed_queue_t { @@ -1598,6 +1605,12 @@ static PyObject *py_wc_walk_status(PyObject *self, PyObject *args, PyObject *kwa Py_RETURN_NONE; } +static svn_lock_t *py_object_to_svn_lock(PyObject *py_lock, apr_pool_t *pool) +{ + LockObject* lockobj = (LockObject *)py_lock; + return lockobj->lock; +} + static PyObject *py_wc_add_lock(PyObject *self, PyObject *args, PyObject *kwargs) { ContextObject *context_obj = (ContextObject *)self; @@ -1607,7 +1620,8 @@ static PyObject *py_wc_add_lock(PyObject *self, PyObject *args, PyObject *kwargs const char *path; apr_pool_t *scratch_pool; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO", kwnames, &py_path, &py_lock)) { + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO!", kwnames, &py_path, &Lock_Type, + &py_lock)) { return NULL; } @@ -1679,6 +1693,9 @@ static PyObject *py_wc_add_from_disk(PyObject *self, PyObject *args, PyObject *k } pool = Pool(NULL); + if (pool == NULL) { + return NULL; + } path = py_object_to_svn_abspath(py_path, pool); if (path == NULL) { @@ -1939,6 +1956,52 @@ static PyTypeObject Context_Type = { #endif +static void lock_dealloc(PyObject *self) +{ + LockObject *lockself = (LockObject *)self; + + apr_pool_destroy(lockself->pool); + + PyObject_Del(self); +} + +static PyObject *lock_init(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + char *kwnames[] = { NULL }; + LockObject *ret; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwnames)) + return NULL; + + ret = PyObject_New(LockObject, &Lock_Type); + if (ret == NULL) + return NULL; + + ret->pool = Pool(NULL); + if (ret->pool == NULL) + return NULL; + ret->lock = svn_lock_create(ret->pool); + + return (PyObject *)ret; +} + +PyTypeObject Lock_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "wc.Lock", /* const char *tp_name; For printing, in format "." */ + sizeof(LockObject), + 0,/* Py_ssize_t tp_basicsize, tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + .tp_dealloc = lock_dealloc, /* destructor tp_dealloc; */ + + .tp_doc = "Lock", /* const char *tp_doc; Documentation string */ + + .tp_methods = NULL, /* struct PyMethodDef *tp_methods; */ + + .tp_new = lock_init, /* tp_new tp_new */ +}; + static PyObject * moduleinit(void) { @@ -1981,6 +2044,9 @@ moduleinit(void) return NULL; #endif + if (PyType_Ready(&Lock_Type) < 0) + return NULL; + apr_initialize(); #if PY_MAJOR_VERSION >= 3 -- cgit v1.2.3 From 99860587e42254d82745769b1a365e7847fe5d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Thu, 24 May 2018 22:02:53 +0100 Subject: Create actual locks in tests, too. --- subvertpy/tests/test_wc.py | 5 +++-- subvertpy/wc.c | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 3a608a72..092fb951 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -426,10 +426,11 @@ class ContextTests(SubversionTestCase): f.write("modified") self.client_add("checkout/bla.txt") context = wc.Context() + lock = wc.Lock() self.assertEqual((False, False), context.locked("checkout")) - context.add_lock("checkout", ()) + context.add_lock("checkout", lock) self.assertEqual((True, True), context.locked("checkout")) - context.remove_lock("checkout") + context.remove_lock("checkout", lock) def test_add_from_disk(self): self.make_client("repos", "checkout") diff --git a/subvertpy/wc.c b/subvertpy/wc.c index da6511d0..16dd0170 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -2125,6 +2125,9 @@ moduleinit(void) PyModule_AddObject(mod, "Adm", (PyObject *)&Adm_Type); Py_INCREF(&Adm_Type); + PyModule_AddObject(mod, "Lock", (PyObject *)&Lock_Type); + Py_INCREF(&Lock_Type); + PyModule_AddObject(mod, "CommittedQueue", (PyObject *)&CommittedQueue_Type); Py_INCREF(&CommittedQueue_Type); -- cgit v1.2.3 From 961279980da225dca83c895c7b3b846c46e7847c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Thu, 24 May 2018 22:24:11 +0100 Subject: Consistently use absolute paths. --- subvertpy/tests/test_wc.py | 2 +- subvertpy/wc.c | 4 +- subvertpy/wc_adm.c | 105 ++++++++++++++------------------------------- 3 files changed, 36 insertions(+), 75 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 092fb951..81c9be66 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -326,7 +326,7 @@ class AdmObjTests(SubversionTestCase): if num != subvertpy.ERR_WC_NOT_WORKING_COPY: raise self.assertEqual( - "checkout", + os.path.abspath("checkout"), adm.probe_try(os.path.join("checkout", "bar")).access_path()) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 16dd0170..02add9be 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -108,7 +108,7 @@ svn_error_t *svn_wc_queue_committed(svn_wc_committed_queue_t **queue, typedef struct { PyObject_VAR_HEAD - apr_pool_t *pool; + apr_pool_t *pool; svn_wc_committed_queue_t *queue; } CommittedQueueObject; @@ -902,7 +902,7 @@ static PyObject *committed_queue_queue(CommittedQueueObject *self, PyObject *arg return NULL; } - path = py_object_to_svn_dirent(py_path, self->pool); + path = py_object_to_svn_abspath(py_path, self->pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index fec8d675..594fa485 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -96,7 +96,7 @@ static PyObject *adm_init(PyTypeObject *self, PyObject *args, PyObject *kwargs) parent_wc = ((AdmObject *)associated)->adm; } - path = py_object_to_svn_dirent(py_path, ret->pool); + path = py_object_to_svn_abspath(py_path, ret->pool); if (path == NULL) { Py_DECREF(ret); return NULL; @@ -109,12 +109,12 @@ static PyObject *adm_init(PyTypeObject *self, PyObject *args, PyObject *kwargs) ret->pool); Py_END_ALLOW_THREADS - if (err != NULL) { - handle_svn_error(err); - svn_error_clear(err); - Py_DECREF(ret); - return NULL; - } + if (err != NULL) { + handle_svn_error(err); + svn_error_clear(err); + Py_DECREF(ret); + return NULL; + } return (PyObject *)ret; } @@ -126,45 +126,6 @@ static PyObject *adm_access_path(PyObject *self) return py_object_from_svn_abspath(svn_wc_adm_access_path(admobj->adm)); } -static const char *py_object_to_adm_abspath(PyObject *obj, PyObject *adm, apr_pool_t *pool) -{ - const char *ret; - AdmObject *admobj = (AdmObject *)adm; - ADM_CHECK_CLOSED(admobj); - - if (PyUnicode_Check(obj)) { - obj = PyUnicode_AsUTF8String(obj); - if (obj == NULL) { - return NULL; - } - } else { - Py_INCREF(obj); - } - - if (!PyBytes_Check(obj)) { - PyErr_SetString(PyExc_TypeError, - "URIs need to be UTF-8 bytestrings or unicode strings"); - Py_DECREF(obj); - return NULL; - } - - ret = PyBytes_AsString(obj); - ret = apr_pstrdup(pool, ret); - Py_XDECREF(obj); - if (ret == NULL) { - return NULL; - } -#if ONLY_SINCE_SVN(1, 7) - if (svn_dirent_is_absolute(ret)) { - return ret; - } else { - return svn_dirent_join(svn_wc_adm_access_path(admobj->adm), ret, pool); - } -#else - return ret; -#endif -} - static PyObject *adm_locked(PyObject *self) { AdmObject *admobj = (AdmObject *)self; @@ -191,7 +152,7 @@ static PyObject *adm_prop_get(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -231,7 +192,7 @@ static PyObject *adm_prop_set(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -319,7 +280,7 @@ static PyObject *adm_walk_entries(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -369,7 +330,7 @@ static PyObject *adm_entry(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -408,7 +369,7 @@ static PyObject *adm_get_prop_diffs(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -452,7 +413,7 @@ static PyObject *adm_add(PyObject *self, PyObject *args, PyObject *kwargs) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -541,7 +502,7 @@ static PyObject *adm_delete(PyObject *self, PyObject *args, PyObject *kwargs) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -598,7 +559,7 @@ static PyObject *adm_crawl_revisions(PyObject *self, PyObject *args, PyObject *k return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -829,7 +790,7 @@ static PyObject *adm_has_binary_prop(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -873,7 +834,7 @@ static PyObject *adm_process_committed(PyObject *self, PyObject *args, PyObject return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -960,7 +921,7 @@ static PyObject *adm_remove_lock(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -992,7 +953,7 @@ static PyObject *get_ancestry(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -1063,7 +1024,7 @@ static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs new_contents = new_py_stream(temp_pool, py_new_contents); - dst_path = py_object_to_svn_dirent(py_dst_path, temp_pool); + dst_path = py_object_to_svn_abspath(py_dst_path, temp_pool); RUN_SVN_WITH_POOL(temp_pool, svn_wc_add_repos_file3(dst_path, admobj->adm, new_base_contents, @@ -1102,7 +1063,7 @@ static PyObject *mark_missing_deleted(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -1160,7 +1121,7 @@ static PyObject *relocate(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -1269,7 +1230,7 @@ static PyObject *adm_text_modified(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -1302,7 +1263,7 @@ static PyObject *adm_props_modified(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -1375,7 +1336,7 @@ static PyObject *is_wc_root(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -1410,7 +1371,7 @@ static PyObject *transmit_text_deltas(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -1459,7 +1420,7 @@ static PyObject *transmit_prop_deltas(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -1493,7 +1454,7 @@ static PyObject *retrieve(PyObject *self, PyObject *args) if (pool == NULL) return NULL; - path = py_object_to_svn_dirent(py_path, pool); + path = py_object_to_svn_abspath(py_path, pool); if (path == NULL) { apr_pool_destroy(pool); return NULL; @@ -1530,7 +1491,7 @@ static PyObject *probe_retrieve(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, pool); + path = py_object_to_svn_abspath(py_path, pool); if (path == NULL) { apr_pool_destroy(pool); return NULL; @@ -1569,7 +1530,7 @@ static PyObject *probe_try(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, pool); + path = py_object_to_svn_abspath(py_path, pool); if (path == NULL) { apr_pool_destroy(pool); return NULL; @@ -1621,7 +1582,7 @@ static PyObject *resolved_conflict(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -1705,7 +1666,7 @@ static PyObject *conflicted(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_svn_dirent(py_path, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; @@ -1754,7 +1715,7 @@ static PyObject *wc_status(PyObject *self, PyObject *args) return NULL; } - path = py_object_to_adm_abspath(py_path, self, temp_pool); + path = py_object_to_svn_abspath(py_path, temp_pool); if (path == NULL) { apr_pool_destroy(temp_pool); return NULL; -- cgit v1.2.3 From 357f9cac281daa5c05bcb7504f9ab53052c2fe67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Thu, 24 May 2018 22:32:19 +0100 Subject: Fix encoding. --- subvertpy/util.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/subvertpy/util.c b/subvertpy/util.c index c0a9a18e..ed77aaaa 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -47,7 +47,18 @@ const char * svn_uri_canonicalize(const char *uri, apr_pool_t *result_pool) { - return svn_path_canonicalize(uri, result_pool); + uri = svn_path_canonicalize(uri, result_pool); + + if (uri == NULL) { + return NULL; + } + + uri = svn_path_uri_decode(uri, result_pool); + if (uri == NULL) { + return NULL; + } + + return svn_path_uri_autoescape(uri, result_pool); } const char * -- cgit v1.2.3 From e3d3db8bf86479a5cc0728a44ea1a81b44207ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 25 May 2018 02:20:24 +0100 Subject: Fix process_committed functions. --- subvertpy/tests/test_wc.py | 65 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 81c9be66..17871ef1 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -302,9 +302,29 @@ class AdmObjTests(SubversionTestCase): if wc.api_version() >= (1, 7): self.skipTest("TODO: doesn't yet work with svn >= 1.7") self.make_client("repos", "checkout") - self.build_tree({"checkout/bar": b"la"}) + self.build_tree({"checkout/bar": b"blala"}) self.client_add('checkout/bar') adm = wc.Adm(None, "checkout", True) + class Editor(object): + """Editor""" + + def __init__(self): + self._windows = [] + + def apply_textdelta(self, checksum): + def window_handler(window): + self._windows.append(window) + return window_handler + + def close(self): + pass + editor = Editor() + (tmpfile, digest) = adm.transmit_text_deltas("checkout/bar", True, editor) + self.assertEqual(editor._windows, + [(0, 0, 5, 0, [(2, 0, 5)], b'blala'), None]) + self.assertIsInstance(tmpfile, str) + self.assertEqual(16, len(digest)) + cq = wc.CommittedQueue() cq.queue("checkout/bar", adm) adm.process_committed_queue(cq, 1, "2010-05-31T08:49:22.430000Z", @@ -312,7 +332,43 @@ class AdmObjTests(SubversionTestCase): bar = adm.entry("checkout/bar") self.assertEqual("bar", bar.name) self.assertEqual(NODE_FILE, bar.kind) - self.assertEqual(wc.SCHEDULE_ADD, bar.schedule) + self.assertEqual(wc.SCHEDULE_NORMAL, bar.schedule) + + def test_process_committed(self): + if wc.api_version() >= (1, 7): + self.skipTest("TODO: doesn't yet work with svn >= 1.7") + self.make_client("repos", ".") + self.build_tree({"bar": b"la"}) + self.client_add('bar') + adm = wc.Adm(None, ".", True) + class Editor(object): + """Editor""" + + def __init__(self): + self._windows = [] + + def apply_textdelta(self, checksum): + def window_handler(window): + self._windows.append(window) + return window_handler + + def close(self): + pass + editor = Editor() + (tmpfile, digest) = adm.transmit_text_deltas("bar", True, editor) + self.assertEqual(editor._windows, + [(0, 0, 2, 0, [(2, 0, 2)], b'la'), None]) + self.assertIsInstance(tmpfile, str) + self.assertEqual(16, len(digest)) + bar = adm.entry("bar") + self.assertEqual(-1, bar.cmt_rev) + self.assertEqual(0, bar.revision) + + adm.process_committed("bar", False, 1, "2010-05-31T08:49:22.430000Z", "jelmer") + bar = adm.entry("bar") + self.assertEqual("bar", bar.name) + self.assertEqual(NODE_FILE, bar.kind) + self.assertEqual(wc.SCHEDULE_NORMAL, bar.schedule) def test_probe_try(self): self.make_client("repos", "checkout") @@ -437,9 +493,10 @@ class ContextTests(SubversionTestCase): with open('checkout/bla.txt', 'w') as f: f.write("modified") context = wc.Context() - context.add_lock("checkout", ()) + lock = wc.Lock() + context.add_lock("checkout", lock) context.add_from_disk('checkout/bla.txt') - context.remove_lock("checkout") + context.remove_lock("checkout", lock) def test_get_prop_diffs(self): self.make_client("repos", "checkout") -- cgit v1.2.3 From b4704c9431c7e667f28f0e2c76c89880bfb10638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 25 May 2018 02:29:22 +0100 Subject: Fix style. --- subvertpy/tests/test_client.py | 3 ++- subvertpy/tests/test_wc.py | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/subvertpy/tests/test_client.py b/subvertpy/tests/test_client.py index b84f86c8..7fa04c6e 100644 --- a/subvertpy/tests/test_client.py +++ b/subvertpy/tests/test_client.py @@ -232,7 +232,8 @@ class TestClient(SubversionTestCase): self.client.commit(["dc"]) commit_2_dt = datetime.utcnow() log_entries = [] - self.client.log(cb, "dc/foo", start_rev="HEAD", end_rev=1, + self.client.log( + cb, "dc/foo", start_rev="HEAD", end_rev=1, discover_changed_paths=True) self.assertEqual(2, len(log_entries)) self.assertLogEntryChangedPathsEquals(["/foo", "/bar"], log_entries[0]) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 17871ef1..8fc6ee87 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -305,6 +305,7 @@ class AdmObjTests(SubversionTestCase): self.build_tree({"checkout/bar": b"blala"}) self.client_add('checkout/bar') adm = wc.Adm(None, "checkout", True) + class Editor(object): """Editor""" @@ -318,8 +319,10 @@ class AdmObjTests(SubversionTestCase): def close(self): pass + editor = Editor() - (tmpfile, digest) = adm.transmit_text_deltas("checkout/bar", True, editor) + (tmpfile, digest) = adm.transmit_text_deltas( + "checkout/bar", True, editor) self.assertEqual(editor._windows, [(0, 0, 5, 0, [(2, 0, 5)], b'blala'), None]) self.assertIsInstance(tmpfile, str) @@ -341,6 +344,7 @@ class AdmObjTests(SubversionTestCase): self.build_tree({"bar": b"la"}) self.client_add('bar') adm = wc.Adm(None, ".", True) + class Editor(object): """Editor""" @@ -364,7 +368,8 @@ class AdmObjTests(SubversionTestCase): self.assertEqual(-1, bar.cmt_rev) self.assertEqual(0, bar.revision) - adm.process_committed("bar", False, 1, "2010-05-31T08:49:22.430000Z", "jelmer") + adm.process_committed( + "bar", False, 1, "2010-05-31T08:49:22.430000Z", "jelmer") bar = adm.entry("bar") self.assertEqual("bar", bar.name) self.assertEqual(NODE_FILE, bar.kind) -- cgit v1.2.3 From 4b6c18b7ef8fd88429c0a7e9aed38fb469ff537f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 25 May 2018 02:50:59 +0100 Subject: Fix path for locks. --- subvertpy/tests/test_wc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 8fc6ee87..3da7b7ea 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -489,9 +489,9 @@ class ContextTests(SubversionTestCase): context = wc.Context() lock = wc.Lock() self.assertEqual((False, False), context.locked("checkout")) - context.add_lock("checkout", lock) + context.add_lock("checkout/", lock) self.assertEqual((True, True), context.locked("checkout")) - context.remove_lock("checkout", lock) + context.remove_lock("checkout/", lock) def test_add_from_disk(self): self.make_client("repos", "checkout") @@ -499,9 +499,9 @@ class ContextTests(SubversionTestCase): f.write("modified") context = wc.Context() lock = wc.Lock() - context.add_lock("checkout", lock) + context.add_lock("checkout/", lock) context.add_from_disk('checkout/bla.txt') - context.remove_lock("checkout", lock) + context.remove_lock("checkout/", lock) def test_get_prop_diffs(self): self.make_client("repos", "checkout") -- cgit v1.2.3 From 8680e3008e8b97d62a406eaf550931ccba9362c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 25 May 2018 03:33:59 +0100 Subject: Prevent recursion. --- subvertpy/util.c | 2 +- subvertpy/util.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/subvertpy/util.c b/subvertpy/util.c index ed77aaaa..ef52cacd 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -44,7 +44,7 @@ void PyErr_SetAprStatus(apr_status_t status) #if ONLY_BEFORE_SVN(1, 7) const char * -svn_uri_canonicalize(const char *uri, +_svn_uri_canonicalize(const char *uri, apr_pool_t *result_pool) { uri = svn_path_canonicalize(uri, result_pool); diff --git a/subvertpy/util.h b/subvertpy/util.h index b58af954..3ed0545b 100644 --- a/subvertpy/util.h +++ b/subvertpy/util.h @@ -141,7 +141,7 @@ extern PyTypeObject Stream_Type; #if ONLY_BEFORE_SVN(1, 7) const char * -svn_uri_canonicalize(const char *uri, +_svn_uri_canonicalize(const char *uri, apr_pool_t *result_pool); const char * svn_relpath_canonicalize(const char *relpath, @@ -150,6 +150,7 @@ svn_relpath_canonicalize(const char *relpath, const char * svn_dirent_canonicalize(const char *dirent, apr_pool_t *result_pool); +#define svn_uri_canonicalize _svn_uri_canonicalize #define svn_dirent_get_absolute svn_path_get_absolute #define svn_dirent_is_absolute svn_path_is_url #endif -- cgit v1.2.3 From 8c0f2660829c69cce5fb24af54fa193c81d094dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 25 May 2018 18:54:21 +0100 Subject: Calculate rather than hardcode md5sum. --- subvertpy/tests/test_wc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 3da7b7ea..dc08693d 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -15,6 +15,7 @@ """Subversion ra library tests.""" +import hashlib from io import BytesIO import os @@ -294,7 +295,7 @@ class AdmObjTests(SubversionTestCase): self.assertEqual("bar", bar.name) self.assertEqual(NODE_FILE, bar.kind) self.assertEqual(wc.SCHEDULE_NORMAL, bar.schedule) - self.assertEqual('52419dba51d65210e87bf52dc1072145', bar.checksum) + self.assertEqual(hashlib.md5('blala').hexdigest(), bar.checksum) self.assertEqual(1, bar.cmt_rev) self.assertEqual(1, bar.revision) -- cgit v1.2.3 From 9d773ea55724f0b1490842571d829f08d8c1dff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 25 May 2018 18:58:07 +0100 Subject: Check for -1. --- subvertpy/util.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/subvertpy/util.c b/subvertpy/util.c index ef52cacd..8fe7407a 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -580,7 +580,7 @@ apr_hash_t *prop_dict_to_hash(apr_pool_t *pool, PyObject *py_props) Py_INCREF(v); } - if (PyBytes_AsStringAndSize(k, &key, &key_size)) { + if (PyBytes_AsStringAndSize(k, &key, &key_size) == -1) { PyErr_SetString(PyExc_TypeError, "property key should be unicode or byte string"); Py_DECREF(k); @@ -588,7 +588,7 @@ apr_hash_t *prop_dict_to_hash(apr_pool_t *pool, PyObject *py_props) return NULL; } - if (PyBytes_AsStringAndSize(v, &val, &val_size)) { + if (PyBytes_AsStringAndSize(v, &val, &val_size) == -1) { PyErr_SetString(PyExc_TypeError, "property value should be unicode or byte string"); Py_DECREF(k); -- cgit v1.2.3 From 6b6a55057f6e79817fcefd64b6108192bc3d1b48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 25 May 2018 19:03:38 +0100 Subject: Consider checksum optional. --- subvertpy/tests/test_wc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index dc08693d..4350a828 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -295,7 +295,7 @@ class AdmObjTests(SubversionTestCase): self.assertEqual("bar", bar.name) self.assertEqual(NODE_FILE, bar.kind) self.assertEqual(wc.SCHEDULE_NORMAL, bar.schedule) - self.assertEqual(hashlib.md5('blala').hexdigest(), bar.checksum) + self.assertIn(bar.checksum, (None, hashlib.md5('blala').hexdigest())) self.assertEqual(1, bar.cmt_rev) self.assertEqual(1, bar.revision) -- cgit v1.2.3 From da5e0cf9b5240fe127aefc71b00e19ab41d659cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 25 May 2018 20:30:39 +0100 Subject: Pass in absolute paths to translated_stream. --- subvertpy/wc_adm.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index 594fa485..fceb2bb5 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -1179,18 +1179,31 @@ static PyObject *translated_stream(PyObject *self, PyObject *args) svn_stream_t *stream; AdmObject *admobj = (AdmObject *)self; apr_pool_t *stream_pool; + PyObject *py_path, *py_versioned_file; int flags; - if (!PyArg_ParseTuple(args, "ssi", &path, &versioned_file, &flags)) + if (!PyArg_ParseTuple(args, "OOi", &py_path, &py_versioned_file, &flags)) return NULL; +#if ONLY_SINCE_SVN(1, 5) ADM_CHECK_CLOSED(admobj); -#if ONLY_SINCE_SVN(1, 5) stream_pool = Pool(NULL); if (stream_pool == NULL) return NULL; + path = py_object_to_svn_abspath(py_path, stream_pool); + if (path == NULL) { + apr_pool_destroy(stream_pool); + return NULL; + } + + versioned_file = py_object_to_svn_abspath(py_versioned_file, stream_pool); + if (versioned_file == NULL) { + apr_pool_destroy(stream_pool); + return NULL; + } + RUN_SVN_WITH_POOL(stream_pool, svn_wc_translated_stream(&stream, path, versioned_file, admobj->adm, flags, stream_pool)); -- cgit v1.2.3 From 22a6d1e0ad1d9013d809a2bf9c1adefbb65f09f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 25 May 2018 20:33:29 +0100 Subject: Const. --- subvertpy/wc_adm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index fceb2bb5..edea6d8d 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -1174,7 +1174,7 @@ static PyObject *crop_tree(PyObject *self, PyObject *args) static PyObject *translated_stream(PyObject *self, PyObject *args) { - char *path, *versioned_file; + const char *path, *versioned_file; StreamObject *ret; svn_stream_t *stream; AdmObject *admobj = (AdmObject *)self; -- cgit v1.2.3 From 6ff9947db9515cf7facae1db9c684fd1a0cbe14d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 25 May 2018 21:05:40 +0100 Subject: Python3 portability fixes. --- subvertpy/_ra.c | 9 +++++---- subvertpy/tests/test_wc.py | 11 ++++------- subvertpy/util.c | 32 +++++++------------------------- subvertpy/wc_adm.c | 2 +- 4 files changed, 17 insertions(+), 37 deletions(-) diff --git a/subvertpy/_ra.c b/subvertpy/_ra.c index c9bc18b7..3fb1b52c 100644 --- a/subvertpy/_ra.c +++ b/subvertpy/_ra.c @@ -1346,15 +1346,16 @@ static PyObject *get_commit_editor(PyObject *self, PyObject *args, PyObject *kwa hash_lock_tokens = NULL; } else { Py_ssize_t idx = 0; + char *key, *val; PyObject *k, *v; hash_lock_tokens = apr_hash_make(pool); while (PyDict_Next(lock_tokens, &idx, &k, &v)) { - if (!PyBytes_Check(k)) { - PyErr_SetString(PyExc_TypeError, "token not bytes"); + key = py_object_to_svn_string(k, pool); + if (key == NULL) { goto fail_prep; } - apr_hash_set(hash_lock_tokens, PyBytes_AsString(k), - PyBytes_Size(k), PyBytes_AsString(v)); + val = apr_pmemdup(pool, PyBytes_AsString(v), PyBytes_Size(v)); + apr_hash_set(hash_lock_tokens, key, strlen(key), val); } } diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 4350a828..62cd675b 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -161,12 +161,9 @@ class AdmObjTests(SubversionTestCase): self.client_set_prop("checkout/bar", "svn:keywords", "Id\n") self.client_commit("checkout", "foo") adm = wc.Adm(None, "checkout", True) - path = os.path.join(self.test_dir, "checkout/bar") - stream = adm.translated_stream(path, path, wc.TRANSLATE_TO_NF) - if wc.api_version() < (1, 6): - self.assertRaises(NotImplementedError, stream.read) - else: - self.assertTrue(stream.read().startswith(b"My id: $Id: ")) + stream = adm.translated_stream('checkout/bar', 'checkout/bar', wc.TRANSLATE_TO_NF) + body = stream.read() + self.assertTrue(body.startswith(b"My id: $Id: "), body) def test_text_modified(self): self.make_client("repos", "checkout") @@ -295,7 +292,7 @@ class AdmObjTests(SubversionTestCase): self.assertEqual("bar", bar.name) self.assertEqual(NODE_FILE, bar.kind) self.assertEqual(wc.SCHEDULE_NORMAL, bar.schedule) - self.assertIn(bar.checksum, (None, hashlib.md5('blala').hexdigest())) + self.assertIn(bar.checksum, (None, hashlib.md5(b'blala').hexdigest())) self.assertEqual(1, bar.cmt_rev) self.assertEqual(1, bar.revision) diff --git a/subvertpy/util.c b/subvertpy/util.c index 8fe7407a..715f9c82 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -522,9 +522,7 @@ PyObject *prop_hash_to_dict(apr_hash_t *props) if (PyDict_SetItem(py_props, py_key, py_val) != 0) { Py_DECREF(py_key); Py_DECREF(py_val); - Py_DECREF(py_props); - apr_pool_destroy(pool); - return NULL; + goto fail_item; } Py_DECREF(py_key); Py_DECREF(py_val); @@ -560,17 +558,10 @@ apr_hash_t *prop_dict_to_hash(apr_pool_t *pool, PyObject *py_props) while (PyDict_Next(py_props, &idx, &k, &v)) { char *key, *val; - Py_ssize_t key_size, val_size; - if (PyUnicode_Check(k)) { - k = PyUnicode_AsUTF8String(k); - } else { - Py_INCREF(k); - } + Py_ssize_t val_size; - if (!PyBytes_Check(k)) { - PyErr_SetString(PyExc_TypeError, - "property name should be unicode or byte string"); - Py_DECREF(k); + key = py_object_to_svn_string(k, pool); + if (key == NULL) { return NULL; } @@ -580,26 +571,17 @@ apr_hash_t *prop_dict_to_hash(apr_pool_t *pool, PyObject *py_props) Py_INCREF(v); } - if (PyBytes_AsStringAndSize(k, &key, &key_size) == -1) { - PyErr_SetString(PyExc_TypeError, - "property key should be unicode or byte string"); - Py_DECREF(k); - Py_DECREF(v); - return NULL; - } - if (PyBytes_AsStringAndSize(v, &val, &val_size) == -1) { PyErr_SetString(PyExc_TypeError, "property value should be unicode or byte string"); - Py_DECREF(k); - Py_DECREF(v); return NULL; } val_string = svn_string_ncreate(val, val_size, pool); - apr_hash_set(hash_props, key, key_size, val_string); - Py_DECREF(k); + Py_DECREF(v); + + apr_hash_set(hash_props, key, strlen(key), val_string); } return hash_props; diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index edea6d8d..6ced3c1b 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -1780,7 +1780,7 @@ static PyMethodDef adm_methods[] = { { "get_ancestry", (PyCFunction)get_ancestry, METH_VARARGS, "S.get_ancestry(path) -> (url, rev)" }, { "maybe_set_repos_root", (PyCFunction)maybe_set_repos_root, METH_VARARGS, "S.maybe_set_repos_root(path, repos)" }, - { "add_repos_file", (PyCFunction)add_repos_file, METH_KEYWORDS, + { "add_repos_file", (PyCFunction)add_repos_file, METH_KEYWORDS|METH_VARARGS, "S.add_repos_file(dst_path, new_base_contents, new_contents, new_base_props, new_props, copyfrom_url=None, copyfrom_rev=-1, notify_func=None)" }, { "mark_missing_deleted", (PyCFunction)mark_missing_deleted, METH_VARARGS, "S.mark_missing_deleted(path)" }, -- cgit v1.2.3 From b60e9f91b5eb9319da7ab275306dcf671c43a67b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 25 May 2018 21:06:12 +0100 Subject: Disable translate_stream for now. --- subvertpy/tests/test_wc.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 62cd675b..5e5be9a0 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -153,8 +153,7 @@ class AdmObjTests(SubversionTestCase): adm.relocate("checkout", "file://", "http://") def test_translated_stream(self): - if wc.api_version() >= (1, 7): - self.skipTest("TODO: doesn't yet work with svn >= 1.7") + self.skipTest("TODO: doesn't yet work") self.make_client("repos", "checkout") self.build_tree({"checkout/bar": b"My id: $Id$"}) self.client_add('checkout/bar') -- cgit v1.2.3 From 8e95c7bdc05b3a327f0105abe19178bfccce396d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 25 May 2018 21:30:08 +0100 Subject: Fix style. --- subvertpy/tests/test_wc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 5e5be9a0..5a31509e 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -160,7 +160,8 @@ class AdmObjTests(SubversionTestCase): self.client_set_prop("checkout/bar", "svn:keywords", "Id\n") self.client_commit("checkout", "foo") adm = wc.Adm(None, "checkout", True) - stream = adm.translated_stream('checkout/bar', 'checkout/bar', wc.TRANSLATE_TO_NF) + stream = adm.translated_stream( + 'checkout/bar', 'checkout/bar', wc.TRANSLATE_TO_NF) body = stream.read() self.assertTrue(body.startswith(b"My id: $Id: "), body) -- cgit v1.2.3 From 5225ac58ad0d7348f58a25f5104d16c56cbea40a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 26 May 2018 00:10:49 +0100 Subject: Skip lock tests for now. --- subvertpy/tests/test_wc.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 5a31509e..69eeb059 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -480,6 +480,7 @@ class ContextTests(SubversionTestCase): os.path.abspath("checkout/bla.txt")}) def test_locking(self): + self.skipTest('TODO: locking does not work yet') self.make_client("repos", "checkout") with open('checkout/bla.txt', 'w') as f: f.write("modified") @@ -492,6 +493,7 @@ class ContextTests(SubversionTestCase): context.remove_lock("checkout/", lock) def test_add_from_disk(self): + self.skipTest('TODO: locking does not work yet') self.make_client("repos", "checkout") with open('checkout/bla.txt', 'w') as f: f.write("modified") -- cgit v1.2.3 From 458a88fc02f43509881d85300654e6c7eaaafaa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 26 May 2018 03:13:12 +0100 Subject: Add lock/unlock. --- subvertpy/client.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/subvertpy/client.c b/subvertpy/client.c index 86b587ce..30f804e6 100644 --- a/subvertpy/client.c +++ b/subvertpy/client.c @@ -2009,6 +2009,71 @@ static PyObject *client_info(PyObject *self, PyObject *args, PyObject *kwargs) return entry_dict; } +static PyObject *client_lock(PyObject *self, PyObject *args, PyObject *kwargs) +{ + char *kwnames[] = { + "targets", "comment", "steal_lock", + NULL, + }; + apr_array_header_t *targets; + apr_pool_t *temp_pool; + char *comment; + bool steal_lock = false; + ClientObject *client = (ClientObject *)self; + PyObject *py_targets; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|zb", kwnames, + &py_targets, &comment, &steal_lock)) + return NULL; + + temp_pool = Pool(NULL); + if (temp_pool == NULL) + return NULL; + + if (!client_list_to_apr_array(temp_pool, py_targets, py_object_to_svn_path_or_url, &targets)) { + apr_pool_destroy(temp_pool); + return NULL; + } + + RUN_SVN_WITH_POOL(temp_pool, svn_client_lock(targets, comment, steal_lock, client->client, temp_pool)); + + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + +static PyObject *client_unlock(PyObject *self, PyObject *args, PyObject *kwargs) +{ + char *kwnames[] = { + "targets", "break_lock", + NULL, + }; + apr_array_header_t *targets; + apr_pool_t *temp_pool; + bool break_lock = false; + ClientObject *client = (ClientObject *)self; + PyObject *py_targets; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|b", kwnames, + &py_targets, &break_lock)) + return NULL; + + temp_pool = Pool(NULL); + if (temp_pool == NULL) + return NULL; + + if (!client_list_to_apr_array(temp_pool, py_targets, py_object_to_svn_path_or_url, &targets)) { + apr_pool_destroy(temp_pool); + return NULL; + } + + RUN_SVN_WITH_POOL(temp_pool, svn_client_unlock(targets, break_lock, client->client, temp_pool)); + + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + static PyMethodDef client_methods[] = { { "add", (PyCFunction)client_add, METH_VARARGS|METH_KEYWORDS, "S.add(path, recursive=True, force=False, no_ignore=False, no_autoprops=False)" }, @@ -2033,6 +2098,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, + "S.lock(targets, comment, steal_lock=False)" }, + { "unlock", (PyCFunction)client_unlock, METH_VARARGS, + "S.lock(targets, break_lock=False)" }, { NULL, } }; -- cgit v1.2.3 From 4072ba6c55769e07abfcd9544670cb2e9e292112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 26 May 2018 13:27:35 +0100 Subject: Add .client_lock and .client_unlock --- subvertpy/tests/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/subvertpy/tests/__init__.py b/subvertpy/tests/__init__.py index 30dfdee2..c635b2a0 100644 --- a/subvertpy/tests/__init__.py +++ b/subvertpy/tests/__init__.py @@ -305,6 +305,12 @@ class SubversionTestCase(TestCaseInTempDir): """Resolve a conflict set on a local path.""" self.client_ctx.resolve(path, depth, choice) + def client_lock(self, path, comment="A comment", steal_lock=False): + self.client_ctx.lock(path, comment, steal_lock) + + def client_unlock(self, path, steal_lock=False): + self.client_ctx.unlock(path, steal_lock) + def client_commit(self, dir, message=None, recursive=True): """Commit current changes in specified working copy. -- cgit v1.2.3 From fb4b8a7b0a6b9d18012932c264553a5475c16ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 26 May 2018 13:55:45 +0100 Subject: More locking improvements. --- subvertpy/tests/test_wc.py | 68 ++++++++++++--- subvertpy/wc.c | 213 +++++++++++++++++++++++++++++++++++---------- subvertpy/wc.h | 2 + subvertpy/wc_adm.c | 39 +++++++++ 4 files changed, 260 insertions(+), 62 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 69eeb059..394c6082 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -117,14 +117,14 @@ class AdmObjTests(SubversionTestCase): adm.close() def test_add_repos_file(self): - if wc.api_version() >= (1, 7): - self.skipTest("TODO: doesn't yet work with svn >= 1.7") if wc.api_version() < (1, 6): self.skipTest("doesn't work with svn < 1.6") self.make_client("repos", "checkout") adm = wc.Adm(None, "checkout", True) adm.add_repos_file("checkout/bar", BytesIO(b"basecontents"), BytesIO(b"contents"), {}, {}) + if wc.api_version() >= (1, 7): + self.skipTest("TODO: doesn't yet work with svn >= 1.7") self.assertEqual(b"basecontents", wc.get_pristine_contents("checkout/bar").read()) @@ -265,6 +265,10 @@ class AdmObjTests(SubversionTestCase): def __init__(self): self._windows = [] + self._prop = {} + + def change_prop(self, name, value): + self._prop[name] = value def apply_textdelta(self, checksum): def window_handler(window): @@ -274,18 +278,21 @@ class AdmObjTests(SubversionTestCase): def close(self): pass editor = Editor() - (tmpfile, digest) = adm.transmit_text_deltas("bar", True, editor) + (tmpfile, md5_digest) = adm.transmit_text_deltas("bar", True, editor) self.assertEqual(editor._windows, [(0, 0, 5, 0, [(2, 0, 5)], b'blala'), None]) self.assertIsInstance(tmpfile, str) - self.assertEqual(16, len(digest)) + self.assertEqual(16, len(md5_digest)) + self.assertEqual(hashlib.md5(b'blala').digest(), md5_digest) bar = adm.entry("bar") self.assertEqual(-1, bar.cmt_rev) self.assertEqual(0, bar.revision) + self.assertIn(bar.checksum, (None, hashlib.md5(b'blala').hexdigest())) cq = wc.CommittedQueue() - cq.queue("bar", adm) + cq.queue("bar", adm, wcprop_changes=editor._prop, + md5_digest=md5_digest) adm.process_committed_queue(cq, 1, "2010-05-31T08:49:22.430000Z", "jelmer") bar = adm.entry("bar") @@ -309,6 +316,10 @@ class AdmObjTests(SubversionTestCase): def __init__(self): self._windows = [] + self._prop = {} + + def change_prop(self, name, value): + self._prop[name] = value def apply_textdelta(self, checksum): def window_handler(window): @@ -319,15 +330,17 @@ class AdmObjTests(SubversionTestCase): pass editor = Editor() - (tmpfile, digest) = adm.transmit_text_deltas( + (tmpfile, md5_digest) = adm.transmit_text_deltas( "checkout/bar", True, editor) self.assertEqual(editor._windows, [(0, 0, 5, 0, [(2, 0, 5)], b'blala'), None]) self.assertIsInstance(tmpfile, str) - self.assertEqual(16, len(digest)) + self.assertEqual(16, len(md5_digest)) + self.assertEqual(hashlib.md5(b'blala').digest(), md5_digest) cq = wc.CommittedQueue() - cq.queue("checkout/bar", adm) + cq.queue("checkout/bar", adm, wcprop_changes=editor._prop, + md5_digest=md5_digest) adm.process_committed_queue(cq, 1, "2010-05-31T08:49:22.430000Z", "jelmer") bar = adm.entry("checkout/bar") @@ -362,6 +375,7 @@ class AdmObjTests(SubversionTestCase): [(0, 0, 2, 0, [(2, 0, 2)], b'la'), None]) self.assertIsInstance(tmpfile, str) self.assertEqual(16, len(digest)) + self.assertEqual(hashlib.md5(b'blala').digest(), digest) bar = adm.entry("bar") self.assertEqual(-1, bar.cmt_rev) self.assertEqual(0, bar.revision) @@ -388,6 +402,16 @@ class AdmObjTests(SubversionTestCase): os.path.abspath("checkout"), adm.probe_try(os.path.join("checkout", "bar")).access_path()) + def test_lock(self): + self.make_client("repos", "checkout") + self.build_tree({"checkout/bar": b"la"}) + self.client_add('checkout/bar') + adm = wc.Adm(None, "checkout", True) + lock = wc.Lock() + lock.token = b"blah" + adm.add_lock("checkout", lock) + adm.remove_lock("checkout") + class ContextTests(SubversionTestCase): @@ -480,28 +504,31 @@ class ContextTests(SubversionTestCase): os.path.abspath("checkout/bla.txt")}) def test_locking(self): - self.skipTest('TODO: locking does not work yet') + if wc.api_version() >= (1, 7): + self.skipTest("TODO: doesn't yet work with svn >= 1.7") self.make_client("repos", "checkout") with open('checkout/bla.txt', 'w') as f: f.write("modified") self.client_add("checkout/bla.txt") context = wc.Context() - lock = wc.Lock() + lock = wc.Lock(token=b'foo') self.assertEqual((False, False), context.locked("checkout")) context.add_lock("checkout/", lock) self.assertEqual((True, True), context.locked("checkout")) context.remove_lock("checkout/", lock) def test_add_from_disk(self): - self.skipTest('TODO: locking does not work yet') + if wc.api_version() >= (1, 7): + self.skipTest("TODO: doesn't yet work with svn >= 1.7") self.make_client("repos", "checkout") with open('checkout/bla.txt', 'w') as f: f.write("modified") context = wc.Context() - lock = wc.Lock() - context.add_lock("checkout/", lock) + lock = wc.Lock(token=b'foo') + lock.path = os.path.abspath('checkout')+"/" + context.add_lock("checkout", lock) context.add_from_disk('checkout/bla.txt') - context.remove_lock("checkout/", lock) + context.remove_lock("checkout", lock) def test_get_prop_diffs(self): self.make_client("repos", "checkout") @@ -509,3 +536,16 @@ class ContextTests(SubversionTestCase): (orig_props, propdelta) = context.get_prop_diffs("checkout") self.assertEqual({}, orig_props) self.assertEqual([], propdelta) + + def test_process_committed_queue(self): + if wc.api_version() >= (1, 7): + self.skipTest("TODO: doesn't yet work with svn >= 1.7") + self.make_client("repos", "checkout") + adm = wc.Context() + self.build_tree({"checkout/bar": b"blala"}) + self.client_add('checkout/bar') + adm.add_lock("checkout", wc.Lock(token=b'foo')) + cq = wc.CommittedQueue() + cq.queue("checkout/bar", adm) + adm.process_committed_queue(cq, 1, "2010-05-31T08:49:22.430000Z", + "jelmer") diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 02add9be..4fb3b0c9 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -37,10 +37,18 @@ typedef struct { PyObject_HEAD - svn_lock_t *lock; + svn_lock_t lock; apr_pool_t *pool; } LockObject; -extern PyTypeObject Lock_Type; + +#if ONLY_SINCE_SVN(1, 7) +typedef struct { + PyObject_VAR_HEAD + apr_pool_t *pool; + svn_wc_context_t *context; +} ContextObject; +static PyTypeObject Context_Type; +#endif #if ONLY_BEFORE_SVN(1, 5) struct svn_wc_committed_queue_t @@ -877,46 +885,36 @@ static PyObject *committed_queue_queue(CommittedQueueObject *self, PyObject *arg const char *path; PyObject *admobj; PyObject *py_wcprop_changes = Py_None, *py_path; - svn_wc_adm_access_t *adm; + svn_wc_adm_access_t *adm = NULL; bool remove_lock = false, remove_changelist = false; char *md5_digest = NULL, *sha1_digest = NULL; bool recurse = false; - apr_pool_t *temp_pool; apr_array_header_t *wcprop_changes; int md5_digest_len, sha1_digest_len; +#if ONLY_SINCE_SVN(1, 7) + svn_wc_context_t *context = NULL; +#endif char *kwnames[] = { "path", "adm", "recurse", "wcprop_changes", "remove_lock", "remove_changelist", "md5_digest", "sha1_digest", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO!|bObbz#z#", kwnames, - &py_path, &Adm_Type, &admobj, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bObbz#z#", kwnames, + &py_path, &admobj, &recurse, &py_wcprop_changes, &remove_lock, &remove_changelist, &md5_digest, &md5_digest_len, &sha1_digest, &sha1_digest_len)) return NULL; - temp_pool = Pool(NULL); - if (temp_pool == NULL) - return NULL; - if (!py_dict_to_wcprop_changes(py_wcprop_changes, self->pool, &wcprop_changes)) { - apr_pool_destroy(temp_pool); return NULL; } path = py_object_to_svn_abspath(py_path, self->pool); if (path == NULL) { - apr_pool_destroy(temp_pool); return NULL; } if (md5_digest != NULL) { if (md5_digest_len != APR_MD5_DIGESTSIZE) { PyErr_SetString(PyExc_ValueError, "Invalid size for md5 digest"); - apr_pool_destroy(temp_pool); - return NULL; - } - md5_digest = apr_pstrdup(temp_pool, md5_digest); - if (md5_digest == NULL) { - PyErr_NoMemory(); return NULL; } } @@ -924,44 +922,65 @@ static PyObject *committed_queue_queue(CommittedQueueObject *self, PyObject *arg if (sha1_digest != NULL) { if (sha1_digest_len != APR_SHA1_DIGESTSIZE) { PyErr_SetString(PyExc_ValueError, "Invalid size for sha1 digest"); - apr_pool_destroy(temp_pool); - return NULL; - } - sha1_digest = apr_pstrdup(temp_pool, sha1_digest); - if (sha1_digest == NULL) { - PyErr_NoMemory(); return NULL; } } - adm = PyObject_GetAdmAccess(admobj); + if (PyObject_IsInstance(admobj, (PyObject *)&Adm_Type)) { + adm = PyObject_GetAdmAccess(admobj); +#if ONLY_SINCE_SVN(1, 7) + } else if (PyObject_IsInstance(admobj, (PyObject *)&Context_Type)) { + context = ((ContextObject*)admobj)->context; +#endif + } else { + PyErr_SetString(PyExc_TypeError, "Second arguments needs to be Adm or Context"); + return NULL; + } +#if ONLY_SINCE_SVN(1, 7) + if (adm != NULL) { +#endif #if ONLY_SINCE_SVN(1, 6) { - svn_checksum_t svn_checksum, *svn_checksum_p = &svn_checksum; + svn_checksum_t *svn_checksum_p; - if (sha1_digest != NULL) { - svn_checksum.digest = (unsigned char *)sha1_digest; - svn_checksum.kind = svn_checksum_sha1; - } else if (md5_digest != NULL) { - svn_checksum.digest = (unsigned char *)md5_digest; - svn_checksum.kind = svn_checksum_md5; + if (md5_digest != NULL) { + svn_checksum_p = apr_palloc(self->pool, sizeof(svn_checksum_t)); + svn_checksum_p->digest = apr_pmemdup( + self->pool, (unsigned char *)md5_digest, APR_MD5_DIGESTSIZE); + svn_checksum_p->kind = svn_checksum_md5; } else { svn_checksum_p = NULL; } - RUN_SVN_WITH_POOL(temp_pool, + RUN_SVN( svn_wc_queue_committed2(self->queue, path, adm, recurse?TRUE:FALSE, wcprop_changes, remove_lock?TRUE:FALSE, remove_changelist?TRUE:FALSE, - svn_checksum_p, temp_pool)); + svn_checksum_p, self->pool)); } #else - RUN_SVN_WITH_POOL(temp_pool, + RUN_SVN( svn_wc_queue_committed(&self->queue, path, adm, recurse?TRUE:FALSE, wcprop_changes, remove_lock?TRUE:FALSE, remove_changelist?TRUE:FALSE, - (unsigned char *)md5_digest, temp_pool)); + (unsigned char *)md5_digest, self->pool)); #endif +#if ONLY_SINCE_SVN(1, 7) + } else { + svn_checksum_t *svn_checksum_p; - apr_pool_destroy(temp_pool); + if (sha1_digest != NULL) { + svn_checksum_p = apr_palloc(self->pool, sizeof(svn_checksum_t)); + svn_checksum_p->digest = apr_pmemdup( + self->pool, (unsigned char *)sha1_digest, APR_SHA1_DIGESTSIZE); + svn_checksum_p->kind = svn_checksum_sha1; + } else { + svn_checksum_p = NULL; + } + RUN_SVN( + svn_wc_queue_committed3(self->queue, context, path, recurse?TRUE:FALSE, + wcprop_changes, remove_lock?TRUE:FALSE, remove_changelist?TRUE:FALSE, + svn_checksum_p, self->pool)); + } +#endif Py_RETURN_NONE; } @@ -1045,12 +1064,6 @@ PyTypeObject CommittedQueue_Type = { #if ONLY_SINCE_SVN(1, 7) static PyTypeObject Context_Type; -typedef struct { - PyObject_VAR_HEAD - apr_pool_t *pool; - svn_wc_context_t *context; -} ContextObject; - static PyObject *py_wc_context_locked(PyObject *self, PyObject *args) { PyObject* py_path; @@ -1605,10 +1618,14 @@ static PyObject *py_wc_walk_status(PyObject *self, PyObject *args, PyObject *kwa Py_RETURN_NONE; } -static svn_lock_t *py_object_to_svn_lock(PyObject *py_lock, apr_pool_t *pool) +svn_lock_t *py_object_to_svn_lock(PyObject *py_lock, apr_pool_t *pool) { LockObject* lockobj = (LockObject *)py_lock; - return lockobj->lock; + if (!PyObject_IsInstance(py_lock, (PyObject *)&Lock_Type)) { + PyErr_SetString(PyExc_TypeError, "Expected Lock object"); + return NULL; + } + return &lockobj->lock; } static PyObject *py_wc_add_lock(PyObject *self, PyObject *args, PyObject *kwargs) @@ -1626,6 +1643,9 @@ static PyObject *py_wc_add_lock(PyObject *self, PyObject *args, PyObject *kwargs } scratch_pool = Pool(NULL); + if (scratch_pool == NULL) { + return NULL; + } path = py_object_to_svn_abspath(py_path, scratch_pool); if (path == NULL) { @@ -1790,6 +1810,38 @@ static PyObject *py_wc_get_prop_diffs(PyObject *self, PyObject *args, PyObject * return Py_BuildValue("NN", py_orig_props, py_propchanges); } +static PyObject *py_wc_context_process_committed_queue(PyObject *self, PyObject *args) +{ + apr_pool_t *temp_pool; + ContextObject *contextobj = (ContextObject *)self; + svn_revnum_t revnum; + char *date, *author; + PyObject *py_queue; + + if (!PyArg_ParseTuple(args, "O!lss", &CommittedQueue_Type, &py_queue, + &revnum, &date, &author)) + return NULL; + + temp_pool = Pool(NULL); + if (temp_pool == NULL) + return NULL; + + svn_wc_committed_queue_t *committed_queue = PyObject_GetCommittedQueue(py_queue); + + RUN_SVN_WITH_POOL(temp_pool, + svn_wc_process_committed_queue2(committed_queue, + contextobj->context, + revnum, date, author, + py_cancel_check, NULL, + temp_pool)); + + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + + + static PyMethodDef context_methods[] = { { "locked", py_wc_context_locked, METH_VARARGS, "locked(path) -> (locked_here, locked)\n" @@ -1825,6 +1877,10 @@ static PyMethodDef context_methods[] = { (PyCFunction)py_wc_context_ensure_adm, METH_VARARGS|METH_KEYWORDS, "ensure_adm(local_abspath, url, repos_root_url, repos_uuid, revnum, depth)" }, + { "process_committed_queue", + (PyCFunction)py_wc_context_process_committed_queue, + METH_VARARGS|METH_KEYWORDS, + "" }, { "status", (PyCFunction)py_wc_status, METH_VARARGS|METH_KEYWORDS, @@ -1967,10 +2023,11 @@ static void lock_dealloc(PyObject *self) static PyObject *lock_init(PyTypeObject *type, PyObject *args, PyObject *kwargs) { - char *kwnames[] = { NULL }; + char *kwnames[] = { "token", NULL }; LockObject *ret; + char *token = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwnames)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|z", kwnames, &token)) return NULL; ret = PyObject_New(LockObject, &Lock_Type); @@ -1980,11 +2037,69 @@ static PyObject *lock_init(PyTypeObject *type, PyObject *args, PyObject *kwargs) ret->pool = Pool(NULL); if (ret->pool == NULL) return NULL; - ret->lock = svn_lock_create(ret->pool); + ret->lock = *svn_lock_create(ret->pool); + if (token != NULL) { + ret->lock.token = apr_pstrdup(ret->pool, token); + } return (PyObject *)ret; } +static PyObject *lock_get_path(PyObject *self, void *closure) { + LockObject *lock_obj = (LockObject *)self; + + if (lock_obj->lock.path == NULL) { + Py_RETURN_NONE; + } + + return PyUnicode_FromString(lock_obj->lock.path); +} + +static int lock_set_path(PyObject *self, PyObject *value, void *closure) { + LockObject *lock_obj = (LockObject *)self; + char *path; + + path = PyBytes_AsString(value); + if (path == NULL) { + return -1; + } + + lock_obj->lock.path = py_object_to_svn_string(value, lock_obj->pool); + return 0; +} + +static PyObject *lock_get_token(PyObject *self, void *closure) { + LockObject *lock_obj = (LockObject *)self; + + if (lock_obj->lock.token == NULL) { + Py_RETURN_NONE; + } + + return PyBytes_FromString(lock_obj->lock.token); +} + +static int lock_set_token(PyObject *self, PyObject *value, void *closure) { + LockObject *lock_obj = (LockObject *)self; + char *token; + + token = PyBytes_AsString(value); + if (token == NULL) { + PyErr_SetNone(PyExc_TypeError); + return -1; + } + + lock_obj->lock.token = apr_pstrdup(lock_obj->pool, PyBytes_AsString(value)); + return 0; +} + +static PyGetSetDef lock_getsetters[] = { + { "path", lock_get_path, lock_set_path, + "the path this lock applies to"}, + { "token", lock_get_token, lock_set_token, + "unique URI representing lock"}, + { NULL }, +}; + PyTypeObject Lock_Type = { PyVarObject_HEAD_INIT(NULL, 0) "wc.Lock", /* const char *tp_name; For printing, in format "." */ @@ -2000,6 +2115,8 @@ PyTypeObject Lock_Type = { .tp_methods = NULL, /* struct PyMethodDef *tp_methods; */ .tp_new = lock_init, /* tp_new tp_new */ + + .tp_getset = lock_getsetters, }; static PyObject * diff --git a/subvertpy/wc.h b/subvertpy/wc.h index 558c38dd..ccd50d48 100644 --- a/subvertpy/wc.h +++ b/subvertpy/wc.h @@ -38,12 +38,14 @@ svn_error_t *wc_validator3(void *baton, const char *uuid, const char *url, const svn_error_t *wc_validator2(void *baton, const char *uuid, const char *url, svn_boolean_t root, apr_pool_t *pool); svn_wc_committed_queue_t *PyObject_GetCommittedQueue(PyObject *obj); extern PyTypeObject CommittedQueue_Type; +svn_lock_t *py_object_to_svn_lock(PyObject *py_lock, apr_pool_t *pool); /* Provided by wc_adm.h */ extern PyTypeObject Adm_Type; extern PyTypeObject Entry_Type; extern PyTypeObject Status2_Type; svn_wc_adm_access_t *PyObject_GetAdmAccess(PyObject *obj); +extern PyTypeObject Lock_Type; #ifdef __GNUC__ #pragma GCC visibility pop diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index 6ced3c1b..7cdce98c 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -1744,6 +1744,44 @@ static PyObject *wc_status(PyObject *self, PyObject *args) return (PyObject*)ret; } +static PyObject *wc_add_lock(PyObject *self, PyObject *args) +{ + const char *path; + apr_pool_t *temp_pool; + AdmObject *admobj = (AdmObject *)self; + svn_lock_t *lock; + PyObject *py_path, *py_lock; + + if (!PyArg_ParseTuple(args, "OO!", &py_path, &Lock_Type, &py_lock)) + return NULL; + + ADM_CHECK_CLOSED(admobj); + + temp_pool = Pool(NULL); + if (temp_pool == NULL) { + return NULL; + } + + path = py_object_to_svn_abspath(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + lock = py_object_to_svn_lock(py_lock, temp_pool); + if (lock == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + + RUN_SVN_WITH_POOL(temp_pool, + svn_wc_add_lock(path, lock, admobj->adm, temp_pool)); + + apr_pool_destroy(temp_pool); + + Py_RETURN_NONE; +} + static PyMethodDef adm_methods[] = { { "prop_set", adm_prop_set, METH_VARARGS, "S.prop_set(name, value, path, skip_checks=False)" }, { "access_path", (PyCFunction)adm_access_path, METH_NOARGS, @@ -1809,6 +1847,7 @@ static PyMethodDef adm_methods[] = { { "resolved_conflict", (PyCFunction)resolved_conflict, METH_VARARGS, "S.resolved_conflict(path, resolve_text, resolve_props, resolve_tree, depth, conflict_choice, notify_func=None, cancel=None)" }, { "status", (PyCFunction)wc_status, METH_VARARGS, "status(wc_path) -> Status" }, + { "add_lock", (PyCFunction)wc_add_lock, METH_VARARGS, "add_lock(path, lock)" }, { NULL, } }; -- cgit v1.2.3 From 8d890946304560fd4439ae95d2c9e7496dfd2c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 26 May 2018 14:01:26 +0100 Subject: Fix path handling. --- subvertpy/_ra.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/subvertpy/_ra.c b/subvertpy/_ra.c index 3fb1b52c..635104dd 100644 --- a/subvertpy/_ra.c +++ b/subvertpy/_ra.c @@ -1599,12 +1599,13 @@ static PyObject *ra_get_file(PyObject *self, PyObject *args) static PyObject *ra_get_lock(PyObject *self, PyObject *args) { - char *path; + const char *path; RemoteAccessObject *ra = (RemoteAccessObject *)self; - svn_lock_t *lock; + svn_lock_t *lock = NULL; + PyObject *py_path; apr_pool_t *temp_pool; - if (!PyArg_ParseTuple(args, "s:get_lock", &path)) + if (!PyArg_ParseTuple(args, "O:get_lock", &py_path)) return NULL; if (ra_check_busy(ra)) @@ -1613,10 +1614,22 @@ static PyObject *ra_get_lock(PyObject *self, PyObject *args) temp_pool = Pool(NULL); if (temp_pool == NULL) return NULL; + + path = py_object_to_svn_relpath(py_path, temp_pool); + if (path == NULL) { + apr_pool_destroy(temp_pool); + return NULL; + } + RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_lock(ra->ra, &lock, path, temp_pool)); apr_pool_destroy(temp_pool); - return wrap_lock(lock); + + if (lock == NULL) { + Py_RETURN_NONE; + } else { + return wrap_lock(lock); + } } static PyObject *ra_check_path(PyObject *self, PyObject *args) -- cgit v1.2.3 From 8a1c36677e697d05d92dde4d5f53afb4640bd9d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 26 May 2018 14:50:19 +0100 Subject: mark tests as failing with 1.9. --- subvertpy/tests/test_wc.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 394c6082..98471d32 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -403,6 +403,8 @@ class AdmObjTests(SubversionTestCase): adm.probe_try(os.path.join("checkout", "bar")).access_path()) def test_lock(self): + if wc.api_version() >= (1, 9): + self.skipTest("TODO: doesn't yet work with svn >= 1.9") self.make_client("repos", "checkout") self.build_tree({"checkout/bar": b"la"}) self.client_add('checkout/bar') -- cgit v1.2.3 From 0db0ebbe4ff07bb374c9240cc8e8b703c890dde8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 26 May 2018 16:07:22 +0100 Subject: Make lock function available for older versions of svn. --- subvertpy/wc.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index 4fb3b0c9..b0d2445d 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -1061,6 +1061,16 @@ PyTypeObject CommittedQueue_Type = { committed_queue_init, /* newfunc tp_new; */ }; +svn_lock_t *py_object_to_svn_lock(PyObject *py_lock, apr_pool_t *pool) +{ + LockObject* lockobj = (LockObject *)py_lock; + if (!PyObject_IsInstance(py_lock, (PyObject *)&Lock_Type)) { + PyErr_SetString(PyExc_TypeError, "Expected Lock object"); + return NULL; + } + return &lockobj->lock; +} + #if ONLY_SINCE_SVN(1, 7) static PyTypeObject Context_Type; @@ -1618,16 +1628,6 @@ static PyObject *py_wc_walk_status(PyObject *self, PyObject *args, PyObject *kwa Py_RETURN_NONE; } -svn_lock_t *py_object_to_svn_lock(PyObject *py_lock, apr_pool_t *pool) -{ - LockObject* lockobj = (LockObject *)py_lock; - if (!PyObject_IsInstance(py_lock, (PyObject *)&Lock_Type)) { - PyErr_SetString(PyExc_TypeError, "Expected Lock object"); - return NULL; - } - return &lockobj->lock; -} - static PyObject *py_wc_add_lock(PyObject *self, PyObject *args, PyObject *kwargs) { ContextObject *context_obj = (ContextObject *)self; -- cgit v1.2.3 From f5a8d72b5bbadcaf437a1adb9e4040d56b776079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 26 May 2018 16:57:34 +0100 Subject: Fix contents for md5 digest. --- subvertpy/tests/test_wc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 98471d32..bc5fcfc0 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -375,7 +375,7 @@ class AdmObjTests(SubversionTestCase): [(0, 0, 2, 0, [(2, 0, 2)], b'la'), None]) self.assertIsInstance(tmpfile, str) self.assertEqual(16, len(digest)) - self.assertEqual(hashlib.md5(b'blala').digest(), digest) + self.assertEqual(hashlib.md5(b'la').digest(), digest) bar = adm.entry("bar") self.assertEqual(-1, bar.cmt_rev) self.assertEqual(0, bar.revision) -- cgit v1.2.3 From d96e33432231eb0457e2ebfa1664173a211241f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sun, 3 Jun 2018 01:28:25 +0100 Subject: Run but check for NotImplementedError. --- subvertpy/tests/test_wc.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index bc5fcfc0..eac8933a 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -69,11 +69,11 @@ class AdmTests(TestCase): if wc.api_version() < (1, 5): self.assertRaises( NotImplementedError, wc.match_ignore_list, "foo", []) - self.skipTest("match_ignore_list not supported with svn < 1.5") - self.assertTrue(wc.match_ignore_list("foo", ["f*"])) - self.assertTrue(wc.match_ignore_list("foo", ["foo"])) - self.assertFalse(wc.match_ignore_list("foo", [])) - self.assertFalse(wc.match_ignore_list("foo", ["bar"])) + else: + self.assertTrue(wc.match_ignore_list("foo", ["f*"])) + self.assertTrue(wc.match_ignore_list("foo", ["foo"])) + self.assertFalse(wc.match_ignore_list("foo", [])) + self.assertFalse(wc.match_ignore_list("foo", ["bar"])) class WcTests(SubversionTestCase): @@ -117,16 +117,20 @@ class AdmObjTests(SubversionTestCase): adm.close() def test_add_repos_file(self): - if wc.api_version() < (1, 6): - self.skipTest("doesn't work with svn < 1.6") self.make_client("repos", "checkout") adm = wc.Adm(None, "checkout", True) - adm.add_repos_file("checkout/bar", BytesIO(b"basecontents"), - BytesIO(b"contents"), {}, {}) - if wc.api_version() >= (1, 7): - self.skipTest("TODO: doesn't yet work with svn >= 1.7") - self.assertEqual(b"basecontents", - wc.get_pristine_contents("checkout/bar").read()) + if wc.api_version() < (1, 6): + self.assertRaises( + NotImplementedError, + adm.add_repos_file, "checkout/bar", + BytesIO(b"basecontents"), BytesIO(b"contents"), {}, {}) + else: + adm.add_repos_file("checkout/bar", BytesIO(b"basecontents"), + BytesIO(b"contents"), {}, {}) + if wc.api_version() >= (1, 7): + self.skipTest("TODO: doesn't yet work with svn >= 1.7") + self.assertEqual(b"basecontents", + wc.get_pristine_contents("checkout/bar").read()) def test_mark_missing_deleted(self): if wc.api_version() >= (1, 7): -- cgit v1.2.3 From 98b3c6725815732940dd4ca0dc7deab2cb2d52d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Mon, 4 Jun 2018 00:56:28 +0100 Subject: Don't check that urls are native strings. --- subvertpy/tests/__init__.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/subvertpy/tests/__init__.py b/subvertpy/tests/__init__.py index c635b2a0..4df5d7ec 100644 --- a/subvertpy/tests/__init__.py +++ b/subvertpy/tests/__init__.py @@ -237,19 +237,13 @@ class SubversionTestCase(TestCaseInTempDir): if sys.platform == 'win32': revprop_hook = os.path.join( abspath, "hooks", "pre-revprop-change.bat") - f = open(revprop_hook, 'w') - try: + with open(revprop_hook, 'w') as f: f.write("exit 0\n") - finally: - f.close() else: revprop_hook = os.path.join( abspath, "hooks", "pre-revprop-change") - f = open(revprop_hook, 'w') - try: + with open(revprop_hook, 'w') as f: f.write("#!/bin/sh\n") - finally: - f.close() os.chmod(revprop_hook, os.stat(revprop_hook).st_mode | 0o111) if sys.platform == 'win32': @@ -344,7 +338,6 @@ class SubversionTestCase(TestCaseInTempDir): :return: Dictionary """ r = ra.RemoteAccess(url) - assert isinstance(url, str) ret = {} def rcvr(orig_paths, rev, revprops, has_children=None): @@ -353,7 +346,7 @@ class SubversionTestCase(TestCaseInTempDir): revprops.get(properties.PROP_REVISION_AUTHOR), revprops.get(properties.PROP_REVISION_DATE), revprops.get(properties.PROP_REVISION_LOG)) - r.get_log(rcvr, [""], start_revnum, stop_revnum, 0, True, True, + r.get_log(rcvr, [u""], start_revnum, stop_revnum, 0, True, True, revprops=[properties.PROP_REVISION_AUTHOR, properties.PROP_REVISION_DATE, properties.PROP_REVISION_LOG]) @@ -402,11 +395,8 @@ class SubversionTestCase(TestCaseInTempDir): os.makedirs(os.path.dirname(name)) except OSError: pass - f = open(name, 'wb') - try: + with open(name, 'wb') as f: f.write(content) - finally: - f.close() def make_client(self, repospath, clientpath, allow_revprop_changes=True): """Create a repository and a checkout. Return the checkout. -- cgit v1.2.3 From a1263b13672d207e4add4e77650b932068956050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Wed, 6 Jun 2018 01:12:10 +0100 Subject: Add context manager to subvertpy.wc.Adm. --- subvertpy/tests/test_wc.py | 8 ++++++++ subvertpy/wc_adm.c | 25 ++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index eac8933a..a501aee1 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -100,6 +100,14 @@ class AdmObjTests(SubversionTestCase): self.assertFalse(adm.has_binary_prop("checkout/bar")) adm.close() + def test_with(self): + self.make_client("repos", "checkout") + self.build_tree({"checkout/bar": b"\x00 \x01"}) + self.client_add('checkout/bar') + self.client_set_prop('checkout/bar', 'svn:mime-type', 'text/bar') + with wc.Adm(None, "checkout") as adm: + self.assertFalse(adm.has_binary_prop("checkout/bar")) + def test_get_ancestry(self): repos_url = self.make_client("repos", "checkout") self.build_tree({"checkout/bar": b"\x00 \x01"}) diff --git a/subvertpy/wc_adm.c b/subvertpy/wc_adm.c index 7cdce98c..07df1343 100644 --- a/subvertpy/wc_adm.c +++ b/subvertpy/wc_adm.c @@ -880,7 +880,7 @@ static PyObject *adm_close(PyObject *self) svn_wc_adm_close(admobj->adm); #endif Py_END_ALLOW_THREADS - admobj->adm = NULL; + admobj->adm = NULL; } Py_RETURN_NONE; @@ -1782,6 +1782,27 @@ static PyObject *wc_add_lock(PyObject *self, PyObject *args) Py_RETURN_NONE; } +static PyObject *wc_enter(PyObject *self) +{ + Py_INCREF(self); + return self; +} + +static PyObject *wc_exit(PyObject *self, PyObject *args) +{ + PyObject *exc_type, *exc_value, *exc_tb, *ret; + + if (!PyArg_ParseTuple(args, "OOO", &exc_type, &exc_value, &exc_tb)) + return NULL; + + ret = adm_close(self); + if (ret == NULL) { + return NULL; + } + + Py_RETURN_NONE; +} + static PyMethodDef adm_methods[] = { { "prop_set", adm_prop_set, METH_VARARGS, "S.prop_set(name, value, path, skip_checks=False)" }, { "access_path", (PyCFunction)adm_access_path, METH_NOARGS, @@ -1848,6 +1869,8 @@ static PyMethodDef adm_methods[] = { "S.resolved_conflict(path, resolve_text, resolve_props, resolve_tree, depth, conflict_choice, notify_func=None, cancel=None)" }, { "status", (PyCFunction)wc_status, METH_VARARGS, "status(wc_path) -> Status" }, { "add_lock", (PyCFunction)wc_add_lock, METH_VARARGS, "add_lock(path, lock)" }, + { "__enter__", (PyCFunction)wc_enter, METH_NOARGS, "__enter__() -> self" }, + { "__exit__", (PyCFunction)wc_exit, METH_VARARGS, "__exit__(exc_type, exc_value, exc_tb)" }, { NULL, } }; -- cgit v1.2.3 From 9d5515eee51541a4cafa07ac0e7ed65f1776516b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Thu, 7 Jun 2018 00:34:21 +0100 Subject: Add get_session_url. --- subvertpy/_ra.c | 20 +++++++++++++++----- subvertpy/tests/test_ra.py | 4 ++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/subvertpy/_ra.c b/subvertpy/_ra.c index 635104dd..0757ac17 100644 --- a/subvertpy/_ra.c +++ b/subvertpy/_ra.c @@ -900,6 +900,16 @@ static PyObject *ra_get_repos_root(PyObject *self) * Obtain the URL of this repository. */ static PyObject *ra_get_url(PyObject *self, void *closure) +{ + RemoteAccessObject *ra = (RemoteAccessObject *)self; + + return PyUnicode_FromString(ra->url); +} + +/** + * Obtain the URL of this repository. + */ +static PyObject *ra_get_session_url(PyObject *self) { const char *url; apr_pool_t *temp_pool; @@ -927,6 +937,8 @@ static PyObject *ra_get_url(PyObject *self, void *closure) #endif } + + static PyObject *ra_do_update(PyObject *self, PyObject *args) { svn_revnum_t revision_to_update_to; @@ -2234,12 +2246,15 @@ static int ra_set_progress_func(PyObject *self, PyObject *value, void *closure) static PyGetSetDef ra_getsetters[] = { { "progress_func", NULL, ra_set_progress_func, NULL }, + { "url", ra_get_url, NULL, NULL }, { NULL } }; #include "_ra_iter_log.c" 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_locations", ra_get_locations, METH_VARARGS, @@ -2303,9 +2318,6 @@ static PyMethodDef ra_methods[] = { { "get_repos_root", (PyCFunction)ra_get_repos_root, METH_NOARGS, "S.get_repos_root() -> url\n" "Return the URL to the root of the repository." }, - { "get_url", (PyCFunction)ra_get_url, METH_NOARGS, - "S.get_url() -> url\n" - "Return the URL of the repository." }, { "get_log", (PyCFunction)ra_get_log, METH_VARARGS|METH_KEYWORDS, "S.get_log(callback, paths, start, end, limit=0, " "discover_changed_paths=False, strict_node_history=True, " @@ -2345,8 +2357,6 @@ static PyMethodDef ra_methods[] = { static PyMemberDef ra_members[] = { { "busy", T_BYTE, offsetof(RemoteAccessObject, busy), READONLY, "Whether this connection is in use at the moment" }, - { "url", T_STRING, offsetof(RemoteAccessObject, url), READONLY, - "URL this connection is to" }, { "corrected_url", T_STRING, offsetof(RemoteAccessObject, corrected_url), READONLY, "Corrected URL" }, { NULL, } diff --git a/subvertpy/tests/test_ra.py b/subvertpy/tests/test_ra.py index 51ad42b3..cafa7479 100644 --- a/subvertpy/tests/test_ra.py +++ b/subvertpy/tests/test_ra.py @@ -85,9 +85,9 @@ class TestRemoteAccess(SubversionTestCase): def test_get_url(self): if ra.api_version() < (1, 5): - self.assertRaises(NotImplementedError, self.ra.get_url) + self.assertRaises(NotImplementedError, self.ra.get_session_url) else: - self.assertEqual(self.repos_url, self.ra.get_url()) + self.assertEqual(self.repos_url, self.ra.get_session_url()) def test_reparent(self): self.ra.reparent(self.repos_url) -- cgit v1.2.3 From 57061ad4b8a4df732695a9ca75dd7c6495f60511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 9 Jun 2018 08:30:10 +0100 Subject: Don't override error message set by PyBytes_AsStringAndSize. --- subvertpy/util.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/subvertpy/util.c b/subvertpy/util.c index 715f9c82..5d679838 100644 --- a/subvertpy/util.c +++ b/subvertpy/util.c @@ -572,8 +572,6 @@ apr_hash_t *prop_dict_to_hash(apr_pool_t *pool, PyObject *py_props) } if (PyBytes_AsStringAndSize(v, &val, &val_size) == -1) { - PyErr_SetString(PyExc_TypeError, - "property value should be unicode or byte string"); return NULL; } -- cgit v1.2.3 From b85351eb10bc0b36523e096e51e5b0c9a8c18fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 9 Jun 2018 16:06:26 +0100 Subject: Accept unicode URLs in copyfrom arguments. --- subvertpy/editor.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/subvertpy/editor.c b/subvertpy/editor.c index b425de0c..71b543a5 100644 --- a/subvertpy/editor.c +++ b/subvertpy/editor.c @@ -397,15 +397,15 @@ static PyObject *py_dir_editor_delete_entry(PyObject *self, PyObject *args) static PyObject *py_dir_editor_add_directory(PyObject *self, PyObject *args) { - PyObject *py_path; + PyObject *py_path, *py_copyfrom_path = Py_None; const char *path; - char *copyfrom_path = NULL; + const char *copyfrom_path = NULL; svn_revnum_t copyfrom_rev = -1; void *child_baton; EditorObject *editor = (EditorObject *)self; apr_pool_t *subpool; - if (!PyArg_ParseTuple(args, "O|zl", &py_path, ©from_path, ©from_rev)) + if (!PyArg_ParseTuple(args, "O|Ol", &py_path, &py_copyfrom_path, ©from_rev)) return NULL; if (editor->done) { @@ -423,6 +423,13 @@ static PyObject *py_dir_editor_add_directory(PyObject *self, PyObject *args) return NULL; } + if (py_copyfrom_path != Py_None) { + copyfrom_path = py_object_to_svn_uri(py_copyfrom_path, editor->pool); + if (copyfrom_path == NULL) { + return NULL; + } + } + RUN_SVN(editor->editor->add_directory( path, editor->baton, copyfrom_path == NULL?NULL:svn_uri_canonicalize(copyfrom_path, editor->pool), @@ -563,14 +570,14 @@ static PyObject *py_dir_editor_absent_directory(PyObject *self, PyObject *args) static PyObject *py_dir_editor_add_file(PyObject *self, PyObject *args) { const char *path; - char *copy_path=NULL; - PyObject *py_path; + const char *copy_path=NULL; + PyObject *py_path, *py_copy_path = Py_None; svn_revnum_t copy_rev=-1; void *file_baton = NULL; EditorObject *editor = (EditorObject *)self; apr_pool_t *subpool; - if (!PyArg_ParseTuple(args, "O|zl", &py_path, ©_path, ©_rev)) + if (!PyArg_ParseTuple(args, "O|Ol", &py_path, &py_copy_path, ©_rev)) return NULL; if (editor->done) { @@ -588,9 +595,15 @@ static PyObject *py_dir_editor_add_file(PyObject *self, PyObject *args) return NULL; } + if (py_copy_path != Py_None) { + copy_path = py_object_to_svn_uri(py_copy_path, editor->pool); + if (copy_path == NULL) { + return NULL; + } + } + RUN_SVN(editor->editor->add_file(path, editor->baton, - copy_path == NULL?NULL:svn_uri_canonicalize(copy_path, editor->pool), - copy_rev, editor->pool, &file_baton)); + copy_path, copy_rev, editor->pool, &file_baton)); subpool = Pool(NULL); if (subpool == NULL) -- cgit v1.2.3 From a08e0ae2d4c06b09acd4b2a4dbad4df8ae8a5e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 9 Jun 2018 17:02:32 +0100 Subject: Accept unicode arguments to is_adm_dir. --- subvertpy/wc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/subvertpy/wc.c b/subvertpy/wc.c index b0d2445d..4b6350c4 100644 --- a/subvertpy/wc.c +++ b/subvertpy/wc.c @@ -481,17 +481,23 @@ static PyObject *is_normal_prop(PyObject *self, PyObject *args) static PyObject *is_adm_dir(PyObject *self, PyObject *args) { - char *name; + const char *name; + PyObject *py_name; apr_pool_t *pool; svn_boolean_t ret; - if (!PyArg_ParseTuple(args, "s", &name)) + if (!PyArg_ParseTuple(args, "O", &py_name)) return NULL; pool = Pool(NULL); if (pool == NULL) return NULL; + name = py_object_to_svn_string(py_name, pool); + if (name == NULL) { + return NULL; + } + ret = svn_wc_is_adm_dir(name, pool); apr_pool_destroy(pool); -- cgit v1.2.3 From c4825480aee941e942bb0e941009890580732020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Mon, 11 Jun 2018 10:46:52 +0100 Subject: Support context manager pattern on test commit editor. --- subvertpy/tests/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/subvertpy/tests/__init__.py b/subvertpy/tests/__init__.py index 4df5d7ec..71b35763 100644 --- a/subvertpy/tests/__init__.py +++ b/subvertpy/tests/__init__.py @@ -198,6 +198,13 @@ class TestCommitEditor(TestDirEditor): TestDirEditor.close(self) self.editor.close() + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_tb): + self.close() + return False + class SubversionTestCase(TestCaseInTempDir): """A test case that provides the ability to build Subversion -- cgit v1.2.3 From bc837db215fa5091d04fbd95dc3ce14f8ff88a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 10 Nov 2018 13:34:50 +0000 Subject: Move flake8 configuration to setup.cfg. --- Makefile | 2 +- setup.cfg | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 setup.cfg diff --git a/Makefile b/Makefile index a28bc5ac..90e7cb0e 100644 --- a/Makefile +++ b/Makefile @@ -44,4 +44,4 @@ pydoctor: $(PYDOCTOR) $(PYDOCTOR_OPTIONS) --introspect-c-modules -c subvertpy.cfg --make-html style: - $(FLAKE8) --exclude=build,.git,build-pypy,.tox subvertpy bin + $(FLAKE8) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..6f815c04 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[flake8] +exclude = build,.git,build-pypy,.tox -- cgit v1.2.3 From 987e965536b10654efbe7c685d49d8700c07011c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 10 Nov 2018 14:33:23 +0000 Subject: Extend long description, update download URL. --- setup.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 21b0da77..0fc21245 100755 --- a/setup.py +++ b/setup.py @@ -429,15 +429,31 @@ if __name__ == "__main__": keywords='svn subvertpy subversion bindings', version=subvertpy_version_string, url='https://jelmer.uk/subvertpy', - download_url="https://jelmer.uk/subvertpy/subvertpy-%s.tar.gz" % ( + download_url="https://jelmer.uk/subvertpy/tarball/subvertpy-%s/" % ( subvertpy_version_string, ), license='LGPLv2.1 or later', author='Jelmer Vernooij', author_email='jelmer@jelmer.uk', long_description=""" - Alternative Python bindings for Subversion. The goal is to have - complete, portable and "Pythonic" Python bindings. - """, +Alternative Python bindings for Subversion. The goal is to have +complete, portable and "Pythonic" Python bindings. + +Bindings are provided for the working copy, client, delta, remote access and +repository APIs. A hookable server side implementation of the custom Subversion +protocol (svn_ra) is also provided. + +Differences with similar packages +--------------------------------- +subvertpy covers more of the APIs than python-svn. It provides a more +"Pythonic" API than python-subversion, which wraps the Subversion C API pretty +much directly. Neither provide a hookable server-side. + +Dependencies +------------ +Subvertpy depends on Python 2.7 or 3.5, and Subversion 1.4 or later. It should +work on Windows as well as most POSIX-based platforms (including Linux, BSDs +and Mac OS X). +""", packages=['subvertpy', 'subvertpy.tests'], ext_modules=subvertpy_modules(), scripts=['bin/subvertpy-fast-export'], -- cgit v1.2.3 From 4488b94ff5cfb4ac560224776198c5ac62409f81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 10 Nov 2018 15:03:25 +0000 Subject: Only check subvertpy. --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index 6f815c04..ccc2a78c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,3 @@ [flake8] exclude = build,.git,build-pypy,.tox +application-package-names = subvertpy -- cgit v1.2.3 From bb39e8956d6bccc83095c469b9c337a4d373fa9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 10 Nov 2018 15:49:05 +0000 Subject: Exclude subversion from flakes. --- setup.cfg | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index ccc2a78c..d62bc256 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,2 @@ [flake8] -exclude = build,.git,build-pypy,.tox -application-package-names = subvertpy +exclude = build,.git,build-pypy,.tox,subversion-* -- cgit v1.2.3 From fc6334b40e85fef51a161d836d9344c0d10b5a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 10 Nov 2018 18:44:05 +0000 Subject: Use right exception. --- subvertpy/ra_svn.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/subvertpy/ra_svn.py b/subvertpy/ra_svn.py index af6f2f14..eae6a311 100644 --- a/subvertpy/ra_svn.py +++ b/subvertpy/ra_svn.py @@ -502,19 +502,20 @@ class SVNClient(SVNConnection): host, port, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, 0) self._socket = None - err = RuntimeError('no addresses for %s:%s' % (host, port)) + last_err = RuntimeError('no addresses for %s:%s' % (host, port)) for (family, socktype, proto, canonname, sockaddr) in sockaddrs: try: self._socket = socket.socket(family, socktype, proto) self._socket.connect(sockaddr) except socket.error as err: + last_err = err if self._socket is not None: self._socket.close() self._socket = None continue break if self._socket is None: - raise err + raise last_err self._socket.setblocking(True) return (self._socket.recv, self._socket.send) -- cgit v1.2.3 From 474e405ccdc809f6006cc6e4420bd38257fbe304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 28 Dec 2019 13:46:10 +0000 Subject: Build with newer Pythons. --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6ac583f5..81f1001d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,10 +9,12 @@ python: - "3.4" - "3.5" - "3.6" + - "3.7" + - "3.8" env: # SVN 1.10 requires libutf8proc, which isn't available on trusty :( -# - SVN_VERSION=1.10.0 -# SVN_OPTIONS="--with-lz4=internal" + - SVN_VERSION=1.10.0 + SVN_OPTIONS="--with-lz4=internal" - SVN_VERSION=1.9.7 - SVN_VERSION=1.8.19 - SVN_VERSION=1.7.19 -- cgit v1.2.3 From cada13258e252848ad27c09fb17c1e83467f527b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 28 Dec 2019 14:12:58 +0000 Subject: Attempt to fix build for svn 1.10. --- .travis.yml | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index 81f1001d..762b7c0f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ language: python -dist: trusty +dist: xenial addons: apt: update: true - packages: libapr1-dev libaprutil1-dev libdb5.3-dev liblz4-dev libsasl2-dev libperl-dev libserf-dev libsqlite3-dev libtool python-all-dev libneon27-gnutls-dev + packages: libapr1-dev libaprutil1-dev libdb5.3-dev liblz4-dev libsasl2-dev libperl-dev libserf-dev libsqlite3-dev libtool python-all-dev libneon27-gnutls-dev libutf8proc-dev python: - "2.7" - "3.4" @@ -12,7 +12,6 @@ python: - "3.7" - "3.8" env: -# SVN 1.10 requires libutf8proc, which isn't available on trusty :( - SVN_VERSION=1.10.0 SVN_OPTIONS="--with-lz4=internal" - SVN_VERSION=1.9.7 @@ -21,19 +20,19 @@ env: - SVN_VERSION=1.6.21 - SVN_VERSION=1.5.9 script: - - make check - - make style + - make check + - make style install: - - travis_retry pip install -U pip coverage codecov flake8 + - travis_retry pip install -U pip coverage codecov flake8 before_install: - - wget https://archive.apache.org/dist/subversion/subversion-${SVN_VERSION}.tar.gz - - tar xvfz subversion-${SVN_VERSION}.tar.gz - - cd subversion-${SVN_VERSION} - - ./configure ${SVN_OPTIONS} - - make - - sudo make install - - sudo ldconfig -v - - cd .. + - wget https://archive.apache.org/dist/subversion/subversion-${SVN_VERSION}.tar.gz + - tar xvfz subversion-${SVN_VERSION}.tar.gz + - cd subversion-${SVN_VERSION} + - ./configure ${SVN_OPTIONS} + - make + - sudo make install + - sudo ldconfig -v + - cd .. after_success: - python -m coverage combine - codecov -- cgit v1.2.3 From 2423bf1d68bd030e93bb6fa18601bf4be7414862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Sat, 28 Dec 2019 14:22:47 +0000 Subject: Build svn 1.11, 1.12 and 1.13. --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 762b7c0f..a10ec6b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,10 @@ python: - "3.7" - "3.8" env: - - SVN_VERSION=1.10.0 + - SVN_VERSION=1.13.0 + - SVN_VERSION=1.12.2 + - SVN_VERSION=1.11.1 + - SVN_VERSION=1.10.6 SVN_OPTIONS="--with-lz4=internal" - SVN_VERSION=1.9.7 - SVN_VERSION=1.8.19 -- cgit v1.2.3