Index: subversion/bindings/swig/core.i
===================================================================
--- subversion/bindings/swig/core.i	(revision 1849073)
+++ subversion/bindings/swig/core.i	(working copy)
@@ -130,6 +130,8 @@
 /* svn_stream_checksummed would require special attention to wrap, because
  * of the read_digest and write_digest parameters. */
 %ignore svn_stream_checksummed;
+// svn_stream_read_full
+// svn_stream_read2
 // svn_stream_read
 // svn_stream_write
 // svn_stream_close
@@ -420,7 +422,7 @@
 
 #ifdef SWIGPYTHON
 %typemap(argout) (char *buffer, apr_size_t *len) {
-  %append_output(PyStr_FromStringAndSize($1, *$2));
+  %append_output(PyBytes_FromStringAndSize($1, *$2));
   free($1);
 }
 #endif
@@ -442,16 +444,18 @@
 */
 #ifdef SWIGPYTHON
 %typemap(in) (const char *data, apr_size_t *len) ($*2_type temp) {
-    if (!PyStr_Check($input)) {
+    char *tmpdata;
+    Py_ssize_t length;
+    if (!PyBytes_Check($input)) {
         PyErr_SetString(PyExc_TypeError,
-                        "expecting a string for the buffer");
+                        "expecting a bytes object for the buffer");
         SWIG_fail;
     }
-    /* Explicitly cast away const from PyStr_AsUTF8AndSize (in Python 3). The
-       swig generated argument ($1) here will be "char *", but since its only
-       use is to pass directly into the "const char *" argument of the wrapped
-       function, this is safe to do. */
-    $1 = (char *)PyStr_AsUTF8AndSize($input, &temp);
+    if (PyBytes_AsStringAndSize($input, &tmpdata, &length) == -1) {
+        SWIG_fail;
+    }
+    temp = ($*2_type)length;
+    $1 = tmpdata;
     $2 = ($2_ltype)&temp;
 }
 #endif
@@ -489,6 +493,20 @@
 #endif
 
 /* -----------------------------------------------------------------------
+   fix up the svn_stream_readline() eol argument
+*/
+#ifdef SWIGPYTHON
+%typemap(in) (const char *eol) {
+    if (!PyBytes_Check($input)) {
+        PyErr_SetString(PyExc_TypeError,
+                        "expecting a bytes object for the eol");
+        SWIG_fail;
+    }
+    $1 = PyBytes_AsString($input);
+}
+#endif
+
+/* -----------------------------------------------------------------------
    auth parameter set/get
 */
 
Index: subversion/bindings/swig/include/svn_string.swg
===================================================================
--- subversion/bindings/swig/include/svn_string.swg	(revision 1849073)
+++ subversion/bindings/swig/include/svn_string.swg	(working copy)
@@ -28,11 +28,14 @@
 
 /* -----------------------------------------------------------------------
    generic OUT param typemap for svn_string(buf)_t. we can share these
-   because we only refer to the ->data and ->len values.
+   except in the case of Python, because we only refer to the ->data and ->len
+   values. In case of Python, svn_stringbuf_t represents raw bytes and
+   svn_string_t represents string in most cases, so it is convenient to
+   distinguish them.
 */
 #ifdef SWIGPYTHON
 
-%typemap(argout) RET_STRING {
+%typemap(argout) svn_string_t ** {
     PyObject *s;
     if (*$1 == NULL) {
         Py_INCREF(Py_None);
@@ -45,6 +48,19 @@
     }
     %append_output(s);
 }
+
+%typemap(argout) svn_stringbuf_t ** {
+    PyObject *s;
+    if (*$1 == NULL) {
+        Py_INCREF(Py_None);
+        s = Py_None;
+    } else {
+        s = PyBytes_FromStringAndSize((*$1)->data, (*$1)->len);
+        if (s == NULL)
+            SWIG_fail;
+    }
+    %append_output(s);
+}
 #endif
 #ifdef SWIGPERL
 %typemap(argout) RET_STRING {
@@ -65,10 +81,12 @@
 }
 #endif
 
+#ifndef SWIGPYTHON
 %apply RET_STRING {
   svn_string_t **,
   svn_stringbuf_t **
 };
+#endif
 
 /* -----------------------------------------------------------------------
    TYPE: svn_stringbuf_t
@@ -76,6 +94,22 @@
 
 #ifdef SWIGPYTHON
 %typemap(in) svn_stringbuf_t * {
+    if (!PyBytes_Check($input)) {
+        PyErr_SetString(PyExc_TypeError, "not a bytes object");
+        SWIG_fail;
+    }
+    {
+      Py_ssize_t strBufLen;
+      char *strBufChar;
+      if (-1 == PyBytes_AsStringAndSize($input, &strBufChar, &strBufLen)) {
+        SWIG_fail;
+      }
+      $1 = svn_stringbuf_ncreate(strBufChar, strBufLen,
+                                 /* ### gah... what pool to use? */
+                                 _global_pool);
+    }
+}
+%typemap(in) svn_stringbuf_t *output {
     if (!PyStr_Check($input)) {
         PyErr_SetString(PyExc_TypeError, "not a string");
         SWIG_fail;
@@ -264,6 +298,18 @@
     }
     %append_output(s);
 }
