On Thursday 18 March 2010 03:50:35 pm Роман Донченко wrote: > Alexey Neyman <sti...@att.net> писал в своём письме Thu, 18 Mar 2010 > > 21:51:53 +0300: > > On Wednesday 17 March 2010 04:59:34 pm Роман Донченко wrote: > >> Alexey Neyman <sti...@att.net> писал в своём письме Wed, 17 Mar 2010 > >> > >> 00:05:01 +0300: > >> > Hi all, > >> > > >> > The svn_repos_history2() function allows the history_func() to > >> > return a special error, SVN_ERR_CEASE_INVOCATION, to stop the > >> > search. This is not supported in Python bindings, though: attempt > >> > to return core.SVN_ERR_CEASE_INVOCATION from the history receiver > >> > results in an exception: > >> > > >> > def history_lookup(path, rev, pool): > >> > return core.SVN_ERR_CEASE_INVOCATION > >> > > >> > repos.svn_repos_history2(..., history_lookup, ...) > >> > > >> > svn.core.SubversionException: ('Python callback returned an > >> > invalid object', 20014) > >> > > >> > Indeed, svn_swig_py_repos_history_func() only expects a return of > >> > None. This patch allows the callback to return > >> > core.SVN_ERR_CEASE_INVOCATION. Currently, this is only used in > >> > svn_repos_history2() - apparently, it's the only function that > >> > supports SVN_ERR_CEASE_INVOCATION. However, there is a FIXME at > >> > least in copyfrom_info_receiver() in libsvn_client/log.c, stating > >> > that other callbacks may eventually be able to return > >> > SVN_ERR_CEASE_INVOCATION as well. > >> > >> Good idea, but I think that the callback should signal an error the > >> same way the Subversion functions do it - namely, by throwing > >> core.SubversionException. The wrapper would then translate the > >> exception's fields into an svn_error_t. It looks like tweaking > >> callback_exception_error is the most obvious way to do that. You > >> don't even have to special-case SVN_ERR_CEASE_INVOCATION, just copy > >> whatever code the exception had into the error. > > > > I think that may be beyond my current knowledge of Python's C API... > > I tried that: > > > > /* Return a Subversion error about a failed callback. */ > > static svn_error_t *callback_exception_error(void) > > { > > PyObject *err = PyErr_Occurred(); > > PyObject *svn_module, *exc_class; > > PyObject *message, *apr_err; > > > > if ((svn_module = PyImport_ImportModule((char *)"svn.core")) == > > NULL) goto finished; > > if ((exc_class = PyObject_GetAttrString(svn_module, > > (char *)"SubversionException")) == NULL) > > goto finished; > > if (PyErr_GivenExceptionMatches(exc_class, err)) > > { > > message = PyObject_GetAttrString(err, (char *)"message"); > > apr_err = PyObject_GetAttrString(err, (char *)"apr_err"); > > if (message && apr_err && PyString_Check(message) > > && PyInt_Check(apr_err)) > > { > > return svn_error_create(PyInt_AsLong(apr_err), NULL, > > PyString_AsString(message)); > > } > > } > > > > finished: > > return svn_error_create(SVN_ERR_SWIG_PY_EXCEPTION_SET, NULL, > > "Python callback raised an exception"); > > } > > > > First, it cannot find "apr_err" in the instance of > > core.Subversion_Exception; PyObject_GetAttrString(err, (char > > *)"apr_err") returns NULL. > > > > And, it produces the following error: > > > > SystemError: NULL result without error in PyObject_Call > > > > Can you help? What's wrong with that code? > > Hmm. How are you testing this? I'd write a test script, but I'm short > on time, and you probably already have one. 8=]
Yes, please use the attached pre-commit script. Then: $ svnadmin create /tmp/c.repo $ cp <...this attachment...> /tmp/c.repo/hooks/pre-commit $ svn mkdir -m "1" file:///tmp/c.repo/trunk $ svn cp -m "2" file:///tmp/c.repo/trunk file:///tmp/c.repo/copied-dir $ svn mkdir -m "3" file:///tmp/c.repo/copied-dir/newdir With stock 1.6.6, it produces the following traceback: ------------------------------------------------------------------------- svn: Commit blocked by pre-commit hook (exit code 1) with output: Traceback (most recent call last): File "/tmp/c.repo/hooks/pre-commit", line 35, in <module> core.run_app(verify, sys.argv[1], sys.argv[2]) File "/usr/local/lib/svn-python/svn/core.py", line 281, in run_app return func(application_pool, *args, **kw) File "/tmp/c.repo/hooks/pre-commit", line 31, in verify repos.svn_repos_replay2(txn_root, "", -1, True, e_ptr, e_baton, None, pool) File "/usr/local/lib/svn-python/libsvn/repos.py", line 311, in svn_repos_replay2 return apply(_repos.svn_repos_replay2, args) File "/tmp/c.repo/hooks/pre-commit", line 22, in open_directory repos.svn_repos_history2(self.fs_ptr, path, history_lookup, None, 0, self.base_rev, True, self.pool) File "/usr/local/lib/svn-python/libsvn/repos.py", line 407, in svn_repos_history2 return apply(_repos.svn_repos_history2, args) File "/tmp/c.repo/hooks/pre-commit", line 21, in history_lookup raise core.SubversionException(apr_err=core.SVN_ERR_CEASE_INVOCATION, message="Hi from history_lookup") svn.core.SubversionException: ('Hi from history_lookup', 200021) ------------------------------------------------------------------------- Changed to the following code (difference from previous version is that it Py_DECREF's everything): ------------------------------------------------------------------------- static svn_error_t *callback_exception_error(void) { PyObject *err = PyErr_Occurred(); PyObject *svn_module = NULL, *exc_class = NULL; PyObject *message = NULL, *apr_err = NULL; svn_error_t *rv = NULL; if ((svn_module = PyImport_ImportModule((char *)"svn.core")) == NULL) goto finished; if ((exc_class = PyObject_GetAttrString(svn_module, (char *)"SubversionException")) == NULL) goto finished; if (PyErr_GivenExceptionMatches(exc_class, err)) { apr_err = PyObject_GetAttrString(err, (char *)"apr_err"); message = PyObject_GetAttrString(err, (char *)"message"); if (message && apr_err && PyString_Check(message) && PyInt_Check(apr_err)) rv = svn_error_create(PyInt_AsLong(apr_err), NULL, PyString_AsString(message)); } finished: Py_XDECREF(svn_module); Py_XDECREF(exc_class); Py_XDECREF(apr_err); Py_XDECREF(message); return rv ? rv : svn_error_create(SVN_ERR_SWIG_PY_EXCEPTION_SET, NULL, "Python callback raised an exception"); } ------------------------------------------------------------------------- With this, pre-commit produces this traceback: ------------------------------------------------------------------------- svn: Commit blocked by pre-commit hook (exit code 1) with output: Traceback (most recent call last): File "/tmp/c.repo/hooks/pre-commit", line 35, in <module> core.run_app(verify, sys.argv[1], sys.argv[2]) File "/usr/local/lib/svn-python/svn/core.py", line 281, in run_app return func(application_pool, *args, **kw) File "/tmp/c.repo/hooks/pre-commit", line 31, in verify repos.svn_repos_replay2(txn_root, "", -1, True, e_ptr, e_baton, None, pool) File "/usr/local/lib/svn-python/libsvn/repos.py", line 311, in svn_repos_replay2 return apply(_repos.svn_repos_replay2, args) SystemError: NULL result without error in PyObject_Call ------------------------------------------------------------------------- Hope that helps. Regards, Alexey.
pre-commit
Description: application/python