You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2022/01/14 14:01:51 UTC

svn commit: r1897034 [8/37] - in /subversion/branches/multi-wc-format: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ contrib/client-side/ contrib/client-side/svn_load_dirs/ contrib/hook-scripts/ contrib/se...

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c Fri Jan 14 14:01:45 2022
@@ -23,9 +23,18 @@
 
 /* Tell swigutil_py.h that we're inside the implementation */
 #define SVN_SWIG_SWIGUTIL_PY_C
+/* Avoid deprecation warnings about PY_SSIZE_T_CLEAN since Python 3.8 */
+#define PY_SSIZE_T_CLEAN
+
+#if defined(_MSC_VER)
+/* Prevent "non-constant aggregate initializer" errors from Python.h in
+ * Python 3.9 */
+# pragma warning(default : 4204)
+#endif
 
 #include <Python.h>
 
+
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -46,10 +55,23 @@
 #include "svn_mergeinfo.h"
 #include "svn_types.h"
 
-#include "svn_private_config.h" /* for SVN_APR_INT64_T_PYCFMT */
+#include "svn_private_config.h"
 
 #include "swig_python_external_runtime.swg"
 #include "swigutil_py.h"
+#include "swigutil_py3c.h"
+
+#if IS_PY3
+
+/* In Python 3 use the bytes format character for raw data */
+#define SVN_SWIG_BYTES_FMT "y"
+
+#else
+
+/* In Python 2 use the string format character for raw data */
+#define SVN_SWIG_BYTES_FMT "s"
+
+#endif
 
 /* Py_ssize_t for old Pythons */
 /* This code is as recommended by: */
@@ -142,6 +164,35 @@ apr_status_t svn_swig_py_initialize(void
   return APR_SUCCESS;
 }
 