+%typemap(argout) const char **eol {
+    PyObject *s;
+    if (*$1 == NULL) {
+        Py_INCREF(Py_None);
+        s = Py_None;
+    } else {
+        s = PyBytes_FromString(*$1);
+        if (s == NULL)
+            SWIG_fail;
+    }
+    %append_output(s);
+}
 #endif
 
 #ifdef SWIGPERL
Index: subversion/bindings/swig/python/svn/core.py
===================================================================
--- subversion/bindings/swig/python/svn/core.py	(revision 1849073)
+++ subversion/bindings/swig/python/svn/core.py	(working copy)
@@ -185,7 +185,7 @@
         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))
Index: subversion/bindings/swig/python/tests/core.py
===================================================================
--- subversion/bindings/swig/python/tests/core.py	(revision 1849073)
+++ subversion/bindings/swig/python/tests/core.py	(working copy)
@@ -19,6 +19,8 @@
 #
 #
 import unittest
+import os
+import tempfile
 
 import svn.core, svn.client
 import utils
@@ -171,6 +173,120 @@
     self.assertEqual(
       svn.core.svn_config_enumerate_sections2(cfg, enumerator), 1)
 
+  def test_stream_from_bufstring(self):
+    stream = svn.core.svn_stream_from_stringbuf(b'')
+    svn.core.svn_stream_close(stream)
+    with self.assertRaises(TypeError):
+        stream = svn.core.svn_stream_from_stringbuf(b''.decode())
+        svn.core.svn_stream_close(stream)
+
+  def test_stream_read_full(self):
+    in_str = (b'Python\x00'
+              b'\xa4\xd1\xa4\xa4\xa4\xbd\xa4\xf3\r\n'
+              b'Subversion\x00'
+              b'\xa4\xb5\xa4\xd6\xa4\xd0\xa1\xbc\xa4\xb8\xa4\xe7\xa4\xf3\n'
+              b'swig\x00'
+              b'\xa4\xb9\xa4\xa6\xa4\xa3\xa4\xb0\r'
+              b'end')
+    stream = svn.core.svn_stream_from_stringbuf(in_str)
+    self.assertEqual(svn.core.svn_stream_read_full(stream, 4096), in_str)
+    svn.core.svn_stream_seek(stream, None)
+    self.assertEqual(svn.core.svn_stream_read_full(stream, 10), in_str[0:10])
+    svn.core.svn_stream_seek(stream, None)
+    svn.core.svn_stream_skip(stream, 20)
+    self.assertEqual(svn.core.svn_stream_read_full(stream, 4096), in_str[20:])
+    self.assertEqual(svn.core.svn_stream_read_full(stream, 4096), b'')
+    svn.core.svn_stream_close(stream)
+
+  def test_stream_read2(self):
+    # as we can't create non block stream by using swig-py API directly,
+    # we only test svn_stream_read2() behaves just same as
+    # svn_stream_read_full()
+    in_str = (b'Python\x00'
+              b'\xa4\xd1\xa4\xa4\xa4\xbd\xa4\xf3\r\n'
+              b'Subversion\x00'
+              b'\xa4\xb5\xa4\xd6\xa4\xd0\xa1\xbc\xa4\xb8\xa4\xe7\xa4\xf3\n'
+              b'swig\x00'
+              b'\xa4\xb9\xa4\xa6\xa4\xa3\xa4\xb0\r'
+              b'end')
+    stream = svn.core.svn_stream_from_stringbuf(in_str)
+    self.assertEqual(svn.core.svn_stream_read2(stream, 4096), in_str)
+    svn.core.svn_stream_seek(stream, None)
+    self.assertEqual(svn.core.svn_stream_read2(stream, 10), in_str[0:10])
+    svn.core.svn_stream_seek(stream, None)
+    svn.core.svn_stream_skip(stream, 20)
+    self.assertEqual(svn.core.svn_stream_read2(stream, 4096), in_str[20:])
+    self.assertEqual(svn.core.svn_stream_read2(stream, 4096), b'')
+    svn.core.svn_stream_close(stream)
+
+  def test_stream_write_exception(self):
+    ostr_unicode = b'Python'.decode()
+    stream = svn.core.svn_stream_empty()
+    with self.assertRaises(TypeError):
+        svn.core.svn_stream_write(stream, ostr_unicode)
+    svn.core.svn_stream_close(stream)
+
+  def test_stream_write(self):
+    o1_str = b'Python\x00\xa4\xd1\xa4\xa4\xa4\xbd\xa4\xf3\r\n'
+    o2_str = (b'subVersioN\x00'
+              b'\xa4\xb5\xa4\xd6\xa4\xd0\xa1\xbc\xa4\xb8\xa4\xe7\xa4\xf3\n')
+    o3_str =  b'swig\x00\xa4\xb9\xa4\xa6\xa4\xa3\xa4\xb0\rend'
+    out_str = o1_str + o2_str + o3_str
+    rewrite_str = b'Subversion'
+    fd, fname = tempfile.mkstemp()
+    os.close(fd)
+    try:
+      stream = svn.core.svn_stream_from_aprfile2(fname, False)
+      self.assertEqual(svn.core.svn_stream_write(stream, out_str),
+                       len(out_str))
+      svn.core.svn_stream_seek(stream, None)
+      self.assertEqual(svn.core.svn_stream_read_full(stream, 4096), out_str)
+      svn.core.svn_stream_seek(stream, None)
+      svn.core.svn_stream_skip(stream, len(o1_str))
+      self.assertEqual(svn.core.svn_stream_write(stream, rewrite_str),
+                       len(rewrite_str))
+      svn.core.svn_stream_seek(stream, None)
+      self.assertEqual(
+                svn.core.svn_stream_read_full(stream, 4096),
+                o1_str + rewrite_str + o2_str[len(rewrite_str):] + o3_str)
+      svn.core.svn_stream_close(stream)
+    finally:
+      try:
+        os.remove(fname)
+      except OSError:
+        pass
+
+  def test_stream_readline(self):
+    o1_str = b'Python\t\xa4\xd1\xa4\xa4\xa4\xbd\xa4\xf3\r\n'
+    o2_str = (b'Subversion\t'
+              b'\xa4\xb5\xa4\xd6\xa4\xd0\xa1\xbc\xa4\xb8\xa4\xe7\xa4\xf3\n')
+    o3_str =  b'swig\t\xa4\xb9\xa4\xa6\xa4\xa3\xa4\xb0\rend'
+    in_str = o1_str + o2_str + o3_str
+    stream = svn.core.svn_stream_from_stringbuf(in_str)
+    self.assertEqual(svn.core.svn_stream_readline(stream, b'\n'),
+                     [o1_str[:-1], 0])
+    self.assertEqual(svn.core.svn_stream_readline(stream, b'\n'),
+                     [o2_str[:-1], 0])
+    self.assertEqual(svn.core.svn_stream_readline(stream, b'\n'),
+                     [o3_str, 1])
+    self.assertEqual(svn.core.svn_stream_readline(stream, b'\n'),
+                     [b'', 1])
+    svn.core.svn_stream_seek(stream, None)
+    self.assertEqual(svn.core.svn_stream_readline(stream, b'\r\n'),
+                     [o1_str[:-2], 0])
+    self.assertEqual(svn.core.svn_stream_readline(stream, b'\r\n'),
+                     [o2_str + o3_str, 1])
+    svn.core.svn_stream_write(stream, b'\r\n')
+    svn.core.svn_stream_seek(stream, None)
+    self.assertEqual(svn.core.svn_stream_readline(stream, b'\r\n'),
+                     [o1_str[:-2], 0])
+    self.assertEqual(svn.core.svn_stream_readline(stream, b'\r\n'),
+                     [o2_str + o3_str, 0])
+    self.assertEqual(svn.core.svn_stream_readline(stream, b'\r\n'),
+                     [b'', 1])
+    svn.core.svn_stream_close(stream)
+
+
 def suite():
     return unittest.defaultTestLoader.loadTestsFromTestCase(
       SubversionCoreTestCase)
