You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2014/12/02 08:53:53 UTC

svn commit: r1642813 - in /subversion/trunk/subversion/bindings/swig: include/svn_types.swg python/libsvn_swig_py/swigutil_py.c python/libsvn_swig_py/swigutil_py.h python/svn/core.py python/svn/repos.py python/tests/repository.py svn_repos.i

Author: stilor
Date: Tue Dec  2 07:53:53 2014
New Revision: 1642813

URL: http://svn.apache.org/r1642813
Log:
Implement Python bindings for dump stream parser.

* subversion/bindings/swig/include/svn_types.swg
  (): Fix a typo in svn_repos_parser_fns2_t type name, add
  svn_repos_parser_fns2_t type to the argout typemap.

* subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c
  (item_baton): Add pool; needed for certain methods in dump
  stream parser that do not take pool argument.
  (make_baton): Increment refcount on `editor'; remove incorrect
  the comment stating the current usage is safe. Save pool pointer.
  (close_baton): Decrement refcount on `editor'.
  (svn_swig_py_make_parse_fns3): New function; creates vtable/baton
  to be used in a later call to repos.parse_dumpstream3()

* subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h:
  (svn_swig_py_make_parse_fns3): New function.

* subversion/bindings/swig/python/svn/core.py
  (Stream.close): Guard against double-close.

* subversion/bindings/swig/python/svn/repos.py
  (ParseFns3): New class; vtable for dump stream parser.

* subversion/bindings/swig/python/tests/repository.py
  (test_parse_fns3): Test for dump stream parser bindings.
  (test_unnamed_editor): Test for make_baton/close_baton fix.

* subversion/bindings/swig/svn_repos.i
  (svn_swig_py_make_parse_fns3): Wrap it.

Modified:
    subversion/trunk/subversion/bindings/swig/include/svn_types.swg
    subversion/trunk/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c
    subversion/trunk/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h
    subversion/trunk/subversion/bindings/swig/python/svn/core.py
    subversion/trunk/subversion/bindings/swig/python/svn/repos.py
    subversion/trunk/subversion/bindings/swig/python/tests/repository.py
    subversion/trunk/subversion/bindings/swig/svn_repos.i

Modified: subversion/trunk/subversion/bindings/swig/include/svn_types.swg
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/swig/include/svn_types.swg?rev=1642813&r1=1642812&r2=1642813&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/swig/include/svn_types.swg (original)
+++ subversion/trunk/subversion/bindings/swig/include/svn_types.swg Tue Dec  2 07:53:53 2014
@@ -146,7 +146,8 @@
   /* svn_repos */
   svn_authz_t **,
   svn_repos_t **,
-  const svn_repos_parse_fns2_t **,
+  const svn_repos_parse_fns3_t **,
+  const svn_repos_parser_fns2_t **,
   const svn_repos_parser_fns_t **,
   void **parse_baton,
   void **revision_baton,