+FILE *svn_swig_py_as_file(PyObject *pyfile)
+{
+#if IS_PY3
+  FILE *fp = NULL;
+  int fd = PyObject_AsFileDescriptor(pyfile);
+  if (fd >= 0)
+    {
+      PyObject *mode_obj;
+      PyObject *mode_byte_obj = NULL;
+      char *mode = NULL;
+
+      /* If any Python API returns NULL, then the Python exception is set and
+         this function will return NULL signifying to the caller that an error
+         occurred. */
+      if (   NULL != (mode_obj = PyObject_GetAttrString(pyfile, "mode"))
+          && NULL != (mode_byte_obj = PyUnicode_AsUTF8String(mode_obj))
+          && NULL != (mode = PyBytes_AsString(mode_byte_obj)))
+        fp = fdopen(fd, mode);
+
+      Py_XDECREF(mode_obj);
+      Py_XDECREF(mode_byte_obj);
+    }
+
+  return fp;
+#else
+  return PyFile_AsFile(pyfile);
+#endif
+}
+
 int svn_swig_py_get_pool_arg(PyObject *args, swig_type_info *type,
     PyObject **py_pool, apr_pool_t **pool)
 {
@@ -150,14 +201,25 @@ int svn_swig_py_get_pool_arg(PyObject *a
   if (argnum >= 0)
     {
       PyObject *input = PyTuple_GET_ITEM(args, argnum);
-      if (input != Py_None && PyObject_HasAttrString(input, markValid))
+      if (input != Py_None)
         {
-          *pool = svn_swig_py_must_get_ptr(input, type, argnum+1);
-          if (*pool == NULL)
-            return 1;
-          *py_pool = input;
-          Py_INCREF(input);
-          return 0;
+          PyObject *fn;
+          if (NULL != (fn = PyObject_GetAttrString(input, markValid)))
+            {
+              Py_DECREF(fn);
+
+              *pool = svn_swig_py_must_get_ptr(input, type, argnum+1);
+              if (*pool == NULL)
+                return 1;
+              *py_pool = input;
+              Py_INCREF(input);
+              return 0;
+            }
+          else
+            {
+              /* Clear any getattr() error, it isn't needed. */
+              PyErr_Clear();
+            }
         }
     }
 
@@ -221,13 +283,20 @@ static int proxy_set_pool(PyObject **pro
     {
       if (pool == NULL)
         {
-          if (PyObject_HasAttrString(*proxy, setParentPool))
+          PyObject *setFn;
+          if (NULL != (setFn = PyObject_GetAttrString(*proxy, setParentPool)))
             {
-              result = PyObject_CallMethod(*proxy, setParentPool, emptyTuple);
+              result = PyObject_CallObject(setFn, NULL);
+              Py_DECREF(setFn);
               if (result == NULL)
                 return 1;
               Py_DECREF(result);
             }
+          else
+            {
+              /* Clear any getattr() error, it isn't needed. */
+              PyErr_Clear();
+            }
         }
       else
         {
@@ -285,23 +354,45 @@ static PyObject *svn_swig_NewPointerObjS
   return svn_swig_py_new_pointer_obj(ptr, typeinfo, py_pool, NULL);
 }
 
-/** Wrapper for SWIG_ConvertPtr */
-int svn_swig_py_convert_ptr(PyObject *input, void **obj, swig_type_info *type)
+static int svn_swig_ensure_valid_swig_wrapper(PyObject *input)
 {
-  if (PyObject_HasAttrString(input, assertValid))
+  PyObject *assertFn;
+  PyObject *unwrapFn;
+  if (NULL != (assertFn = PyObject_GetAttrString(input, assertValid)))
     {
-      PyObject *result = PyObject_CallMethod(input, assertValid, emptyTuple);
+      PyObject *result = PyObject_CallObject(assertFn, NULL);
+      Py_DECREF(assertFn);
       if (result == NULL)
         return 1;
       Py_DECREF(result);
     }
-  if (PyObject_HasAttrString(input, unwrap))
+  else
+    {
+      /* Clear any getattr() error, it isn't needed. */
+      PyErr_Clear();
+    }
+  if (NULL != (unwrapFn = PyObject_GetAttrString(input, unwrap)))
     {
-      input = PyObject_CallMethod(input, unwrap, emptyTuple);
+      input = PyObject_CallObject(unwrapFn, NULL);
+      Py_DECREF(unwrapFn);
       if (input == NULL)
         return 1;
       Py_DECREF(input);
     }
+  else
+    {
+      /* Clear any getattr() error, it isn't needed. */
+      PyErr_Clear();
+    }
+
+  return 0;
+}
+
+/** Wrapper for SWIG_ConvertPtr */
+int svn_swig_py_convert_ptr(PyObject *input, void **obj, swig_type_info *type)
+{
+  if (svn_swig_ensure_valid_swig_wrapper(input))
+    return 1;
 
   return SWIG_ConvertPtr(input, obj, type, SWIG_POINTER_EXCEPTION | 0);
 }
@@ -316,21 +407,8 @@ static int svn_swig_ConvertPtrString(PyO
 /** Wrapper for SWIG_MustGetPtr */
 void *svn_swig_py_must_get_ptr(void *input, swig_type_info *type, int argnum)
 {
-  if (PyObject_HasAttrString(input, assertValid))
-    {
-      PyObject *result = PyObject_CallMethod(input, assertValid, emptyTuple);
-      if (result == NULL)
-        return NULL;
-      Py_DECREF(result);
-    }
-
-  if (PyObject_HasAttrString(input, unwrap))
-    {
-      input = PyObject_CallMethod(input, unwrap, emptyTuple);
-      if (input == NULL)
-        return NULL;
-      Py_DECREF((PyObject *) input);
-    }
+  if (svn_swig_ensure_valid_swig_wrapper(input))
+    return NULL;
 
   return SWIG_MustGetPtr(input, type, argnum, SWIG_POINTER_EXCEPTION | 0);
 }
@@ -339,10 +417,12 @@ void *svn_swig_py_must_get_ptr(void *inp
 
 /*** Custom SubversionException stuffs. ***/
 
-void svn_swig_py_svn_exception(svn_error_t *error_chain)
+void svn_swig_py_build_svn_exception(PyObject **exc_class,
+                                     PyObject **exc_ob,
+                                     svn_error_t *error_chain)
 {
   PyObject *args_list, *args, *apr_err_ob, *message_ob, *file_ob, *line_ob;
-  PyObject *svn_module, *exc_class, *exc_ob;
+  PyObject *svn_module;
   svn_error_t *err;
 
   if (error_chain == NULL)
@@ -350,7 +430,7 @@ void svn_swig_py_svn_exception(svn_error
 
   /* Start with no references. */
   args_list = args = apr_err_ob = message_ob = file_ob = line_ob = NULL;
-  svn_module = exc_class = exc_ob = NULL;
+  svn_module = *exc_class = *exc_ob = NULL;
 
   if ((args_list = PyList_New(0)) == NULL)
     goto finished;
@@ -370,14 +450,14 @@ void svn_swig_py_svn_exception(svn_error
           Py_INCREF(Py_None);
           message_ob = Py_None;
         }
-      else if ((message_ob = PyString_FromString(err->message)) == NULL)
+      else if ((message_ob = PyStr_FromString(err->message)) == NULL)
         goto finished;
       if (err->file == NULL)
         {
           Py_INCREF(Py_None);
           file_ob = Py_None;
         }
-      else if ((file_ob = PyString_FromString(err->file)) == NULL)
+      else if ((file_ob = PyStr_FromString(err->file)) == NULL)
         goto finished;
       if ((line_ob = PyInt_FromLong(err->line)) == NULL)
         goto finished;
@@ -409,15 +489,12 @@ void svn_swig_py_svn_exception(svn_error
   /* Create the exception object chain. */
   if ((svn_module = PyImport_ImportModule((char *)"svn.core")) == NULL)
     goto finished;
-  if ((exc_class = PyObject_GetAttrString(svn_module,
-                                       (char *)"SubversionException")) == NULL)
-    goto finished;
-  if ((exc_ob = PyObject_CallMethod(exc_class, (char *)"_new_from_err_list",
-                                    (char *)"O", args_list)) == NULL)
-    goto finished;
-
-  /* Raise the exception. */
-  PyErr_SetObject(exc_class, exc_ob);
+  if ((*exc_class = PyObject_GetAttrString(svn_module,
+                                       (char *)"SubversionException")) != NULL)
+    {
+      *exc_ob = PyObject_CallMethod(*exc_class, (char *)"_new_from_err_list",
+                                    (char *)"O", args_list);
+    }
 
  finished:
   /* Release any references. */
@@ -428,14 +505,66 @@ void svn_swig_py_svn_exception(svn_error
   Py_XDECREF(file_ob);
   Py_XDECREF(line_ob);
   Py_XDECREF(svn_module);
-  Py_XDECREF(exc_class);
-  Py_XDECREF(exc_ob);
+}
+
+void svn_swig_py_svn_exception(svn_error_t *error_chain)
+{
+  PyObject *exc_class, *exc_ob;
+
+  /* First, we create the exception... */
+  svn_swig_py_build_svn_exception(&exc_class, &exc_ob, error_chain);
+  
+  /* ...then, we raise it.  If got only an exception class but no
+     instance, we'll raise the class without an instance. */
+  if (exc_class != NULL)
+    {
+      if (exc_ob != NULL)
+        {
+          PyErr_SetObject(exc_class, exc_ob);
+          Py_DECREF(exc_ob);
+        }
+      else 
+        {
+          PyErr_SetNone(exc_class);
+        }
+      Py_DECREF(exc_class);
+    }
 }
 
 
 
 /*** Helper/Conversion Routines ***/
 
+/* Function to get char * representation of bytes/str object. This is
+   the replacement of typemap(in, parse="s") and typemap(in, parse="z")
+   to accept both of bytes object and str object, and it assumes to be
+   used from those typemaps only.
+   Note: type of return value should be char const *, however, as SWIG
+   produces variables for C function without 'const' modifier, to avoid
+   ton of cast in SWIG produced C code we drop it from return value
+   types as well  */
+char *svn_swig_py_string_to_cstring(PyObject *input, int maybe_null,
+                                    const char * funcsym, const char * argsym)
+{
+    char *retval = NULL;
+    if (PyBytes_Check(input))
+      {
+        retval = PyBytes_AsString(input);
+      }
+    else if (PyUnicode_Check(input))
+      {
+        retval = (char *)PyStr_AsUTF8(input);
+      }
+    else if (input != Py_None || ! maybe_null)
+      {
+        PyErr_Format(PyExc_TypeError,
+                     "%s() argument %s must be bytes or str%s, not %s",
+                     funcsym, argsym, maybe_null?" or None":"",
+                     Py_TYPE(input)->tp_name);
+      }
+    return retval;
+}
+
 /* Functions for making Python wrappers around Subversion structs */
 static PyObject *make_ob_pool(void *pool)
 {
@@ -470,32 +599,89 @@ static PyObject *make_ob_error(svn_error
 
 /***/
 
+static void svn_swig_py_string_type_exception(int maybe_null)
+{
+  PyErr_Format(PyExc_TypeError, "not a bytes or a str%s",
+               maybe_null?" or None":"");
+}
+
 /* Conversion from Python single objects (not hashes/lists/etc.) to
    Subversion types. */
 static char *make_string_from_ob(PyObject *ob, apr_pool_t *pool)
 {
+  /* caller should not expect to raise TypeError: check return value
+     whether it is NULL or not, if needed */
+  if (PyBytes_Check(ob))
+    {
+      return apr_pstrdup(pool, PyBytes_AsString(ob));
+    }
+  if (PyUnicode_Check(ob))
+    {
+      /* PyStr_AsUTF8() may cause UnicodeEncodeError,
+         but apr_pstrdup() allows NULL for s */
+      return apr_pstrdup(pool, PyStr_AsUTF8(ob));
+    }
+  return NULL;
+}
+
+static char *make_string_from_ob_maybe_null(PyObject *ob, apr_pool_t *pool)
+{
+  char * retval;
   if (ob == Py_None)
-    return NULL;
-  if (! PyString_Check(ob))
     {
-      PyErr_SetString(PyExc_TypeError, "not a string");
       return NULL;
     }
-  return apr_pstrdup(pool, PyString_AS_STRING(ob));
+  retval = make_string_from_ob(ob, pool);
+  if (!retval)
+    {
+      if (!PyErr_Occurred())
+        {
+          svn_swig_py_string_type_exception(TRUE);
+        }
+    }
+  return retval;
 }
+
 static svn_string_t *make_svn_string_from_ob(PyObject *ob, apr_pool_t *pool)
 {
+  /* caller should not expect to raise TypeError: check return value
+     whether it is NULL or not, if needed */
+  if (PyBytes_Check(ob))
+    {
+      return svn_string_create(PyBytes_AsString(ob), pool);
+    }
+  if (PyUnicode_Check(ob))
+    {
+      /* PyStr_AsUTF8() may cause UnicodeEncodeError,
+         and svn_string_create() does not allows NULL for cstring */
+      const char *obstr = PyStr_AsUTF8(ob);
+      if (obstr)
+        {
+          return svn_string_create(obstr, pool);
+        }
+    }
+  return NULL;
+}
+
+static svn_string_t *make_svn_string_from_ob_maybe_null(PyObject *ob,
+                                                        apr_pool_t *pool)
+{
+  svn_string_t * retval;
   if (ob == Py_None)
-    return NULL;
-  if (! PyString_Check(ob))
     {
-      PyErr_SetString(PyExc_TypeError, "not a string");
       return NULL;
     }
-  return svn_string_create(PyString_AS_STRING(ob), pool);
+  retval = make_svn_string_from_ob(ob, pool);
+  if (!retval)
+    {
+      if (!PyErr_Occurred())
+        {
+          svn_swig_py_string_type_exception(TRUE);
+        }
+    }
+  return retval;
 }
 
-
 /***/
 
 static PyObject *convert_hash(apr_hash_t *hash,
@@ -527,7 +713,7 @@ static PyObject *convert_hash(apr_hash_t
             return NULL;
           }
         /* ### gotta cast this thing cuz Python doesn't use "const" */
-        if (PyDict_SetItemString(dict, (char *)key, value) == -1)
+        if (PyDict_SetItem(dict, PyBytes_FromString((char *)key), value) == -1)
           {
             Py_DECREF(value);
             Py_DECREF(dict);
@@ -552,11 +738,10 @@ static PyObject *convert_svn_string_t(vo
 
   const svn_string_t *s = value;
 
-  /* ### gotta cast this thing cuz Python doesn't use "const" */
-  return PyString_FromStringAndSize((void *)s->data, s->len);
+  return PyBytes_FromStringAndSize(s->data, s->len);
 }
 
-/* Convert a C string into a Python String object (or a reference to
+/* Convert a C string into a Python Bytes object (or a reference to
    Py_None if CSTRING is NULL). */
 static PyObject *cstring_to_pystring(const char *cstring)
 {
@@ -566,7 +751,7 @@ static PyObject *cstring_to_pystring(con
       Py_INCREF(Py_None);
       return retval;
     }
-  return PyString_FromString(cstring);
+  return PyBytes_FromString(cstring);
 }
 
 static PyObject *convert_svn_client_commit_item3_t(void *value, void *ctx)
@@ -642,8 +827,7 @@ PyObject *svn_swig_py_prophash_to_dict(a
 static PyObject *convert_string(void *value, void *ctx,
                                 PyObject *py_pool)
 {
-  /* ### gotta cast this thing cuz Python doesn't use "const" */
-  return PyString_FromString((const char *)value);
+  return PyBytes_FromString((const char *)value);
 }
 
 PyObject *svn_swig_py_stringhash_to_dict(apr_hash_t *hash)
@@ -725,7 +909,7 @@ svn_swig_py_propinheriteditemarray_to_di
         apr_hash_t *prop_hash = prop_inherited_item->prop_hash;
         PyObject *py_key, *py_value;
 
-        py_key = PyString_FromString(prop_inherited_item->path_or_url);
+        py_key = PyBytes_FromString(prop_inherited_item->path_or_url);
         if (py_key == NULL)
           goto error;
 
@@ -769,7 +953,7 @@ PyObject *svn_swig_py_proparray_to_dict(
 
         prop = APR_ARRAY_IDX(array, i, svn_prop_t);
 
-        py_key = PyString_FromString(prop.name);
+        py_key = PyBytes_FromString(prop.name);
         if (py_key == NULL)
           goto error;
 
@@ -780,8 +964,8 @@ PyObject *svn_swig_py_proparray_to_dict(
           }
         else
           {
-             py_value = PyString_FromStringAndSize((void *)prop.value->data,
-                                                   prop.value->len);
+             py_value = PyBytes_FromStringAndSize(prop.value->data,
+                                                  prop.value->len);
              if (py_value == NULL)
                {
                  Py_DECREF(py_key);
@@ -831,7 +1015,7 @@ PyObject *svn_swig_py_locationhash_to_di
             Py_DECREF(dict);
             return NULL;
           }
-        value = PyString_FromString((char *)v);
+        value = PyBytes_FromString((const char *)v);
         if (value == NULL)
           {
             Py_DECREF(key);
@@ -881,6 +1065,7 @@ DECLARE_SWIG_CONSTRUCTOR(info, svn_info_
 DECLARE_SWIG_CONSTRUCTOR(location_segment, svn_location_segment_dup)
 DECLARE_SWIG_CONSTRUCTOR(commit_info, svn_commit_info_dup)
 DECLARE_SWIG_CONSTRUCTOR(wc_notify, svn_wc_dup_notify)
+DECLARE_SWIG_CONSTRUCTOR(client_status, svn_client_status_dup)
 
 static PyObject *convert_log_changed_path(void *value, void *ctx,
                                           PyObject *py_pool)
@@ -895,7 +1080,7 @@ PyObject *svn_swig_py_c_strings_to_list(
 
     while ((s = *strings++) != NULL)
       {
-        PyObject *ob = PyString_FromString(s);
+        PyObject *ob = PyBytes_FromString(s);
 
         if (ob == NULL)
             goto error;
@@ -938,7 +1123,7 @@ PyObject *svn_swig_py_changed_path_hash_
             Py_DECREF(dict);
             return NULL;
         }
-      if (PyDict_SetItemString(dict, (char *)key, value) == -1)
+      if (PyDict_SetItem(dict, PyBytes_FromString((char *)key), value) == -1)
         {
           Py_DECREF(value);
           Py_DECREF(dict);
@@ -974,7 +1159,7 @@ PyObject *svn_swig_py_changed_path2_hash
             Py_DECREF(dict);
             return NULL;
         }
-      if (PyDict_SetItemString(dict, (char *)key, value) == -1)
+      if (PyDict_SetItem(dict, PyBytes_FromString((char *)key), value) == -1)
         {
           Py_DECREF(value);
           Py_DECREF(dict);
@@ -986,6 +1171,9 @@ PyObject *svn_swig_py_changed_path2_hash
   return dict;
 }
 
+#define TYPE_ERROR_DICT_STRING_KEY \
+        "dictionary keys aren't bytes or str objects"
+
 apr_hash_t *svn_swig_py_stringhash_from_dict(PyObject *dict,
                                              apr_pool_t *pool)
 {
@@ -1010,11 +1198,19 @@ apr_hash_t *svn_swig_py_stringhash_from_
       PyObject *key = PyList_GetItem(keys, i);
       PyObject *value = PyDict_GetItem(dict, key);
       const char *propname = make_string_from_ob(key, pool);
-      const char *propval = make_string_from_ob(value, pool);
-      if (! (propname && propval))
+      const char *propval;
+      if (!propname)
+        {
+          if (!PyErr_Occurred())
+            {
+              PyErr_SetString(PyExc_TypeError, TYPE_ERROR_DICT_STRING_KEY);
+            }
+          Py_DECREF(keys);
+          return NULL;
+        }
+      propval = make_string_from_ob_maybe_null(value, pool);
+      if (PyErr_Occurred())
         {
-          PyErr_SetString(PyExc_TypeError,
-                          "dictionary keys/values aren't strings");
           Py_DECREF(keys);
           return NULL;
         }
@@ -1047,17 +1243,27 @@ apr_hash_t *svn_swig_py_mergeinfo_from_d
       PyObject *key = PyList_GetItem(keys, i);
       PyObject *value = PyDict_GetItem(dict, key);
       const char *pathname = make_string_from_ob(key, pool);
-      const svn_rangelist_t *ranges = svn_swig_py_seq_to_array(value,
+      const svn_rangelist_t *ranges;
+      if (!pathname)
+        {
+          if (!PyErr_Occurred())
+            {
+              PyErr_SetString(PyExc_TypeError, TYPE_ERROR_DICT_STRING_KEY);
+            }
+          Py_DECREF(keys);
+          return NULL;
+        }
+      ranges = svn_swig_py_seq_to_array(value,
         sizeof(const svn_merge_range_t *),
         svn_swig_py_unwrap_struct_ptr,
         svn_swig_TypeQuery("svn_merge_range_t *"),
         pool
       );
 
-      if (! (pathname && ranges))
+      if (!ranges)
         {
           PyErr_SetString(PyExc_TypeError,
-                          "dictionary keys aren't strings or values aren't svn_merge_range_t *'s");
+                          "dictionary values aren't svn_merge_range_t *'s");
           Py_DECREF(keys);
           return NULL;
         }
@@ -1092,11 +1298,18 @@ apr_array_header_t *svn_swig_py_proparra
       PyObject *value = PyDict_GetItem(dict, key);
       svn_prop_t *prop = apr_palloc(pool, sizeof(*prop));
       prop->name = make_string_from_ob(key, pool);
-      prop->value = make_svn_string_from_ob(value, pool);
-      if (! (prop->name && prop->value))
+      if (! prop->name)
+        {
+          if (!PyErr_Occurred())
+            {
+              PyErr_SetString(PyExc_TypeError, TYPE_ERROR_DICT_STRING_KEY);
+            }
+          Py_DECREF(keys);
+          return NULL;
+        }
+      prop->value = make_svn_string_from_ob_maybe_null(value, pool);
+      if (PyErr_Occurred())
         {
-          PyErr_SetString(PyExc_TypeError,
-                          "dictionary keys/values aren't strings");
           Py_DECREF(keys);
           return NULL;
         }
@@ -1130,11 +1343,19 @@ apr_hash_t *svn_swig_py_prophash_from_di
       PyObject *key = PyList_GetItem(keys, i);
       PyObject *value = PyDict_GetItem(dict, key);
       const char *propname = make_string_from_ob(key, pool);
-      svn_string_t *propval = make_svn_string_from_ob(value, pool);
-      if (! (propname && propval))
+      svn_string_t *propval;
+      if (!propname)
+        {
+          if (!PyErr_Occurred())
+            {
+              PyErr_SetString(PyExc_TypeError, TYPE_ERROR_DICT_STRING_KEY);
+            }
+          Py_DECREF(keys);
+          return NULL;
+        }
+      propval = make_svn_string_from_ob_maybe_null(value, pool);
+      if (PyErr_Occurred())
         {
-          PyErr_SetString(PyExc_TypeError,
-                          "dictionary keys/values aren't strings");
           Py_DECREF(keys);
           return NULL;
         }
@@ -1172,8 +1393,10 @@ apr_hash_t *svn_swig_py_path_revs_hash_f
 
       if (!(path))
         {
-          PyErr_SetString(PyExc_TypeError,
-                          "dictionary keys aren't strings");
+          if (!PyErr_Occurred())
+            {
+              PyErr_SetString(PyExc_TypeError, TYPE_ERROR_DICT_STRING_KEY);
+            }
           Py_DECREF(keys);
           return NULL;
         }
@@ -1227,8 +1450,10 @@ apr_hash_t *svn_swig_py_struct_ptr_hash_
 
       if (!c_key)
         {
-          PyErr_SetString(PyExc_TypeError,
-                          "dictionary keys aren't strings");
+          if (!PyErr_Occurred())
+            {
+              PyErr_SetString(PyExc_TypeError, TYPE_ERROR_DICT_STRING_KEY);
+            }
           Py_DECREF(keys);
           return NULL;
         }
@@ -1252,8 +1477,21 @@ svn_swig_py_unwrap_string(PyObject *sour
                           void *baton)
 {
     const char **ptr_dest = destination;
-    *ptr_dest = PyString_AsString(source);
-
+    if (PyBytes_Check(source))
+      {
+        *ptr_dest = PyBytes_AsString(source);
+      }
+    else if (PyUnicode_Check(source))
+      {
+        *ptr_dest = PyStr_AsUTF8(source);
+      }
+    else
+      {
+        PyErr_Format(PyExc_TypeError,
+                        "Expected bytes or str object, %s found",
+                        Py_TYPE(source)->tp_name);
+        *ptr_dest = NULL;
+      }
     if (*ptr_dest != NULL)
         return 0;
     else
@@ -1371,7 +1609,7 @@ PyObject *svn_swig_py_array_to_list(cons
     for (i = 0; i < array->nelts; ++i)
       {
         PyObject *ob =
-          PyString_FromString(APR_ARRAY_IDX(array, i, const char *));
+          PyBytes_FromString(APR_ARRAY_IDX(array, i, const char *));
         if (ob == NULL)
           goto error;
         PyList_SET_ITEM(list, i, ob);
@@ -1425,7 +1663,7 @@ commit_item_array_to_list(const apr_arra
 }
 
 
- 
+
 /*** Errors ***/
 
 /* Convert a given SubversionException to an svn_error_t. On failure returns
@@ -1446,13 +1684,13 @@ static svn_error_t *exception_to_error(P
 
     if ((message_ob = PyObject_GetAttrString(exc, "message")) == NULL)
         goto finished;
-    message = PyString_AsString(message_ob);
+    message = PyStr_AsString(message_ob);
     if (PyErr_Occurred()) goto finished;
 
     if ((file_ob = PyObject_GetAttrString(exc, "file")) == NULL)
         goto finished;
     if (file_ob != Py_None)
-        file = PyString_AsString(file_ob);
+        file = PyStr_AsString(file_ob);
     if (PyErr_Occurred()) goto finished;
 
     if ((line_ob = PyObject_GetAttrString(exc, "line")) == NULL)
@@ -1679,7 +1917,8 @@ static svn_error_t *delete_entry(const c
 
   /* ### python doesn't have 'const' on the method name and format */
   if ((result = PyObject_CallMethod(ib->editor, (char *)"delete_entry",
-                                    (char *)"slOO&", path, revision, ib->baton,
+                                    (char *)SVN_SWIG_BYTES_FMT "lOO&",
+                                    path, revision, ib->baton,
                                     make_ob_pool, pool)) == NULL)
     {
       err = callback_exception_error();
@@ -1710,7 +1949,12 @@ static svn_error_t *add_directory(const
 
   /* ### python doesn't have 'const' on the method name and format */
   if ((result = PyObject_CallMethod(ib->editor, (char *)"add_directory",
-                                    (char *)"sOslO&", path, ib->baton,
+#if IS_PY3
+                                    (char *)"yOylO&",
+#else
+                                    (char *)"sOslO&",
+#endif
+                                    path, ib->baton,
                                     copyfrom_path, copyfrom_revision,
                                     make_ob_pool, dir_pool)) == NULL)
     {
@@ -1741,8 +1985,8 @@ static svn_error_t *open_directory(const
 
   /* ### python doesn't have 'const' on the method name and format */
   if ((result = PyObject_CallMethod(ib->editor, (char *)"open_directory",
-                                    (char *)"sOlO&", path, ib->baton,
-                                    base_revision,
+                                    (char *)SVN_SWIG_BYTES_FMT "OlO&",
+                                    path, ib->baton, base_revision,
                                     make_ob_pool, dir_pool)) == NULL)
     {
       err = callback_exception_error();
@@ -1771,9 +2015,14 @@ static svn_error_t *change_dir_prop(void
 
   /* ### python doesn't have 'const' on the method name and format */
   if ((result = PyObject_CallMethod(ib->editor, (char *)"change_dir_prop",
-                                    (char *)"Oss#O&", ib->baton, name,
+#if IS_PY3
+                                    (char *)"Oyy#O&",
+#else
+                                    (char *)"Oss#O&",
+#endif
+                                    ib->baton, name,
                                     value ? value->data : NULL,
-                                    value ? value->len : 0,
+                                    (Py_ssize_t) (value ? value->len : 0),
                                     make_ob_pool, pool)) == NULL)
     {
       err = callback_exception_error();
@@ -1810,7 +2059,12 @@ static svn_error_t *add_file(const char
 
   /* ### python doesn't have 'const' on the method name and format */
   if ((result = PyObject_CallMethod(ib->editor, (char *)"add_file",
-                                    (char *)"sOslO&", path, ib->baton,
+#if IS_PY3
+                                    (char *)"yOylO&",
+#else
+                                    (char *)"sOslO&",
+#endif
+                                    path, ib->baton,
                                     copyfrom_path, copyfrom_revision,
                                     make_ob_pool, file_pool)) == NULL)
     {
@@ -1842,8 +2096,8 @@ static svn_error_t *open_file(const char
 
   /* ### python doesn't have 'const' on the method name and format */
   if ((result = PyObject_CallMethod(ib->editor, (char *)"open_file",
-                                    (char *)"sOlO&", path, ib->baton,
-                                    base_revision,
+                                    (char *)SVN_SWIG_BYTES_FMT "OlO&",
+                                    path, ib->baton, base_revision,
                                     make_ob_pool, file_pool)) == NULL)
     {
       err = callback_exception_error();
@@ -1916,7 +2170,12 @@ static svn_error_t *apply_textdelta(void
 
   /* ### python doesn't have 'const' on the method name and format */
   if ((result = PyObject_CallMethod(ib->editor, (char *)"apply_textdelta",
-                                    (char *)"(Os)", ib->baton,
+#if IS_PY3
+                                    (char *)"(Oy)",
+#else
+                                    (char *)"(Os)",
+#endif
+                                    ib->baton,
                                     base_checksum)) == NULL)
     {
       err = callback_exception_error();
@@ -1961,9 +2220,14 @@ static svn_error_t *change_file_prop(voi
 
   /* ### python doesn't have 'const' on the method name and format */
   if ((result = PyObject_CallMethod(ib->editor, (char *)"change_file_prop",
-                                    (char *)"Oss#O&", ib->baton, name,
+#if IS_PY3
+                                    (char *)"Oyy#O&",
+#else
+                                    (char *)"Oss#O&",
+#endif
+                                    ib->baton, name,
                                     value ? value->data : NULL,
-                                    value ? value->len : 0,
+                                    (Py_ssize_t) (value ? value->len : 0),
                                     make_ob_pool, pool)) == NULL)
     {
       err = callback_exception_error();
@@ -1991,7 +2255,12 @@ static svn_error_t *close_file(void *fil
 
   /* ### python doesn't have 'const' on the method name and format */
   if ((result = PyObject_CallMethod(ib->editor, (char *)"close_file",
-                                    (char *)"(Os)", ib->baton,
+#if IS_PY3
+                                    (char *)"(Oy)",
+#else
+                                    (char *)"(Os)",
+#endif
+                                    ib->baton,
                                     text_checksum)) == NULL)
     {
       err = callback_exception_error();
@@ -2099,7 +2368,7 @@ static svn_error_t *parse_fn3_uuid_recor
 
   /* ### python doesn't have 'const' on the method name and format */
   if ((result = PyObject_CallMethod(ib->editor, (char *)"uuid_record",
-                                    (char *)"sO&", uuid,
+                                    (char *)SVN_SWIG_BYTES_FMT "O&", uuid,
                                     make_ob_pool, pool)) == NULL)
     {
       err = callback_exception_error();
@@ -2188,9 +2457,15 @@ static svn_error_t *parse_fn3_set_revisi
 
   /* ### 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,
+#if IS_PY3
+                                    (char *)"Oyy#",
+#else
+                                    (char *)"Oss#",
+#endif
+                                    ib->baton, name,
                                     value ? value->data : NULL,
-                                    value ? value->len : 0)) == NULL)
+                                    (Py_ssize_t) (value ? value->len : 0)))
+      == NULL)
     {
       err = callback_exception_error();
       goto finished;
@@ -2218,9 +2493,15 @@ static svn_error_t *parse_fn3_set_node_p
 
   /* ### 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,
+#if IS_PY3
+                                    (char *)"Oyy#",
+#else
+                                    (char *)"Oss#",
+#endif
+                                    ib->baton, name,
                                     value ? value->data : NULL,
-                                    value ? value->len : 0)) == NULL)
+                                    (Py_ssize_t) (value ? value->len : 0)))
+      == NULL)
     {
       err = callback_exception_error();
       goto finished;
@@ -2247,7 +2528,8 @@ static svn_error_t *parse_fn3_delete_nod
 
   /* ### 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)
+                                    (char *)"O" SVN_SWIG_BYTES_FMT,
+                                    ib->baton, name)) == NULL)
     {
       err = callback_exception_error();
       goto finished;
@@ -2293,8 +2575,8 @@ static svn_error_t *parse_fn3_set_fullte
                                            void *node_baton)
 {
   item_baton *ib = node_baton;
-  PyObject *result;
-  svn_error_t *err;
+  PyObject *result = NULL;
+  svn_error_t *err = SVN_NO_ERROR;
 
   svn_swig_py_acquire_py_lock();
 
@@ -2316,14 +2598,17 @@ static svn_error_t *parse_fn3_set_fullte
       /* 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 (*stream == NULL)
+        {
+          err = callback_exception_error();
+          goto finished;
+        }
     }
 
   /* 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:
+finished:
+  Py_XDECREF(result);
   svn_swig_py_release_py_lock();
   return err;
 }
@@ -2432,14 +2717,23 @@ apr_file_t *svn_swig_py_make_file(PyObje
 {
   apr_file_t *apr_file = NULL;
   apr_status_t apr_err;
+  const char* fname = NULL;
 
   if (py_file == NULL || py_file == Py_None)
     return NULL;
 
-  if (PyString_Check(py_file))
+  /* check if input is a path  */
+  if (PyBytes_Check(py_file))
+    {
+      fname = PyBytes_AsString(py_file);
+    }
+  else if (PyUnicode_Check(py_file))
+    {
+      fname = PyStr_AsUTF8(py_file);
+    }
+  if (fname)
     {
       /* input is a path -- just open an apr_file_t */
-      char* fname = PyString_AS_STRING(py_file);
       apr_err = apr_file_open(&apr_file, fname,
                               APR_CREATE | APR_READ | APR_WRITE,
                               APR_OS_DEFAULT, pool);
@@ -2452,25 +2746,26 @@ apr_file_t *svn_swig_py_make_file(PyObje
           return NULL;
         }
     }
-  else if (PyFile_Check(py_file))
+  else
     {
-      FILE *file;
-      apr_os_file_t osfile;
+      FILE *file = svn_swig_py_as_file(py_file);
 
       /* input is a file object -- convert to apr_file_t */
-      file = PyFile_AsFile(py_file);
+      if (file != NULL)
+        {
 #ifdef WIN32
-      osfile = (apr_os_file_t)_get_osfhandle(_fileno(file));
+          apr_os_file_t osfile = (apr_os_file_t)_get_osfhandle(_fileno(file));
 #else
-      osfile = (apr_os_file_t)fileno(file);
+          apr_os_file_t osfile = (apr_os_file_t)fileno(file);
 #endif
-      apr_err = apr_os_file_put(&apr_file, &osfile, O_CREAT | O_WRONLY, pool);
-      if (apr_err)
-        {
-          char buf[256];
-          apr_strerror(apr_err, buf, sizeof(buf));
-          PyErr_Format(PyExc_IOError, "apr_os_file_put failed: %s", buf);
-          return NULL;
+          apr_err = apr_os_file_put(&apr_file, &osfile, O_CREAT | O_WRONLY, pool);
+          if (apr_err)
+            {
+              char buf[256];
+              apr_strerror(apr_err, buf, sizeof(buf));
+              PyErr_Format(PyExc_IOError, "apr_os_file_put failed: %s", buf);
+              return NULL;
+            }
         }
     }
   return apr_file;
@@ -2482,7 +2777,6 @@ read_handler_pyio(void *baton, char *buf
 {
   PyObject *result;
   PyObject *py_io = baton;
-  apr_size_t bytes;
   svn_error_t *err = SVN_NO_ERROR;
 
   if (py_io == Py_None)
@@ -2499,10 +2793,17 @@ read_handler_pyio(void *baton, char *buf
     {
       err = callback_exception_error();
     }
-  else if (PyString_Check(result))
+  else if (PyBytes_Check(result))
     {
-      bytes = PyString_GET_SIZE(result);
-      if (bytes > *len)
+      Py_ssize_t bytes;
+      char *result_str;
+
+      if (   -1 == PyBytes_AsStringAndSize(result, &result_str, &bytes)
+          || result_str == NULL)
+        {
+          err = callback_exception_error();
+        }
+      else if (bytes > *len)
         {
           err = callback_bad_return_error("Too many bytes");
         }
@@ -2510,12 +2811,12 @@ read_handler_pyio(void *baton, char *buf
         {
           /* Writeback, in case this was a short read, indicating EOF */
           *len = bytes;
-          memcpy(buffer, PyString_AS_STRING(result), *len);
+          memcpy(buffer, result_str, *len);
         }
     }
   else
     {
-      err = callback_bad_return_error("Not a string");
+      err = callback_bad_return_error("Not a bytes object");
     }
   Py_XDECREF(result);
   svn_swig_py_release_py_lock();
@@ -2534,7 +2835,8 @@ write_handler_pyio(void *baton, const ch
     {
       svn_swig_py_acquire_py_lock();
       if ((result = PyObject_CallMethod(py_io, (char *)"write",
-                                        (char *)"s#", data, *len)) == NULL)
+                                       (char *) SVN_SWIG_BYTES_FMT "#",
+                                       data, (Py_ssize_t) *len)) == NULL)
         {
           err = callback_exception_error();
         }
@@ -2575,17 +2877,39 @@ svn_swig_py_stream_destroy(void *py_io)
 svn_stream_t *
 svn_swig_py_make_stream(PyObject *py_io, apr_pool_t *pool)
 {
-  svn_stream_t *stream;
+  PyObject *_stream = NULL;
+  void *result = NULL;
+  swig_type_info *typeinfo = svn_swig_TypeQuery("svn_stream_t *");
+
+  if (svn_swig_py_convert_ptr(py_io, &result, typeinfo) != 0) {
+      PyErr_Clear();
+      if (PyObject_HasAttrString(py_io, "_stream")) {
+        _stream = PyObject_GetAttrString(py_io, "_stream");
+        if (svn_swig_py_convert_ptr(_stream, &result, typeinfo) != 0) {
+          PyErr_Clear();
+        }
+      }
+  }
+  if (result == NULL) {
+    if (!PyObject_HasAttrString(py_io, "read")
+        && !PyObject_HasAttrString(py_io, "write")) {
+      PyErr_SetString(PyExc_TypeError,
+                      "expecting a svn_stream_t or file like object");
+      goto finished;
+    }
+    result = svn_stream_create(py_io, pool);
+    svn_stream_set_read2(result, read_handler_pyio, NULL);
+    svn_stream_set_write(result, write_handler_pyio);
+    svn_stream_set_close(result, close_handler_pyio);
+    apr_pool_cleanup_register(pool, py_io, svn_swig_py_stream_destroy,
+                              apr_pool_cleanup_null);
+    Py_INCREF(py_io);
+  }
 
-  stream = svn_stream_create(py_io, pool);
-  svn_stream_set_read2(stream, read_handler_pyio, NULL);
-  svn_stream_set_write(stream, write_handler_pyio);
-  svn_stream_set_close(stream, close_handler_pyio);
-  apr_pool_cleanup_register(pool, py_io, svn_swig_py_stream_destroy,
-                            apr_pool_cleanup_null);
-  Py_INCREF(py_io);
+finished:
+  Py_XDECREF(_stream);
 
-  return stream;
+  return result;
 }
 
 PyObject *
@@ -2619,13 +2943,24 @@ void svn_swig_py_notify_func(void *baton
   PyObject *function = baton;
   PyObject *result;
   svn_error_t *err = SVN_NO_ERROR;
+  PyObject *exc, *exc_type, *exc_traceback;
 
   if (function == NULL || function == Py_None)
     return;
 
   svn_swig_py_acquire_py_lock();
+
+  /* As caller can't understand Python context and we can't notify if
+     Python call back function raise exception to caller, we must catch it
+     if it is occurred, and restore error indicator */
+  PyErr_Fetch(&exc_type, &exc, &exc_traceback);
+
   if ((result = PyObject_CallFunction(function,
+#if IS_PY3
+                                      (char *)"(yiiyiii)",
+#else
                                       (char *)"(siisiii)",
+#endif
                                       path, action, kind,
                                       mime_type,
                                       content_state, prop_state,
@@ -2644,6 +2979,9 @@ void svn_swig_py_notify_func(void *baton
   /* Our error has no place to go. :-( */
   svn_error_clear(err);
 
+  /* Also, restore error indicator */
+  PyErr_Restore(exc_type, exc, exc_traceback);
+
   svn_swig_py_release_py_lock();
 }
 
@@ -2655,12 +2993,18 @@ void svn_swig_py_notify_func2(void *bato
   PyObject *function = baton;
   PyObject *result;
   svn_error_t *err = SVN_NO_ERROR;
+  PyObject *exc, *exc_type, *exc_traceback;
 
   if (function == NULL || function == Py_None)
     return;
 
   svn_swig_py_acquire_py_lock();
 
+  /* As caller can't understand Python context and we can't notify if
+     Python call back function raise exception to caller, we must catch it
+     if it is occurred, and restore error indicator */
+  PyErr_Fetch(&exc_type, &exc, &exc_traceback);
+
   if ((result = PyObject_CallFunction(function,
                                       (char *)"(O&O&)",
                                       make_ob_wc_notify, notify,
@@ -2679,6 +3023,9 @@ void svn_swig_py_notify_func2(void *bato
   /* Our error has no place to go. :-( */
   svn_error_clear(err);
 
+  /* Also, restore error indicator */
+  PyErr_Restore(exc_type, exc, exc_traceback);
+
   svn_swig_py_release_py_lock();
 }
 
@@ -2689,12 +3036,20 @@ void svn_swig_py_status_func(void *baton
   PyObject *function = baton;
   PyObject *result;
   svn_error_t *err = SVN_NO_ERROR;
+  PyObject *exc, *exc_type, *exc_traceback;
 
   if (function == NULL || function == Py_None)
     return;
 
   svn_swig_py_acquire_py_lock();
-  if ((result = PyObject_CallFunction(function, (char *)"sO&", path,
+
+  /* As caller can't understand Python context and we can't notify if
+     Python call back function raise exception to caller, we must catch it
+     if it is occurred, and restore error indicator */
+  PyErr_Fetch(&exc_type, &exc, &exc_traceback);
+
+  if ((result = PyObject_CallFunction(function,
+                                      (char *)SVN_SWIG_BYTES_FMT "O&", path,
                                       make_ob_wc_status, status)) == NULL)
     {
       err = callback_exception_error();
@@ -2710,7 +3065,64 @@ void svn_swig_py_status_func(void *baton
   /* Our error has no place to go. :-( */
   svn_error_clear(err);
 
+  /* Also, restore error indicator */
+  PyErr_Restore(exc_type, exc, exc_traceback);
+
+  svn_swig_py_release_py_lock();
+}
+
+svn_error_t *svn_swig_py_client_status_func(void *baton,
+                                            const char *path,
+                                            const svn_client_status_t *status,
+                                            apr_pool_t *scratch_pool)
+{
+  PyObject *function = baton;
+  PyObject *result;
+  svn_error_t *err = SVN_NO_ERROR;
+  PyObject *exc, *exc_type, *exc_traceback;
+
+  if (function == NULL || function == Py_None)
+    return err;
+
+  svn_swig_py_acquire_py_lock();
+
+  if (status == NULL)
+    {
+      result = PyObject_CallFunction(function,
+#if IS_PY3
+                                     (char *)"yOO&",
+#else
+                                     (char *)"sOO&",
+#endif
+                                     path, Py_None,
+                                     make_ob_pool, scratch_pool);
+    }
+  else
+    {
+      result = PyObject_CallFunction(function,
+#if IS_PY3
+                                     (char *)"yO&O&",
+#else
+                                     (char *)"sO&O&",
+#endif
+                                     path,
+                                     make_ob_client_status, status,
+                                     make_ob_pool, scratch_pool);
+    }
+  if (result == NULL)
+    {
+      err = callback_exception_error();
+    }
+  else
+    {
+      /* The callback shouldn't be returning anything. */
+      if (result != Py_None)
+        err = callback_bad_return_error("Not None");
+      Py_DECREF(result);
+    }
+
   svn_swig_py_release_py_lock();
+  return err;
 }
 
 
@@ -2733,7 +3145,12 @@ svn_error_t *svn_swig_py_delta_path_driv
                                                  "void *",
                                                  NULL);
 
-  result = PyObject_CallFunction(function, (char *)"OsO&",
+  result = PyObject_CallFunction(function,
+#if IS_PY3
+                                 (char *)"OyO&",
+#else
+                                 (char *)"OsO&",
+#endif
                                  py_parent_baton,
                                  path, make_ob_pool, pool);
 
@@ -2768,12 +3185,20 @@ void svn_swig_py_status_func2(void *bato
   PyObject *function = baton;
   PyObject *result;
   svn_error_t *err = SVN_NO_ERROR;
+  PyObject *exc, *exc_type, *exc_traceback;
 
   if (function == NULL || function == Py_None)
     return;
 
   svn_swig_py_acquire_py_lock();
-  if ((result = PyObject_CallFunction(function, (char *)"sO&", path,
+
+  /* As caller can't understand Python context and we can't notify if
+     Python call back function raise exception to caller, we must catch it
+     if it is occurred, and restore error indicator */
+  PyErr_Fetch(&exc_type, &exc, &exc_traceback);
+
+  if ((result = PyObject_CallFunction(function,
+                                      (char *)SVN_SWIG_BYTES_FMT "O&", path,
                                       make_ob_wc_status, status)) == NULL)
     {
       err = callback_exception_error();
@@ -2787,8 +3212,10 @@ void svn_swig_py_status_func2(void *bato
     }
 
   /* Our error has no place to go. :-( */
-  if (err)
-    svn_error_clear(err);
+  svn_error_clear(err);
+
+  /* Also, restore error indicator */
+  PyErr_Restore(exc_type, exc, exc_traceback);
 
   svn_swig_py_release_py_lock();
 }
@@ -2877,7 +3304,7 @@ svn_error_t *svn_swig_py_fs_lock_callbac
   svn_swig_py_acquire_py_lock();
 
   if ((result = PyObject_CallFunction(py_callback,
-                                      (char *)"sO&O&O&",
+                                      (char *)SVN_SWIG_BYTES_FMT "O&O&O&",
                                       path,
                                       make_ob_lock, lock,
                                       make_ob_error, fs_err,
@@ -2943,23 +3370,34 @@ svn_error_t *svn_swig_py_get_commit_log_
 
   if (result == Py_None)
     {
-      Py_DECREF(result);
       *log_msg = NULL;
       err = SVN_NO_ERROR;
     }
-  else if (PyString_Check(result))
+  else if (PyBytes_Check(result))
     {
-      *log_msg = apr_pstrdup(pool, PyString_AS_STRING(result));
-      Py_DECREF(result);
+      *log_msg = apr_pstrdup(pool, PyBytes_AsString(result));
       err = SVN_NO_ERROR;
     }
+  else if (PyUnicode_Check(result))
+    {
+      /* PyStr_AsUTF8() may cause UnicodeEncodeError,
+         but apr_pstrdup() allows NULL for s */
+      if ((*log_msg = apr_pstrdup(pool, PyStr_AsUTF8(result))) == NULL)
+        {
+          err = callback_exception_error();
+        }
+      else
+        {
+          err = SVN_NO_ERROR;
+        }
+    }
   else
     {
-      Py_DECREF(result);
-      err = callback_bad_return_error("Not a string");
+      err = callback_bad_return_error("Not a bytes or str object");
     }
+  Py_DECREF(result);
 
- finished:
+finished:
   svn_swig_py_release_py_lock();
   return err;
 }
@@ -2998,7 +3436,11 @@ svn_error_t *svn_swig_py_repos_authz_fun
     }
 
   if ((result = PyObject_CallFunction(function,
+#if IS_PY3
+                                      (char *)"OyO",
+#else
                                       (char *)"OsO",
+#endif
                                       py_root, path, py_pool)) == NULL)
     {
       err = callback_exception_error();
@@ -3035,7 +3477,7 @@ svn_error_t *svn_swig_py_repos_history_f
 
   svn_swig_py_acquire_py_lock();
   if ((result = PyObject_CallFunction(function,
-                                      (char *)"slO&",
+                                      (char *)SVN_SWIG_BYTES_FMT "lO&",
                                       path, revision,
                                       make_ob_pool, pool)) == NULL)
     {
@@ -3162,7 +3604,7 @@ svn_error_t *svn_swig_py_proplist_receiv
     }
 
   result = PyObject_CallFunction(receiver,
-                                 (char *)"sOOO",
+                                 (char *)SVN_SWIG_BYTES_FMT "OOO",
                                  path, py_props, py_iprops, py_pool);
   if (result == NULL)
     {
@@ -3222,7 +3664,11 @@ svn_error_t *svn_swig_py_log_receiver(vo
     }
 
   if ((result = PyObject_CallFunction(receiver,
+#if IS_PY3
+                                      (char *)"OlyyyO",
+#else
                                       (char *)"OlsssO",
+#endif
                                       chpaths, rev, author, date, msg,
                                       py_pool)) == NULL)
     {
@@ -3300,7 +3746,7 @@ svn_error_t *svn_swig_py_info_receiver_f
   svn_swig_py_acquire_py_lock();
 
   if ((result = PyObject_CallFunction(receiver,
-                                      (char *)"sO&O&",
+                                      (char *)SVN_SWIG_BYTES_FMT "O&O&",
                                       path, make_ob_info, info,
                                       make_ob_pool, pool)) == NULL)
     {
@@ -3370,9 +3816,13 @@ svn_error_t *svn_swig_py_client_blame_re
 
   if ((result = PyObject_CallFunction(receiver,
                                       (char *)
-                                      (SVN_APR_INT64_T_PYCFMT "lsssO&"),
-                                      line_no, revision, author, date, line,
-                                      make_ob_pool, pool)) == NULL)
+#if IS_PY3
+                                      "LlyyyO&",
+#else
+                                      "LlsssO&",
+#endif
+                                      (PY_LONG_LONG)line_no, revision, author,
+                                      date, line, make_ob_pool, pool)) == NULL)
     {
       err = callback_exception_error();
     }
@@ -3402,7 +3852,11 @@ svn_error_t *svn_swig_py_changelist_rece
   svn_swig_py_acquire_py_lock();
 
   if ((result = PyObject_CallFunction(receiver,
+#if IS_PY3
+                                      (char *)"yyO&",
+#else
                                       (char *)"ssO&",
+#endif
                                       path, changelist,
                                       make_ob_pool, pool)) == NULL)
     {
@@ -3437,7 +3891,7 @@ svn_swig_py_auth_gnome_keyring_unlock_pr
   svn_swig_py_acquire_py_lock();
 
   if ((result = PyObject_CallFunction(function,
-                                      (char *)"sO&",
+                                      (char *)SVN_SWIG_BYTES_FMT "O&",
                                       keyring_name,
                                       make_ob_pool, pool)) == NULL)
     {
@@ -3445,7 +3899,11 @@ svn_swig_py_auth_gnome_keyring_unlock_pr
     }
   else
     {
-      *keyring_passwd = make_string_from_ob(result, pool);
+      *keyring_passwd = make_string_from_ob_maybe_null(result, pool);
+      if (PyErr_Occurred())
+        {
+          err = callback_exception_error();
+        }
       Py_DECREF(result);
     }
 
@@ -3473,7 +3931,11 @@ svn_swig_py_auth_simple_prompt_func(svn_
   svn_swig_py_acquire_py_lock();
 
   if ((result = PyObject_CallFunction(function,
+#if IS_PY3
+                                      (char *)"yylO&",
+#else
                                       (char *)"sslO&",
+#endif
                                       realm, username, may_save,
                                       make_ob_pool, pool)) == NULL)
     {
@@ -3524,7 +3986,7 @@ svn_swig_py_auth_username_prompt_func(sv
   svn_swig_py_acquire_py_lock();
 
   if ((result = PyObject_CallFunction(function,
-                                      (char *)"slO&",
+                                      (char *)SVN_SWIG_BYTES_FMT "lO&",
                                       realm, may_save,
                                       make_ob_pool, pool)) == NULL)
     {
@@ -3576,7 +4038,8 @@ svn_swig_py_auth_ssl_server_trust_prompt
 
   svn_swig_py_acquire_py_lock();
 
-  if ((result = PyObject_CallFunction(function, (char *)"slO&lO&",
+  if ((result = PyObject_CallFunction(function,
+                  (char *)SVN_SWIG_BYTES_FMT "lO&lO&",
                   realm, failures, make_ob_auth_ssl_server_cert_info,
                   cert_info, may_save, make_ob_pool, pool)) == NULL)
     {
@@ -3627,7 +4090,7 @@ svn_swig_py_auth_ssl_client_cert_prompt_
   svn_swig_py_acquire_py_lock();
 
   if ((result = PyObject_CallFunction(function,
-                                      (char *)"slO&",
+                                      (char *)SVN_SWIG_BYTES_FMT "lO&",
                                       realm, may_save,
                                       make_ob_pool, pool)) == NULL)
     {
@@ -3678,7 +4141,7 @@ svn_swig_py_auth_ssl_client_cert_pw_prom
   svn_swig_py_acquire_py_lock();
 
   if ((result = PyObject_CallFunction(function,
-                                      (char *)"slO&",
+                                      (char *)SVN_SWIG_BYTES_FMT "lO&",
                                       realm, may_save,
                                       make_ob_pool, pool)) == NULL)
     {
@@ -3745,7 +4208,12 @@ svn_swig_py_config_auth_walk_func(svn_bo
       goto finished;
     }
 
-  if ((result = PyObject_CallFunction(function, (char *)"ssOO",
+  if ((result = PyObject_CallFunction(function,
+#if IS_PY3
+                                      (char *)"yyOO",
+#else
+                                      (char *)"ssOO",
+#endif
                                       cred_kind, realmstring,
                                       py_hash, py_scratch_pool)) == NULL)
     {
@@ -3844,16 +4312,21 @@ ra_callbacks_get_wc_prop(void *baton,
     }
 
   if ((result = PyObject_CallFunction(py_callback,
-                                      (char *)"ssO&", path, name,
+#if IS_PY3
+                                      (char *)"yyO&",
+#else
+                                      (char *)"ssO&",
+#endif
+                                      path, name,
                                       make_ob_pool, pool)) == NULL)
     {
       err = callback_exception_error();
     }
   else if (result != Py_None)
     {
-      char *buf;
       Py_ssize_t len;
-      if (PyString_AsStringAndSize(result, &buf, &len) == -1)
+      char *buf;
+      if (PyBytes_AsStringAndSize(result, &buf, &len) == -1)
         {
           err = callback_exception_error();
         }
@@ -3896,14 +4369,19 @@ ra_callbacks_push_or_set_wc_prop(const c
       goto finished;
     }
 
-  if ((py_value = PyString_FromStringAndSize(value->data, value->len)) == NULL)
+  if ((py_value = PyBytes_FromStringAndSize(value->data, value->len)) == NULL)
     {
       err = callback_exception_error();
       goto finished;
     }
 
   if ((result = PyObject_CallFunction(py_callback,
-                                      (char *)"ssOO&", path, name, py_value,
+#if IS_PY3
+                                      (char *)"yyOO&",
+#else
+                                      (char *)"ssOO&",
+#endif
+                                      path, name, py_value,
                                       make_ob_pool, pool)) == NULL)
     {
       err = callback_exception_error();
@@ -3966,7 +4444,12 @@ ra_callbacks_invalidate_wc_props(void *b
     }
 
   if ((result = PyObject_CallFunction(py_callback,
-                                      (char *)"ssO&", path, name,
+#if IS_PY3
+                                      (char *)"yyO&",
+#else
+                                      (char *)"ssO&",
+#endif
+                                      path, name,
                                       make_ob_pool, pool)) == NULL)
     {
       err = callback_exception_error();
@@ -3988,11 +4471,17 @@ ra_callbacks_progress_func(apr_off_t pro
 {
   PyObject *callbacks = (PyObject *)baton;
   PyObject *py_callback, *py_progress, *py_total, *result;
+  PyObject *exc, *exc_type, *exc_traceback;
 
   py_progress = py_total = NULL;
 
   svn_swig_py_acquire_py_lock();
 
+  /* As caller can't understand Python context and we can't notify if
+     Python call back function raise exception to caller, we must catch it
+     if it is occurred, and restore error indicator */
+  PyErr_Fetch(&exc_type, &exc, &exc_traceback);
+
   py_callback = PyObject_GetAttrString(callbacks,
                                        (char *)"progress_func");
   if (py_callback == NULL)
@@ -4032,6 +4521,9 @@ ra_callbacks_progress_func(apr_off_t pro
 
   Py_XDECREF(result);
 finished:
+  /* Restore error indicator */
+  PyErr_Restore(exc_type, exc, exc_traceback);
+
   Py_XDECREF(py_callback);
   Py_XDECREF(py_progress);
   Py_XDECREF(py_total);
@@ -4095,7 +4587,7 @@ ra_callbacks_get_client_string(void *bat
     }
   else if (result != Py_None)
     {
-      if ((*name = PyString_AsString(result)) == NULL)
+      if ((*name = PyBytes_AsString(result)) == NULL)
         {
           err = callback_exception_error();
         }
@@ -4198,7 +4690,11 @@ svn_error_t *svn_swig_py_commit_callback
   svn_swig_py_acquire_py_lock();
 
   if ((result = PyObject_CallFunction(receiver,
+#if IS_PY3
+                                      (char *)"lyy",
+#else
                                       (char *)"lss",
+#endif
                                       new_revision, date, author)) == NULL)
     {
       err = callback_exception_error();
@@ -4250,7 +4746,7 @@ svn_error_t *svn_swig_py_ra_file_rev_han
     }
 
   if ((result = PyObject_CallFunction(handler,
-                                      (char *)"slOOO&",
+                                      (char *)SVN_SWIG_BYTES_FMT "lOOO&",
                                       path, rev, py_rev_props, py_prop_diffs,
                                       make_ob_pool, pool)) == NULL)
     {
@@ -4296,7 +4792,7 @@ svn_error_t *svn_swig_py_ra_lock_callbac
   svn_swig_py_acquire_py_lock();
 
   if ((result = PyObject_CallFunction(py_callback,
-                                     (char *)"sbO&O&O&",
+                                     (char *)SVN_SWIG_BYTES_FMT "bO&O&O&",
                                      path, do_lock,
                                      make_ob_lock, lock,
                                      make_ob_error, ra_err,
@@ -4333,7 +4829,11 @@ static svn_error_t *reporter_set_path(vo
 
   if ((result = PyObject_CallMethod(py_reporter,
                                     (char *)"set_path",
+#if IS_PY3
+                                    (char *)"ylbyO&",
+#else
                                     (char *)"slbsO&",
+#endif
                                     path, revision,
                                     start_empty, lock_token,
                                     make_ob_pool, pool)) == NULL)
@@ -4366,7 +4866,7 @@ static svn_error_t *reporter_delete_path
 
   if ((result = PyObject_CallMethod(py_reporter,
                                     (char *)"delete_path",
-                                    (char *)"sO&",
+                                    (char *)SVN_SWIG_BYTES_FMT "O&",
                                     path,
                                     make_ob_pool, pool)) == NULL)
     {
@@ -4402,7 +4902,11 @@ static svn_error_t *reporter_link_path(v
 
   if ((result = PyObject_CallMethod(py_reporter,
                                     (char *)"link_path",
+#if IS_PY3
+                                    (char *)"yylbsO&",
+#else
                                     (char *)"sslbsO&",
+#endif
                                     path, url, revision,
                                     start_empty, lock_token,
                                     make_ob_pool, pool)) == NULL)
@@ -4533,7 +5037,11 @@ wc_diff_callbacks2_file_changed_or_added
     }
 
   result = PyObject_CallFunction(py_callback,
+#if IS_PY3
+                                 (char *)"O&yyyllyyO&O&",
+#else
                                  (char *)"O&sssllssO&O&",
+#endif
                                  make_ob_wc_adm_access, adm_access,
                                  path,
                                  tmpfile1, tmpfile2,
@@ -4656,7 +5164,11 @@ wc_diff_callbacks2_file_deleted(svn_wc_a
     }
 
   result = PyObject_CallFunction(py_callback,
+#if IS_PY3
+                                 (char *)"O&yyyyyO&",
+#else
                                  (char *)"O&sssssO&",
+#endif
                                  make_ob_wc_adm_access, adm_access,
                                  path,
                                  tmpfile1, tmpfile2,
@@ -4710,7 +5222,11 @@ wc_diff_callbacks2_dir_added(svn_wc_adm_
     }
 
   result = PyObject_CallFunction(py_callback,
+#if IS_PY3
+                                 (char *)"O&yl",
+#else
                                  (char *)"O&sl",
+#endif
                                  make_ob_wc_adm_access, adm_access,
                                  path, rev);
   if (result == NULL)
@@ -4760,7 +5276,7 @@ wc_diff_callbacks2_dir_deleted(svn_wc_ad
     }
 
   result = PyObject_CallFunction(py_callback,
-                                 (char *)"O&s",
+                                 (char *)"O&" SVN_SWIG_BYTES_FMT,
                                  make_ob_wc_adm_access, adm_access, path);
   if (result == NULL)
     {
@@ -4812,7 +5328,11 @@ wc_diff_callbacks2_dir_props_changed(svn
     }
 
   result = PyObject_CallFunction(py_callback,
+#if IS_PY3
+                                 (char *)"O&yO&O&",
+#else
                                  (char *)"O&sO&O&",
+#endif
                                  make_ob_wc_adm_access, adm_access,
                                  path,
                                  svn_swig_py_proparray_to_dict, propchanges,
@@ -4864,11 +5384,21 @@ svn_swig_py_config_enumerator2(const cha
   PyObject *result;
   svn_error_t *err = SVN_NO_ERROR;
   svn_boolean_t c_result;
+  PyObject *exc, *exc_type, *exc_traceback;
 
   svn_swig_py_acquire_py_lock();
 
+  /* As caller can't understand Python context and we can't notify if
+     Python call back function raise exception to caller, we must catch it
+     if it is occurred, and restore error indicator */
+  PyErr_Fetch(&exc_type, &exc, &exc_traceback);
+
   if ((result = PyObject_CallFunction(function,
+#if IS_PY3
+                                      (char *)"yyO&",
+#else
                                       (char *)"ssO&",
+#endif
                                       name,
                                       value,
                                       make_ob_pool, pool)) == NULL)
@@ -4884,7 +5414,7 @@ svn_swig_py_config_enumerator2(const cha
   /* Any Python exception we might have pending must be cleared,
      because the SWIG wrapper will not check for it, and return a value with
      the exception still set. */
-  PyErr_Clear();
+  PyErr_Restore(exc_type, exc, exc_traceback);
 
   if (err)
     {
@@ -4912,11 +5442,17 @@ svn_swig_py_config_section_enumerator2(c
   PyObject *result;
   svn_error_t *err = SVN_NO_ERROR;
   svn_boolean_t c_result;
+  PyObject *exc, *exc_type, *exc_traceback;
 
   svn_swig_py_acquire_py_lock();
 
+  /* As caller can't understand Python context and we can't notify if
+     Python call back function raise exception to caller, we must catch it
+     if it is occurred, and restore error indicator */
+  PyErr_Fetch(&exc_type, &exc, &exc_traceback);
+
   if ((result = PyObject_CallFunction(function,
-                                      (char *)"sO&",
+                                      (char *)SVN_SWIG_BYTES_FMT "O&",
                                       name,
                                       make_ob_pool, pool)) == NULL)
     {
@@ -4931,7 +5467,8 @@ svn_swig_py_config_section_enumerator2(c
   /* Any Python exception we might have pending must be cleared,
      because the SWIG wrapper will not check for it, and return a value with
      the exception still set. */
-  PyErr_Clear();
+  PyErr_Restore(exc_type, exc, exc_traceback);
+
 
   if (err)
     {

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h Fri Jan 14 14:01:45 2022
@@ -50,6 +50,12 @@ extern "C" {
 apr_status_t svn_swig_py_initialize(void);
 
 
+/* Returns the provided python object as a FILE *object.
+ * Return the underlying FILE or NULL if the object is not a File instance.
+ */
+FILE *svn_swig_py_as_file(PyObject *pyfile);
+
+
 
 /* Functions to manage python's global interpreter lock */
 void svn_swig_py_release_py_lock(void);
@@ -92,15 +98,33 @@ int svn_swig_py_convert_ptr(PyObject *in
 
 /* Wrapper for SWIG_MustGetPtr */
 void *svn_swig_py_must_get_ptr(void *input, swig_type_info *type, int argnum);
+
 
 /*** Functions to expose a custom SubversionException ***/
 
-/* raise a subversion exception, created from a normal subversion
-   error.  consume the error.  */
+/* Get a SubversionException class object and its instance built from
+   error_chain, but do not raise it immediately.  Consume the
+   error_chain.  */
+void svn_swig_py_build_svn_exception(
+    PyObject **exc_class, PyObject **exc_ob, svn_error_t *error_chain);
+
+/* Raise a SubversionException, created from a normal subversion
+   error.  Consume the error.  */
 void svn_swig_py_svn_exception(svn_error_t *err);
 
 
 
+/* Function to get char * representation of bytes/str object. This is
+   the replacement of typemap(in, parse="s") and typemap(in, parse="z")
+   to accept both of bytes object and str object, and it assumes to be
+   used from those typemaps only.
+   Note: type of return value should be char const *, however, as SWIG
+   produces variables for C function without 'const' modifier, to avoid
+   ton of cast in SWIG produced C code we drop it from return value
+   types as well  */
+char *svn_swig_py_string_to_cstring(PyObject *input, int maybe_null,
+                                    const char * funcsym, const char * argsym);
+
 /* helper function to convert an apr_hash_t* (char* -> svnstring_t*) to
    a Python dict */
 PyObject *svn_swig_py_prophash_to_dict(apr_hash_t *hash);
@@ -226,7 +250,10 @@ svn_swig_py_seq_to_array(PyObject *seq,
                          apr_pool_t *pool);
 
 /* An svn_swig_py_object_unwrap_t that extracts a char pointer from a Python
-   string. */
+   string.
+
+   Note the lifetime of the returned string is tied to the provided Python
+   object. */
 int
 svn_swig_py_unwrap_string(PyObject *source,
                           void *destination,
@@ -293,6 +320,13 @@ void svn_swig_py_status_func(void *baton
                              const char *path,
                              svn_wc_status_t *status);
 
+/* a client status function that executes a Python function that is passed in
+   via the baton argument */
+svn_error_t *svn_swig_py_client_status_func(void *baton,
+                                            const char *path,
+                                            const svn_client_status_t *status,
+                                            apr_pool_t *scratch_pool);
+
 /* a svn_delta_path_driver callback that executes a Python function
   that is passed in via the baton argument */
 svn_error_t *svn_swig_py_delta_path_driver_cb_func(void **dir_baton,

Propchange: subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Fri Jan 14 14:01:45 2022
@@ -1 +1,2 @@
 *.pyc
+__pycache__

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/client.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/client.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/client.py (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/client.py Fri Jan 14 14:01:45 2022
@@ -24,8 +24,8 @@
 ######################################################################
 
 from libsvn.client import *
-from svn.core import _unprefix_names
+from svn.core import _unprefix_names, _as_list
 _unprefix_names(locals(), 'svn_client_')
 _unprefix_names(locals(), 'SVN_CLIENT_')
-__all__ = filter(lambda x: x.lower().startswith('svn_'), locals().keys())
+__all__ = [x for x in _as_list(locals()) if x.lower().startswith('svn_')]
 del _unprefix_names

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/core.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/core.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/core.py (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/core.py Fri Jan 14 14:01:45 2022
@@ -27,8 +27,8 @@ from libsvn.core import *
 import libsvn.core as _libsvncore
 import atexit as _atexit
 import sys
-# __all__ is defined later, since some svn_* functions are implemented below.
 
+# __all__ is defined later, since some svn_* functions are implemented below.
 
 class SubversionException(Exception):
 
@@ -89,6 +89,20 @@ class SubversionException(Exception):
       child = cls(message, apr_err, child, file, line)
     return child
 
+# This function is useful for common Python 2/3 code. It prevents the double
+# memory hit of simply wrapping values/keys/items calls on dictionaries on
+# python 2, but ensuring an independent list is returned in Python 3.
+def _as_list(seq):
+  """Returns the given sequence or iterator as a list.
+
+  If already a list, simply returns the list, otherwise a list is constructed
+  using the given object.
+  """
+  if isinstance(seq, list):
+    return seq
+
+  return list(seq)
+
 def _cleanup_application_pool():
   """Cleanup the application pool before exiting"""
   if application_pool and application_pool.valid():
@@ -96,7 +110,7 @@ def _cleanup_application_pool():
 _atexit.register(_cleanup_application_pool)
 
 def _unprefix_names(symbol_dict, from_prefix, to_prefix = ''):
-  for name, value in symbol_dict.items():
+  for name, value in _as_list(symbol_dict.items()):
     if name.startswith(from_prefix):
       symbol_dict[to_prefix + name[len(from_prefix):]] = value
 
@@ -141,7 +155,7 @@ def svn_path_compare_paths(path1, path2)
 
   # Common prefix was skipped above, next character is compared to
   # determine order
-  return cmp(char1, char2)
+  return (char1 > char2) - (char1 < char2)
 
 def svn_mergeinfo_merge(mergeinfo, changes):
   return _libsvncore.svn_swig_mergeinfo_merge(mergeinfo, changes)
@@ -171,7 +185,7 @@ class Stream:
         if not data:
           break
         chunks.append(data)
-      return ''.join(chunks)
+      return b''.join(chunks)
 
     # read the amount specified
     return svn_stream_read(self._stream, int(amt))
@@ -194,7 +208,7 @@ def secs_from_timestr(svn_datetime, pool
   # ### convert to a time_t; this requires intimate knowledge of
   # ### the apr_time_t type
   # ### aprtime is microseconds; turn it into seconds
-  return aprtime / 1000000
+  return aprtime // 1000000
 
 
 # ============================================================================
@@ -317,12 +331,12 @@ def run_app(func, *args, **kw):
 # 'apr_pool_clear' 'apr_pool_destroy' 'apr_pool_t'
 # 'apr_time_ansi_put'
 # 'run_app'
-# 'svn_relpath__internal_style' 'svn_uri__is_ancestor'
+# 'svn_uri__is_ancestor'
 # 'svn_tristate__from_word' 'svn_tristate__to_word'
-__all__ = filter(lambda s: (s.startswith('svn_')
-                            or s.startswith('SVN_')
-                            or s.startswith('SVNSYNC_')
-                            or s in ('Pool', 'SubversionException'))
-                           and '__' not in s,
-                 locals())
+__all__ = [s for s in _as_list(locals())
+           if (s.startswith('svn_')
+               or s.startswith('SVN_')
+               or s.startswith('SVNSYNC_')
+               or s in ('Pool', 'SubversionException'))
+           and '__' not in s]
 

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/delta.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/delta.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/delta.py (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/delta.py Fri Jan 14 14:01:45 2022
@@ -24,10 +24,10 @@
 ######################################################################
 
 from libsvn.delta import *
-from svn.core import _unprefix_names
+from svn.core import _unprefix_names, _as_list
 _unprefix_names(locals(), 'svn_delta_')
 _unprefix_names(locals(), 'svn_txdelta_', 'tx_')
-__all__ = filter(lambda x: x.lower().startswith('svn_'), locals().keys())
+__all__ = [x for x in _as_list(locals()) if x.lower().startswith('svn_')]
 del _unprefix_names
 
 class Editor:

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/diff.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/diff.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/diff.py (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/diff.py Fri Jan 14 14:01:45 2022
@@ -24,7 +24,7 @@
 ######################################################################
 
 from libsvn.diff import *
-from svn.core import _unprefix_names
+from svn.core import _unprefix_names, _as_list
 _unprefix_names(locals(), 'svn_diff_')
-__all__ = filter(lambda x: x.lower().startswith('svn_'), locals().keys())
+__all__ = [x for x in _as_list(locals()) if x.lower().startswith('svn_')]
 del _unprefix_names

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/fs.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/fs.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/fs.py (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/fs.py Fri Jan 14 14:01:45 2022
@@ -24,10 +24,38 @@
 ######################################################################
 
 from libsvn.fs import *
-from svn.core import _unprefix_names, Pool
+
+######################################################################
+# Any adjustment of SWIG generated wrapper functions on svn.fs module
+# should be placed before adding alternative names for them.
+
+# fs.commit_txn() should return a 2-tupple of conflict_p and new_rev.
+# However if conflict_p is None, SWIG generated wrapper function
+# libsvn.fs.svn_fs_commit_txn returns an integer value of new_rev.
+# This is a bug of SWIG generated wrapper function, so we fix it here.
+#
+# (There was discussion about this behavior because C API
+# svn_fs_commit_txn() always set NULL to conflict_p if it returns
+# SVN_NO_ERROR and so it seems to be reasonable that fs.commit_txn()
+# returns only rev_new value. However for compatibility, we decided
+# that fs.commit_txn always returns 2-tuple if it does not raises
+# an exception.)
+
+_svn_fs_commit_txn = svn_fs_commit_txn
+
+def svn_fs_commit_txn(*args):
+    r"""svn_fs_commit_txn(svn_fs_txn_t txn, apr_pool_t pool) -> svn_error_t"""
+    ret = _svn_fs_commit_txn(*args)
+    if not isinstance(ret, tuple):
+      ret = (None, ret)
+    return ret
+
+######################################################################
+
+from svn.core import _unprefix_names, Pool, _as_list
 _unprefix_names(locals(), 'svn_fs_')
 _unprefix_names(locals(), 'SVN_FS_')
-__all__ = filter(lambda x: x.lower().startswith('svn_'), locals().keys())
+__all__ = [x for x in _as_list(locals()) if x.lower().startswith('svn_')]
 del _unprefix_names
 
 
@@ -48,10 +76,30 @@ import svn.diff as _svndiff
 def entries(root, path, pool=None):
   "Call dir_entries returning a dictionary mappings names to IDs."
   e = dir_entries(root, path, pool)
-  for name, entry in e.items():
+  for name, entry in _as_list(e.items()):
     e[name] = dirent_t_id_get(entry)
   return e
 
+class _PopenStdoutWrapper(object):
+  "Private wrapper object of _subprocess.Popen.stdout to clean up sub process"
+  def __init__(self, pobject):
+    self._pobject = pobject
+  def __getattr__(self, name):
+    return getattr(self._pobject.stdout, name)
+  def close(self):
+    self._pobject.stdout.close()
+    if self._pobject.poll() is None:
+        self._pobject.terminate()
+  def __del__(self):
+    if not self.closed:
+      self.close()
+    if self._pobject.poll() is None:
+        self._pobject.terminate()
+        if _sys.hexversion >= 0x030300F0:
+          try:
+            self._pobject.wait(10)
+          except _subprocess.TimeoutExpired:
+            self._pobject.kill()
 
 class FileDiff:
   def __init__(self, root1, path1, root2, path2, pool=None, diffoptions=[]):
@@ -124,7 +172,7 @@ class FileDiff:
       # open the pipe, and return the file object for reading from the child.
       p = _subprocess.Popen(cmd, stdout=_subprocess.PIPE, bufsize=-1,
                             close_fds=_sys.platform != "win32")
-      return p.stdout
+      return _PopenStdoutWrapper(p)
 
     else:
       if self.difftemp is None:
@@ -132,16 +180,16 @@ class FileDiff:
 
         with builtins.open(self.difftemp, "wb") as fp:
           diffopt = _svndiff.file_options_create()
-          diffobj = _svndiff.file_diff_2(self.tempfile1,
-                                         self.tempfile2,
+          diffobj = _svndiff.file_diff_2(self.tempfile1.encode('UTF-8'),
+                                         self.tempfile2.encode('UTF-8'),
                                          diffopt)
 
           _svndiff.file_output_unified4(fp,
                                         diffobj,
-                                        self.tempfile1,
-                                        self.tempfile2,
+                                        self.tempfile1.encode('UTF-8'),
+                                        self.tempfile2.encode('UTF-8'),
                                         None, None,
-                                        "utf8",
+                                        b"utf8",
                                         None,
                                         diffopt.show_c_function,
                                         diffopt.context_size,

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/ra.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/ra.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/ra.py (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/ra.py Fri Jan 14 14:01:45 2022
@@ -24,10 +24,10 @@
 ######################################################################
 
 from libsvn.ra import *
-from svn.core import _unprefix_names
+from svn.core import _unprefix_names, _as_list
 _unprefix_names(locals(), 'svn_ra_')
 _unprefix_names(locals(), 'SVN_RA_')
-__all__ = filter(lambda x: x.lower().startswith('svn_'), locals().keys())
+__all__ = [x for x in _as_list(locals()) if x.lower().startswith('svn_')]
 del _unprefix_names
 
 class Callbacks:
@@ -58,7 +58,7 @@ class Callbacks:
                                       svn.core.SVN_AUTH_PARAM_DEFAULT_PASSWORD,
                                       password)
     def open_tmp_file(self, pool):
-      path = '/'.join([self.wc, svn.wc.get_adm_dir(pool), 'tmp'])
+      path = b'/'.join([self.wc, svn.wc.get_adm_dir(pool), b'tmp'])
       (fd, fn) = tempfile.mkstemp(dir=path)
       os.close(fd)
       return fn

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/repos.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/repos.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/repos.py (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/repos.py Fri Jan 14 14:01:45 2022
@@ -24,10 +24,10 @@
 ######################################################################
 
 from libsvn.repos import *
-from svn.core import _unprefix_names, Pool
+from svn.core import _unprefix_names, Pool, _as_list
 _unprefix_names(locals(), 'svn_repos_')
 _unprefix_names(locals(), 'SVN_REPOS_')
-__all__ = filter(lambda x: x.lower().startswith('svn_'), locals().keys())
+__all__ = [x for x in _as_list(locals()) if x.lower().startswith('svn_')]
 del _unprefix_names
 
 
@@ -126,9 +126,9 @@ class ChangeCollector(_svndelta.Editor):
         self.notify_cb(change)
 
   def _make_base_path(self, parent_path, path):
-    idx = path.rfind('/')
+    idx = path.rfind(b'/')
     if parent_path:
-      parent_path = parent_path + '/'
+      parent_path = parent_path + b'/'
     if idx == -1:
       return parent_path + path
     return parent_path + path[idx+1:]
@@ -142,7 +142,7 @@ class ChangeCollector(_svndelta.Editor):
     return root
 
   def open_root(self, base_revision, dir_pool=None):
-    return ('', '', self.base_rev)  # dir_baton
+    return (b'', b'', self.base_rev)  # dir_baton
 
   def delete_entry(self, path, revision, parent_baton, pool=None):
     base_path = self._make_base_path(parent_baton[1], path)
@@ -281,9 +281,9 @@ class RevisionChangeCollector(ChangeColl
     ChangeCollector.__init__(self, fs_ptr, root, pool, notify_cb)
 
   def _make_base_path(self, parent_path, path):
-    idx = path.rfind('/')
+    idx = path.rfind(b'/')
     if idx == -1:
-      return parent_path + '/' + path
+      return parent_path + b'/' + path
     return parent_path + path[idx:]
 
 

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/wc.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/wc.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/wc.py (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/python/svn/wc.py Fri Jan 14 14:01:45 2022
@@ -24,10 +24,10 @@
 ######################################################################
 
 from libsvn.wc import *
-from svn.core import _unprefix_names
+from svn.core import _unprefix_names, _as_list
 _unprefix_names(locals(), 'svn_wc_')
 _unprefix_names(locals(), 'SVN_WC_')
-__all__ = filter(lambda x: x.lower().startswith('svn_'), locals().keys())
+__all__ = [x for x in _as_list(locals()) if x.lower().startswith('svn_')]
 del _unprefix_names
 
 

Propchange: subversion/branches/multi-wc-format/subversion/bindings/swig/python/tests/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Fri Jan 14 14:01:45 2022
@@ -1,2 +1,3 @@
 *.pyc
+__pycache__
 .gdb_history