Index: subversion/bindings/swig/python/tests/delta.py
===================================================================
--- subversion/bindings/swig/python/tests/delta.py	(revision 1849073)
+++ subversion/bindings/swig/python/tests/delta.py	(working copy)
@@ -47,9 +47,9 @@
   def testTxWindowHandler_stream_IF(self):
     """Test tx_invoke_window_handler, with svn.core.svn_stream_t object"""
     pool = svn.core.Pool()
-    in_str = "hello world"
+    in_str = b"hello world"
     src_stream = svn.core.svn_stream_from_stringbuf(in_str)
-    content_str = "bye world"
+    content_str = b"bye world"
     content_stream = svn.core.svn_stream_from_stringbuf(content_str)
     fd, fname = tempfile.mkstemp()
     os.close(fd)
@@ -72,10 +72,10 @@
   def testTxWindowHandler_Stream_IF(self):
     """Test tx_invoke_window_handler, with svn.core.Stream object"""
     pool = svn.core.Pool()
-    in_str = "hello world"
+    in_str = b"hello world"
     src_stream = svn.core.Stream(
                     svn.core.svn_stream_from_stringbuf(in_str))
-    content_str = "bye world"
+    content_str = b"bye world"
     content_stream = svn.core.Stream(
                     svn.core.svn_stream_from_stringbuf(content_str))
     fd, fname = tempfile.mkstemp()