Modified: subversion/trunk/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c?rev=1642813&r1=1642812&r2=1642813&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c (original)
+++ subversion/trunk/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c Tue Dec  2 07:53:53 2014
@@ -1540,6 +1540,7 @@ static svn_error_t *type_conversion_erro
 typedef struct item_baton {
   PyObject *editor;     /* the editor handling the callbacks */
   PyObject *baton;      /* the dir/file baton (or NULL for edit baton) */
+  apr_pool_t *pool;     /* top-level pool */
 } item_baton;
 
 static item_baton *make_baton(apr_pool_t *pool,
@@ -1548,13 +1549,11 @@ static item_baton *make_baton(apr_pool_t
 {
   item_baton *newb = apr_palloc(pool, sizeof(*newb));
 
-  /* Note: We steal the caller's reference to 'baton'. Also, to avoid
-     memory leaks, we borrow the caller's reference to 'editor'. In this
-     case, borrowing the reference to 'editor' is safe because the contents
-     of an item_baton struct are only used by function calls which operate on
-     the editor itself. */
+  /* Note: We steal the caller's reference to 'baton'. */
+  Py_INCREF(editor);
   newb->editor = editor;
   newb->baton = baton;
+  newb->pool = pool;
 
   return newb;
 }
@@ -1583,6 +1582,9 @@ static svn_error_t *close_baton(void *ba
   /* there is no return value, so just toss this object (probably Py_None) */
   Py_DECREF(result);
 
+  /* Release the editor object */
+  Py_DECREF(ib->editor);
+
   /* We're now done with the baton. Since there isn't really a free, all
      we need to do is note that its objects are no longer referenced by
      the baton.  */
@@ -2044,6 +2046,373 @@ void svn_swig_py_make_editor(const svn_d
   *edit_baton = make_baton(pool, py_editor, NULL);
 }
 
+
+/* Wrappers for dump stream parser */
+
+static svn_error_t *parse_fn3_magic_header_record(int version,
+                                                  void *parse_baton,
+                                                  apr_pool_t *pool)
+{
+  item_baton *ib = parse_baton;
+  PyObject *result;
+  svn_error_t *err;
+
+  svn_swig_py_acquire_py_lock();
+
+  /* ### python doesn't have 'const' on the method name and format */
+  if ((result = PyObject_CallMethod(ib->editor, (char *)"magic_header_record",
+                                    (char *)"lO&", version,
+                                    make_ob_pool, pool)) == NULL)
+    {
+      err = callback_exception_error();
+      goto finished;
+    }
+
+  /* there is no return value, so just toss this object (probably Py_None) */
+  Py_DECREF(result);
+  err = SVN_NO_ERROR;
+
+ finished:
+  svn_swig_py_release_py_lock();
+  return err;
+}
+
+
+static svn_error_t *parse_fn3_uuid_record(const char *uuid,
+                                          void *parse_baton,
+                                          apr_pool_t *pool)
+{
+  item_baton *ib = parse_baton;
+  PyObject *result;
+  svn_error_t *err;
+
+  svn_swig_py_acquire_py_lock();
+
+  /* ### python doesn't have 'const' on the method name and format */
+  if ((result = PyObject_CallMethod(ib->editor, (char *)"uuid_record",
+                                    (char *)"sO&", uuid,
+                                    make_ob_pool, pool)) == NULL)
+    {
+      err = callback_exception_error();
+      goto finished;
+    }
+
+  /* there is no return value, so just toss this object (probably Py_None) */
+  Py_DECREF(result);
+  err = SVN_NO_ERROR;
+
+ finished:
+  svn_swig_py_release_py_lock();
+  return err;
+}
+
+
+static svn_error_t *parse_fn3_new_revision_record(void **revision_baton,
+                                                  apr_hash_t *headers,
+                                                  void *parse_baton,
+                                                  apr_pool_t *pool)
+{
+  item_baton *ib = parse_baton;
+  PyObject *result;
+  PyObject *tmp;
+  svn_error_t *err;
+
+  svn_swig_py_acquire_py_lock();
+
+  if ((result = PyObject_CallMethod(ib->editor, (char *)"new_revision_record",
+                                   (char *)"O&O&",
+                                   svn_swig_py_stringhash_to_dict, headers,
+                                   make_ob_pool, pool)) == NULL) {
+      err = callback_exception_error();
+      goto finished;
+    }
+
+  /* make_baton takes our 'result' reference */
+  *revision_baton = make_baton(pool, ib->editor, result);
+  err = SVN_NO_ERROR;
+
+ finished:
+  svn_swig_py_release_py_lock();
+  return err;
+}
+
+
+static svn_error_t *parse_fn3_new_node_record(void **node_baton,
+                                              apr_hash_t *headers,
+                                              void *revision_baton,
+                                              apr_pool_t *pool)
+{
+  item_baton *ib = revision_baton;
+  PyObject *result;
+  svn_error_t *err;
+
+  svn_swig_py_acquire_py_lock();
+
+  if ((result = PyObject_CallMethod(ib->editor, (char *)"new_node_record",
+                                   (char *)"O&OO&",
+                                   svn_swig_py_stringhash_to_dict, headers,
+                                   ib->baton,
+                                   make_ob_pool, pool)) == NULL) {
+      err = callback_exception_error();
+      goto finished;
+    }
+
+  /* make_baton takes our 'result' reference */
+  *node_baton = make_baton(pool, ib->editor, result);
+  err = SVN_NO_ERROR;
+
+ finished:
+  svn_swig_py_release_py_lock();
+  return err;
+}
+
+
+static svn_error_t *parse_fn3_set_revision_property(void *revision_baton,
+                                                    const char *name,
+                                                    const svn_string_t *value)
+{
+  item_baton *ib = revision_baton;
+  PyObject *result;
+  svn_error_t *err;
+
+  svn_swig_py_acquire_py_lock();
+
+  /* ### python doesn't have 'const' on the method name and format */
+  if ((result = PyObject_CallMethod(ib->editor, (char *)"set_revision_property",
+                                    (char *)"Oss#", ib->baton, name,
+                                    value ? value->data : NULL,
+                                    value ? value->len : 0)) == NULL)
+    {
+      err = callback_exception_error();
+      goto finished;
+    }
+
+  /* there is no return value, so just toss this object (probably Py_None) */
+  Py_DECREF(result);
+  err = SVN_NO_ERROR;
+
+ finished:
+  svn_swig_py_release_py_lock();
+  return err;
+}
+
+
+static svn_error_t *parse_fn3_set_node_property(void *node_baton,
+                                                const char *name,
+                                                const svn_string_t *value)
+{
+  item_baton *ib = node_baton;
+  PyObject *result;
+  svn_error_t *err;
+
+  svn_swig_py_acquire_py_lock();
+
+  /* ### python doesn't have 'const' on the method name and format */
+  if ((result = PyObject_CallMethod(ib->editor, (char *)"set_node_property",
+                                    (char *)"Oss#", ib->baton, name,
+                                    value ? value->data : NULL,
+                                    value ? value->len : 0)) == NULL)
+    {
+      err = callback_exception_error();
+      goto finished;
+    }
+
+  /* there is no return value, so just toss this object (probably Py_None) */
+  Py_DECREF(result);
+  err = SVN_NO_ERROR;
+
+ finished:
+  svn_swig_py_release_py_lock();
+  return err;
+}
+
+
+static svn_error_t *parse_fn3_delete_node_property(void *node_baton,
+                                                   const char *name)
+{
+  item_baton *ib = node_baton;
+  PyObject *result;
+  svn_error_t *err;
+
+  svn_swig_py_acquire_py_lock();
+
+  /* ### python doesn't have 'const' on the method name and format */
+  if ((result = PyObject_CallMethod(ib->editor, (char *)"delete_node_property",
+                                    (char *)"Os", ib->baton, name)) == NULL)
+    {
+      err = callback_exception_error();
+      goto finished;
+    }
+
+  /* there is no return value, so just toss this object (probably Py_None) */
+  Py_DECREF(result);
+  err = SVN_NO_ERROR;
+
+ finished:
+  svn_swig_py_release_py_lock();
+  return err;
+}
+
+
+static svn_error_t *parse_fn3_remove_node_props(void *node_baton)
+{
+  item_baton *ib = node_baton;
+  PyObject *result;
+  svn_error_t *err;
+
+  svn_swig_py_acquire_py_lock();
+
+  /* ### python doesn't have 'const' on the method name and format */
+  if ((result = PyObject_CallMethod(ib->editor, (char *)"remove_node_props",
+                                    (char *)"(O)", ib->baton)) == NULL)
+    {
+      err = callback_exception_error();
+      goto finished;
+    }
+
+  /* there is no return value, so just toss this object (probably Py_None) */
+  Py_DECREF(result);
+  err = SVN_NO_ERROR;
+
+ finished:
+  svn_swig_py_release_py_lock();
+  return err;
+}
+
+
+static svn_error_t *parse_fn3_set_fulltext(svn_stream_t **stream,
+                                           void *node_baton)
+{
+  item_baton *ib = node_baton;
+  PyObject *result;
+  svn_error_t *err;
+
+  svn_swig_py_acquire_py_lock();
+
+  /* ### python doesn't have 'const' on the method name and format */
+  if ((result = PyObject_CallMethod(ib->editor, (char *)"set_fulltext",
+                                    (char *)"(O)", ib->baton)) == NULL)
+    {
+      err = callback_exception_error();
+      goto finished;
+    }
+
+  /* Interpret None to mean NULL - no text is desired */
+  if (result == Py_None)
+    {
+      *stream = NULL;
+    }
+  else
+    {
+      /* create a stream from the IO object. it will increment the
+         reference on the 'result'. */
+      *stream = svn_swig_py_make_stream(result, ib->pool);
+    }
+
+  /* if the handler returned an IO object, svn_swig_py_make_stream() has
+     incremented its reference counter. If it was None, it is discarded. */
+  Py_DECREF(result);
+  err = SVN_NO_ERROR;
+
+ finished:
+  svn_swig_py_release_py_lock();
+  return err;
+}
+
+
+static svn_error_t *parse_fn3_apply_textdelta(svn_txdelta_window_handler_t *handler,
+                                              void **handler_baton,
+                                              void *node_baton)
+{
+  item_baton *ib = node_baton;
+  PyObject *result;
+  svn_error_t *err;
+
+  svn_swig_py_acquire_py_lock();
+
+  /* ### python doesn't have 'const' on the method name and format */
+  if ((result = PyObject_CallMethod(ib->editor, (char *)"apply_textdelta",
+                                    (char *)"(O)", ib->baton)) == NULL)
+    {
+      err = callback_exception_error();
+      goto finished;
+    }
+
+  /* Interpret None to mean svn_delta_noop_window_handler. This is much
+     easier/faster than making code always have to write a NOOP handler
+     in Python.  */
+  if (result == Py_None)
+    {
+      Py_DECREF(result);
+
+      *handler = svn_delta_noop_window_handler;
+      *handler_baton = NULL;
+    }
+  else
+    {
+      /* return the thunk for invoking the handler. the baton takes our
+         'result' reference, which is the handler. */
+      *handler = window_handler;
+      *handler_baton = result;
+    }
+
+  err = SVN_NO_ERROR;
+
+ finished:
+  svn_swig_py_release_py_lock();
+  return err;
+}
+
+
+static svn_error_t *parse_fn3_close_node(void *node_baton)
+{
+  return close_baton(node_baton, "close_node");
+}
+
+
+static svn_error_t *parse_fn3_close_revision(void *revision_baton)
+{
+  return close_baton(revision_baton, "close_revision");
+}
+
+
+static const svn_repos_parse_fns3_t thunk_parse_fns3_vtable = 
+  {
+    parse_fn3_magic_header_record,
+    parse_fn3_uuid_record,
+    parse_fn3_new_revision_record,
+    parse_fn3_new_node_record,
+    parse_fn3_set_revision_property,
+    parse_fn3_set_node_property,
+    parse_fn3_delete_node_property,
+    parse_fn3_remove_node_props,
+    parse_fn3_set_fulltext,
+    parse_fn3_apply_textdelta,
+    parse_fn3_close_node,
+    parse_fn3_close_revision
+  };
+
+static apr_status_t
+svn_swig_py_parse_fns3_destroy(void *parse_baton)
+{
+  close_baton(parse_baton, "_close_dumpstream");
+  return APR_SUCCESS;
+}
+
+void svn_swig_py_make_parse_fns3(const svn_repos_parse_fns3_t **parse_fns3,
+                                 void **parse_baton,
+                                 PyObject *py_parse_fns3,
+                                 apr_pool_t *pool)
+{
+  *parse_fns3 = &thunk_parse_fns3_vtable;
+  *parse_baton = make_baton(pool, py_parse_fns3, NULL);
+
+  /* Dump stream vtable does not provide a method which is called right before
+     the end of the parsing (similar to close_edit/abort_edit in delta editor).
+     Thus, register a pool clean-up routine to release this parse baton. */
+  apr_pool_cleanup_register(pool, *parse_baton, svn_swig_py_parse_fns3_destroy,
+                            apr_pool_cleanup_null);
+}
 
 
 /*** Other Wrappers for SVN Functions ***/

Modified: subversion/trunk/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h?rev=1642813&r1=1642812&r2=1642813&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h (original)
+++ subversion/trunk/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h Tue Dec  2 07:53:53 2014
@@ -252,6 +252,12 @@ void svn_swig_py_make_editor(const svn_d
                              PyObject *py_editor,
                              apr_pool_t *pool);
 
+/* make a parse vtable that "thunks" from C callbacks up to Python */
+void svn_swig_py_make_parse_fns3(const svn_repos_parse_fns3_t **parse_fns3,
+                                 void **parse_baton,
+                                 PyObject *py_parse_fns3,
+                                 apr_pool_t *pool);
+
 apr_file_t *svn_swig_py_make_file(PyObject *py_file,
                                   apr_pool_t *pool);
 

Modified: subversion/trunk/subversion/bindings/swig/python/svn/core.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/swig/python/svn/core.py?rev=1642813&r1=1642812&r2=1642813&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/swig/python/svn/core.py (original)
+++ subversion/trunk/subversion/bindings/swig/python/svn/core.py Tue Dec  2 07:53:53 2014
@@ -191,8 +191,9 @@ class Stream:
     svn_stream_write(self._stream, buf)
 
   def close(self):
-    svn_stream_close(self._stream)
-    self._stream = None
+    if self._stream is not None:
+      svn_stream_close(self._stream)
+      self._stream = None
 
 def secs_from_timestr(svn_datetime, pool=None):
   """Convert a Subversion datetime string into seconds since the Epoch."""

Modified: subversion/trunk/subversion/bindings/swig/python/svn/repos.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/swig/python/svn/repos.py?rev=1642813&r1=1642812&r2=1642813&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/swig/python/svn/repos.py (original)
+++ subversion/trunk/subversion/bindings/swig/python/svn/repos.py Tue Dec  2 07:53:53 2014
@@ -285,3 +285,56 @@ class RevisionChangeCollector(ChangeColl
     if idx == -1:
       return parent_path + '/' + path
     return parent_path + path[idx:]
+
+
+class ParseFns3:
+    def __init__(self):
+        pass
+
+    def __del__(self):
+        pass
+
+    def _close_dumpstream(self):
+        # Does not correspond to a C method - called before finishing the
+        # parsing of the dump stream.
+        pass
+
+    def magic_header_record(self, version, pool=None):
+        pass
+
+    def uuid_record(self, uuid, pool=None):
+        pass
+
+    def new_revision_record(self, headers, pool=None):
+        return None # Returns revision_baton
+
+    def new_node_record(self, headers, revision_baton, pool=None):
+        return None # Returns node_baton
+
+    def set_revision_property(self, revision_baton, name, value):
+        pass
+
+    def set_node_property(self, node_baton, name, value):
+        pass
+
+    def delete_node_property(self, node_baton, name):
+        pass
+
+    def remove_node_props(self, node_baton):
+        pass
+
+    def set_fulltext(self, node_baton):
+        return None # Returns a writable stream
+
+    def apply_textdelta(self, node_baton):
+        return None # Returns delta window handler
+
+    def close_node(self, node_baton):
+        pass
+
+    def close_revision(self, revision_baton):
+        pass
+
+
+def make_parse_fns3(parse_fns3, pool=None):
+    return svn_swig_py_make_parse_fns3(parse_fns3, pool)

Modified: subversion/trunk/subversion/bindings/swig/python/tests/repository.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/swig/python/tests/repository.py?rev=1642813&r1=1642812&r2=1642813&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/swig/python/tests/repository.py (original)
+++ subversion/trunk/subversion/bindings/swig/python/tests/repository.py Tue Dec  2 07:53:53 2014
@@ -18,7 +18,7 @@
 # under the License.
 #
 #
-import unittest, setup_path
+import unittest, setup_path, os, sys
 from sys import version_info # For Python version check
 if version_info[0] >= 3:
   # Python >=3.0
@@ -44,6 +44,42 @@ class ChangeReceiver(delta.Editor):
         self.textdeltas.append(textdelta)
     return textdelta_handler
 
+class DumpStreamParser(repos.ParseFns3):
+  def __init__(self):
+    repos.ParseFns3.__init__(self)
+    self.ops = []
+  def magic_header_record(self, version, pool=None):
+    self.ops.append(("magic-header", version))
+  def uuid_record(self, uuid, pool=None):
+    self.ops.append(("uuid", uuid))
+  def new_revision_record(self, headers, pool=None):
+    rev = int(headers[repos.DUMPFILE_REVISION_NUMBER])
+    self.ops.append(("new-revision", rev))
+    return rev
+  def close_revision(self, revision_baton):
+    self.ops.append(("close-revision", revision_baton))
+  def new_node_record(self, headers, revision_baton, pool=None):
+    node = headers[repos.DUMPFILE_NODE_PATH]
+    self.ops.append(("new-node", revision_baton, node))
+    return (revision_baton, node)
+  def close_node(self, node_baton):
+    self.ops.append(("close-node", node_baton[0], node_baton[1]))
+  def set_revision_property(self, revision_baton, name, value):
+    self.ops.append(("set-revision-prop", revision_baton, name, value))
+  def set_node_property(self, node_baton, name, value):
+    self.ops.append(("set-node-prop", node_baton[0], node_baton[1], name, value))
+  def remove_node_props(self, node_baton):
+    self.ops.append(("remove-node-props", node_baton[0], node_baton[1]))
+  def delete_node_property(self, node_baton, name):
+    self.ops.append(("delete-node-prop", node_baton[0], node_baton[1], name))
+  def apply_textdelta(self, node_baton):
+    self.ops.append(("apply-textdelta", node_baton[0], node_baton[1]))
+    return None
+  def set_fulltext(self, node_baton):
+    self.ops.append(("set-fulltext", node_baton[0], node_baton[1]))
+    return None
+
+
 def _authz_callback(root, path, pool):
   "A dummy authz callback which always returns success."
   return 1
@@ -139,6 +175,62 @@ class SubversionRepositoryTestCase(unitt
     #        svn_repos_t objects, so the following call segfaults
     #repos.dump_fs2(None, None, None, 0, self.rev, 0, 0, None)
 
+  def test_parse_fns3(self):
+    self.cancel_calls = 0
+    def is_cancelled():
+      self.cancel_calls += 1
+      return None
+    dump_path = os.path.join(os.path.dirname(sys.argv[0]),
+        "trac/versioncontrol/tests/svnrepos.dump")
+    stream = open(dump_path)
+    dsp = DumpStreamParser()
+    ptr, baton = repos.make_parse_fns3(dsp)
+    repos.parse_dumpstream3(stream, ptr, baton, False, is_cancelled)
+    stream.close()
+    self.assertEqual(self.cancel_calls, 76)
+    expected_list = [
+        ("magic-header", 2),
+        ('uuid', '92ea810a-adf3-0310-b540-bef912dcf5ba'),
+        ('new-revision', 0),
+        ('set-revision-prop', 0, 'svn:date', '2005-04-01T09:57:41.312767Z'),
+        ('close-revision', 0),
+        ('new-revision', 1),
+        ('set-revision-prop', 1, 'svn:log', 'Initial directory layout.'),
+        ('set-revision-prop', 1, 'svn:author', 'john'),
+        ('set-revision-prop', 1, 'svn:date', '2005-04-01T10:00:52.353248Z'),
+        ('new-node', 1, 'branches'),
+        ('remove-node-props', 1, 'branches'),
+        ('close-node', 1, 'branches'),
+        ('new-node', 1, 'tags'),
+        ('remove-node-props', 1, 'tags'),
+        ('close-node', 1, 'tags'),
+        ('new-node', 1, 'trunk'),
+        ('remove-node-props', 1, 'trunk'),
+        ('close-node', 1, 'trunk'),
+        ('close-revision', 1),
+        ('new-revision', 2),
+        ('set-revision-prop', 2, 'svn:log', 'Added README.'),
+        ('set-revision-prop', 2, 'svn:author', 'john'),
+        ('set-revision-prop', 2, 'svn:date', '2005-04-01T13:12:18.216267Z'),
+        ('new-node', 2, 'trunk/README.txt'),
+        ('remove-node-props', 2, 'trunk/README.txt'),
+        ('set-fulltext', 2, 'trunk/README.txt'),
+        ('close-node', 2, 'trunk/README.txt'),
+        ('close-revision', 2), ('new-revision', 3),
+        ('set-revision-prop', 3, 'svn:log', 'Fixed README.\n'),
+        ('set-revision-prop', 3, 'svn:author', 'kate'),
+        ('set-revision-prop', 3, 'svn:date', '2005-04-01T13:24:58.234643Z'),
+        ('new-node', 3, 'trunk/README.txt'),
+        ('remove-node-props', 3, 'trunk/README.txt'),
+        ('set-node-prop', 3, 'trunk/README.txt', 'svn:mime-type', 'text/plain'),
+        ('set-node-prop', 3, 'trunk/README.txt', 'svn:eol-style', 'native'),
+        ('set-fulltext', 3, 'trunk/README.txt'),
+        ('close-node', 3, 'trunk/README.txt'), ('close-revision', 3),
+        ]
+    # Compare only the first X nodes described in the expected list - otherwise
+    # the comparison list gets too long.
+    self.assertListEqual(dsp.ops[:len(expected_list)], expected_list)
+
   def test_get_logs(self):
     """Test scope of get_logs callbacks"""
     logs = []
@@ -177,6 +269,17 @@ class SubversionRepositoryTestCase(unitt
                      set(["This is a test.\n", "A test.\n"]))
     self.assertEqual(len(editor.textdeltas), 2)
 
+  def test_unnamed_editor(self):
+      """Test editor object without reference from interpreter"""
+      # Check that the delta.Editor object has proper lifetime. Without
+      # increment of the refcount in make_baton, the object was destroyed
+      # immediately because the interpreter does not hold a reference to it.
+      this_root = fs.revision_root(self.fs, self.rev)
+      prev_root = fs.revision_root(self.fs, self.rev-1)
+      e_ptr, e_baton = delta.make_editor(ChangeReceiver(this_root, prev_root))
+      repos.dir_delta(prev_root, '', '', this_root, '', e_ptr, e_baton,
+              _authz_callback, 1, 1, 0, 0)
+
   def test_retrieve_and_change_rev_prop(self):
     """Test playing with revprops"""
     self.assertEqual(repos.fs_revision_prop(self.repos, self.rev, "svn:log",

Modified: subversion/trunk/subversion/bindings/swig/svn_repos.i
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/swig/svn_repos.i?rev=1642813&r1=1642812&r2=1642813&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/swig/svn_repos.i (original)
+++ subversion/trunk/subversion/bindings/swig/svn_repos.i Tue Dec  2 07:53:53 2014
@@ -147,8 +147,18 @@ svn_error_t *svn_repos_dump_fs2(svn_repo
 %ignore svn_repos_dump_fs2;
 #endif
 
+/* ----------------------------------------------------------------------- */
+#ifdef SWIGPYTHON
+/* Make swig wrap this function for us, to allow making a vtable in python */
+void svn_swig_py_make_parse_fns3(const svn_repos_parse_fns3_t **parse_fns3,
+                                 void **parse_baton,
+                                 PyObject *py_parse_fns3,
+                                 apr_pool_t *pool);
+#endif
+
 %include svn_repos_h.swg
 
 #ifdef SWIGRUBY
 %define_close_related_methods(repos)
 #endif
+



Re: svn commit: r1642813 - in /subversion/trunk/subversion/bindings/swig: include/svn_types.swg python/libsvn_swig_py/swigutil_py.c python/libsvn_swig_py/swigutil_py.h python/svn/core.py python/svn/repos.py python/tests/repository.py svn_repos.i

Posted by Julian Foad <ju...@btopenworld.com>.
I (Julian Foad) wrote:
> Branko Čibej wrote:
>>  On 18.12.2014 03:48, Alexey Neyman wrote:
>>>  Any objections to the attached fix?
>>> 
>>>>  + def _assertListEqual(self, l1, l2):
>>>>  + """Poor man's replacement for assertListEqual, available in Python
>>>>  2.7+"""
[...]
> So isn't it best just to call assertEqual() in the first place? It sounds 
> like that will delegate to assertListEqual() if available, and work in a simpler 
> older way otherwise.

The Python 2.6 docs show assertEqual comparing lists:
https://docs.python.org/2.6/library/unittest.html#basic-example

    self.assertEqual(self.seq, range(10))

- Julian


Re: svn commit: r1642813 - in /subversion/trunk/subversion/bindings/swig: include/svn_types.swg python/libsvn_swig_py/swigutil_py.c python/libsvn_swig_py/swigutil_py.h python/svn/core.py python/svn/repos.py python/tests/repository.py svn_repos.i

Posted by Julian Foad <ju...@btopenworld.com>.
Branko Čibej wrote:

> On 18.12.2014 03:48, Alexey Neyman wrote:
>> Any objections to the attached fix?
>> 
>>>  + def _assertListEqual(self, l1, l2):
>>>  + """Poor man's replacement for assertListEqual, available in Python
>>>  2.7+"""
>>>  + self.assertEqual(len(l1), len(l2))
>>>  + for i in range(0, len(l1)):
>>>  + self.assertEqual(l1[i], l2[i])
> 
> Perhaps just:
> 
>     map(self.assertEqual, l1, l2)
> 
> and I would prefer subclassing to instance method injection.

According to https://docs.python.org/2/library/unittest.html#type-specific-methods

"The assertEqual() method dispatches the equality check for objects of
the same type to different type-specific methods.  These methods are already
implemented for most of the built-in types..."

And under addTypeEqualityFunc it says:

"The list of type-specific methods automatically used by assertEqual() are summarized in the following table.  Note that it’s usually not necessary to invoke these methods directly."

So isn't it best just to call assertEqual() in the first place? It sounds like that will delegate to assertListEqual() if available, and work in a simpler older way otherwise.


- Julian


Re: svn commit: r1642813 - in /subversion/trunk/subversion/bindings/swig: include/svn_types.swg python/libsvn_swig_py/swigutil_py.c python/libsvn_swig_py/swigutil_py.h python/svn/core.py python/svn/repos.py python/tests/repository.py svn_repos.i

Posted by Branko Čibej <br...@wandisco.com>.
On 18.12.2014 03:48, Alexey Neyman wrote:
> Any objections to the attached fix?
>
>> + def _assertListEqual(self, l1, l2):
>> + """Poor man's replacement for assertListEqual, available in Python
>> 2.7+"""
>> + self.assertEqual(len(l1), len(l2))
>> + for i in range(0, len(l1)):
>> + self.assertEqual(l1[i], l2[i])


Perhaps just:

    map(self.assertEqual, l1, l2)


and I would prefer subclassing to instance method injection.

-- Brane

Re: svn commit: r1642813 - in /subversion/trunk/subversion/bindings/swig: include/svn_types.swg python/libsvn_swig_py/swigutil_py.c python/libsvn_swig_py/swigutil_py.h python/svn/core.py python/svn/repos.py python/tests/repository.py svn_repos.i

Posted by Alexey Neyman <st...@att.net>.
Any objections to the attached fix?

Regards,
Alexey.

On 12/17/2014 05:43 AM, Bert Huijben wrote:
>
>> -----Original Message-----
>> From: stilor@apache.org [mailto:stilor@apache.org]
>> Sent: dinsdag 2 december 2014 08:54
>> To: commits@subversion.apache.org
>> Subject: svn commit: r1642813 - in /subversion/trunk/subversion/bindings/swig:
>> include/svn_types.swg python/libsvn_swig_py/swigutil_py.c
>> python/libsvn_swig_py/swigutil_py.h python/svn/core.py python/svn/repos.py
>> python/tests/repository.py svn_repos.i
>>
>> Author: stilor
>> Date: Tue Dec  2 07:53:53 2014
>> New Revision: 1642813
> This patch introduces a dependency on assertListEqual() - new in Python 2.7- in the test suite, which seems to fail on at least one of our buildbots.
>
> Is it possible to rewrite this test to keep compatibility with older python versions?
>
> Thanks,
>
> 	Bert
>
>
>


RE: svn commit: r1642813 - in /subversion/trunk/subversion/bindings/swig: include/svn_types.swg python/libsvn_swig_py/swigutil_py.c python/libsvn_swig_py/swigutil_py.h python/svn/core.py python/svn/repos.py python/tests/repository.py svn_repos.i

Posted by Bert Huijben <be...@qqmail.nl>.

> -----Original Message-----
> From: stilor@apache.org [mailto:stilor@apache.org]
> Sent: dinsdag 2 december 2014 08:54
> To: commits@subversion.apache.org
> Subject: svn commit: r1642813 - in /subversion/trunk/subversion/bindings/swig:
> include/svn_types.swg python/libsvn_swig_py/swigutil_py.c
> python/libsvn_swig_py/swigutil_py.h python/svn/core.py python/svn/repos.py
> python/tests/repository.py svn_repos.i
> 
> Author: stilor
> Date: Tue Dec  2 07:53:53 2014
> New Revision: 1642813

This patch introduces a dependency on assertListEqual() - new in Python 2.7- in the test suite, which seems to fail on at least one of our buildbots.

Is it possible to rewrite this test to keep compatibility with older python versions?

Thanks,

	Bert