Index: subversion/bindings/swig/python/tests/repository.py
===================================================================
--- subversion/bindings/swig/python/tests/repository.py	(revision 1849073)
+++ subversion/bindings/swig/python/tests/repository.py	(working copy)
@@ -232,7 +232,7 @@
         DumpStreamParser.set_fulltext(self, node_baton)
         return 42
     stream = open(os.path.join(os.path.dirname(sys.argv[0]),
-                               "trac/versioncontrol/tests/svnrepos.dump"))
+                               "trac/versioncontrol/tests/svnrepos.dump"), "rb")
     try:
       dsp = DumpStreamParserSubclass()
       ptr, baton = repos.make_parse_fns3(dsp)
Index: subversion/bindings/swig/python/tests/trac/versioncontrol/tests/svn_fs.py
===================================================================
--- subversion/bindings/swig/python/tests/trac/versioncontrol/tests/svn_fs.py	(revision 1849073)
+++ subversion/bindings/swig/python/tests/trac/versioncontrol/tests/svn_fs.py	(working copy)
@@ -173,7 +173,7 @@
         node = self.repos.get_node('/trunk/README.txt')
         self.assertEqual(8, node.content_length)
         self.assertEqual('text/plain', node.content_type)
-        self.assertEqual('A test.\n', node.get_content().read())
+        self.assertEqual(b'A test.\n', node.get_content().read())
 
     def test_get_dir_properties(self):
         f = self.repos.get_node('/trunk')
Index: subversion/bindings/swig/svn_ra.i
===================================================================
--- subversion/bindings/swig/svn_ra.i	(revision 1849073)
+++ subversion/bindings/swig/svn_ra.i	(working copy)
@@ -76,6 +76,25 @@
 #endif
 
 #ifdef SWIGPYTHON
+/* -----------------------------------------------------------------------
+   handle svn_ra_print_ra_libraries().
+*/
+%typemap(argout) svn_stringbuf_t **descriptions {
+    PyObject *s;
+    if (*$1 == NULL) {
+        Py_INCREF(Py_None);
+        s = Py_None;
+    } else {
+        s = PyStr_FromStringAndSize((*$1)->data, (*$1)->len);
+        if (s == NULL)
+            SWIG_fail;
+    }
+    %append_output(s);
+}
+#endif
+
+
+#ifdef SWIGPYTHON
 %callback_typemap(const svn_ra_reporter2_t *reporter, void *report_baton,
                   svn_swig_py_get_ra_reporter2(),
                   ,

