You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by fu...@apache.org on 2019/02/14 16:48:40 UTC
svn commit: r1853592 - in
/subversion/branches/swig-py3/subversion/bindings/swig: ./ include/
python/libsvn_swig_py/ python/tests/
Author: futatuki
Date: Thu Feb 14 16:48:40 2019
New Revision: 1853592
URL: http://svn.apache.org/viewvc?rev=1853592&view=rev
Log:
On branch swig-py3: accept both of bytes/str for input char * arguments
* Replace typemap(in) for char * using 'parse' modifier with one using
function allows both of bytes/str in py2/py3 in libsvn_swig_py library
* Fix functions to convert Python objects into char pointer to accept
both of bytes/str object in py2/py3
* Fix to accept None as representation of NULL value on conversion
prop dict into apr_hash_t * and apr_array_header_t * of svn_props_t *
[In subversion/bindings/swig]
* core.i (%typemap(in) (const char *data, apr_size_t *len): Allow str
as well as bytes for data argment of svn_stream_write()
* include/svn_global.swg
(remove)(%typemap(in) char *, char const *, char * const,
char const * const): Move this typemap into include/svn_strings as
typemap (in) IN_STRING
* include/svn_string.swg (new)(%typemap(in) IN_STRING): replacement of
%typemap(in) char *, char const *, char * const, char const * const.
actual processing code is moved new svn_swig_py_string_to_cstring()
function in python/libsvn_swig_py/swigutil_py.c
* include/svn_types.swg (%typemap(in) const char *MAY_BE_NULL):
Move processing code into new svn_swig_py_string_to_cstring() function
in python/libsvn_swig_py/swigutil_py.c
* python/libsvn_swig_py/swigutil_py.c
(svn_swig_py_string_to_cstring): New function to convert Python
bytes or str into const char *, with better TypeError exception message
(svn_swig_py_string_type_exception): New function to construct
TypeError exception for new make_string_from_ob_maybe_null() function
(make_string_from_ob, make_svn_string_from_ob):
- Allow str as well as bytes for ob
- Don't raise TypeError exception because all callers don't expect it
(make_string_from_ob_maybe_null, make_svn_string_from_ob_maybe_null):
New function same as make_string_from_ob() and make_svn_string_fromob()
but allows None input represents NULL value and raise TypeError
if input value don't have appropriate type
(svn_swig_py_stringhash_from_dict, svn_swig_py_mergeinfo_from_dict,
svn_swig_py_proparray_from_dict, svn_swig_py_prophash_from_dict,
svn_swig_py_path_rev_hash_from_dict,
svn_swig_py_struct_ptr_hash_from_dict): separate check of key conversion
result and value conversion result
(svn_swig_py_proparray_from_dict, svn_swig_py_prophash_from_dict):
allow NULL for prop values
(svn_swig_py_unwrap_string):
- Allow str as well as bytes for source argument
(svn_swig_py_make_file):
- Allow str as well as bytes for py_file argument as file path
(svn_swig_py_auth_gnome_keyring_unlock_prompt_func):
- Use new function make_string_from_ob_maybe_null() instead of
make_string_from_ob() to check TypeError
- Report Python exception caused by Python callback function as
callback exception error
* python/libsvn_swig_py/swigutil_py.h
Expose new public function make_string_from_ob_maybe_null(), which is
used by typemap(in) char IN_STRING, typemap(in) const char *MAY_BY_NULL
* python/tests/client.py
(SubversionClientTestCase.log_entry_receiver_whole): New helper
callback function for new SubversionClientTestCase.test_log5_revprops
(SubversionClientTestCase.test_log5_revprops): new test for
typemap(in) apr_array_t *STRINGLIST and its helper function
svn_swig_py_unwrap_string()
* python/tests/core.py
(SubversionCoreTestCase.test_stream_write_exception):
- As unicode input is now valid, use int value as invalid input
- Add case to be expected to cause UnicodeEncodeError
(SubversionCoreTestCase.test_stream_write_str):(Only for Python 3)
New test case for svn_stream_write() to pass str object as data argument
(SubversionCoreTestCase.test_stream_write_bytes):
Renamed from SubversionCoreTestCase.test_stream_write
* python/tests/run_all.py: Register new test module typemap
* python/tests/typemap.py: New unittest module for typemaps
(SubversionTypemapTestCase): New unittest subclass for unit test about
typemaps
(SubversionTypemapTestCase.test_char_ptr_in): New test case
(SubversionTypemapTestCase.test_char_ptr_in_unicode_exception):
New test case
(SubversionTypemapTestCase.test_char_ptr_may_be_null): New test case
(SubversionTypemapTestCase.test_char_ptr_may_be_null_unicode_exception):
New test case
(SubversionTypemapTestCase.test_make_string_from_ob): New test case
(SubversionTypemapTestCase.test_prophash_from_dict_null_value):
New test case
(SubversionTypemapTestCase.test_make-string_ob_unicode_exception):
New test case
(SubversionTypemapTestCase.test_make_svn_string_ob_unicode_exception):
New test case
(suite): New function to drive SubversionTypemapTestCase
Added:
subversion/branches/swig-py3/subversion/bindings/swig/python/tests/typemap.py (with props)
Modified:
subversion/branches/swig-py3/subversion/bindings/swig/core.i
subversion/branches/swig-py3/subversion/bindings/swig/include/svn_global.swg
subversion/branches/swig-py3/subversion/bindings/swig/include/svn_string.swg
subversion/branches/swig-py3/subversion/bindings/swig/include/svn_types.swg
subversion/branches/swig-py3/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c
subversion/branches/swig-py3/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h
subversion/branches/swig-py3/subversion/bindings/swig/python/tests/client.py
subversion/branches/swig-py3/subversion/bindings/swig/python/tests/core.py
subversion/branches/swig-py3/subversion/bindings/swig/python/tests/run_all.py
Modified: subversion/branches/swig-py3/subversion/bindings/swig/core.i
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/bindings/swig/core.i?rev=1853592&r1=1853591&r2=1853592&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/bindings/swig/core.i (original)
+++ subversion/branches/swig-py3/subversion/bindings/swig/core.i Thu Feb 14 16:48:40 2019
@@ -442,18 +442,24 @@
*/
#ifdef SWIGPYTHON
%typemap(in) (const char *data, apr_size_t *len) ($*2_type temp) {
- char *tmpdata;
Py_ssize_t length;
- if (!PyBytes_Check($input)) {
- PyErr_SetString(PyExc_TypeError,
- "expecting a bytes object for the buffer");
- SWIG_fail;
+ if (PyBytes_Check($input)) {
+ if (PyBytes_AsStringAndSize($input, (char **)&$1, &length) == -1) {
+ SWIG_fail;
+ }
}
- if (PyBytes_AsStringAndSize($input, &tmpdata, &length) == -1) {
+ else if (PyUnicode_Check($input)) {
+ $1 = (char *)PyStr_AsUTF8AndSize($input, &length);
+ if (PyErr_Occurred()) {
+ SWIG_fail;
+ }
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "expecting a bytes or str object for the buffer");
SWIG_fail;
}
temp = ($*2_type)length;
- $1 = tmpdata;
$2 = ($2_ltype)&temp;
}
#endif
Modified: subversion/branches/swig-py3/subversion/bindings/swig/include/svn_global.swg
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/bindings/swig/include/svn_global.swg?rev=1853592&r1=1853591&r2=1853592&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/bindings/swig/include/svn_global.swg (original)
+++ subversion/branches/swig-py3/subversion/bindings/swig/include/svn_global.swg Thu Feb 14 16:48:40 2019
@@ -142,13 +142,6 @@ static PyObject * _global_py_pool = NULL
/* Python format specifiers. Use Python instead of SWIG to parse
these basic types, because Python reports better error messages
(with correct argument numbers). */
-#if defined(PY3)
-%typemap (in, parse="y")
- char *, char const *, char * const, char const * const "";
-#else
-%typemap (in, parse="s")
- char *, char const *, char * const, char const * const "";
-#endif
%typemap (in, parse="c") char "";
%typemap (in, fragment=SWIG_As_frag(long)) long
Modified: subversion/branches/swig-py3/subversion/bindings/swig/include/svn_string.swg
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/bindings/swig/include/svn_string.swg?rev=1853592&r1=1853591&r2=1853592&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/bindings/swig/include/svn_string.swg (original)
+++ subversion/branches/swig-py3/subversion/bindings/swig/include/svn_string.swg Thu Feb 14 16:48:40 2019
@@ -251,6 +251,26 @@ typedef struct svn_string_t svn_string_t
}
#endif
+ /* -----------------------------------------------------------------------
+ Type: char * (input)
+*/
+#ifdef SWIGPYTHON
+%typemap (in) IN_STRING
+{
+ $1 = svn_swig_py_string_to_cstring($input, FALSE, "$symname", "$1_name");
+ if (PyErr_Occurred()) SWIG_fail;
+}
+
+%typemap (freearg) IN_STRING "";
+
+%apply IN_STRING {
+ const char *,
+ char *,
+ char const *,
+ char * const,
+ char const * const
+};
+#endif
/* -----------------------------------------------------------------------
define a way to return a 'const char *'
*/
Modified: subversion/branches/swig-py3/subversion/bindings/swig/include/svn_types.swg
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/bindings/swig/include/svn_types.swg?rev=1853592&r1=1853591&r2=1853592&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/bindings/swig/include/svn_types.swg (original)
+++ subversion/branches/swig-py3/subversion/bindings/swig/include/svn_types.swg Thu Feb 14 16:48:40 2019
@@ -348,12 +348,8 @@ svn_ ## TYPE ## _swig_rb_closed(VALUE se
#ifdef SWIGPYTHON
%typemap(in) const char *MAY_BE_NULL
{
- if ($input == Py_None) {
- $1 = NULL;
- } else {
- $1 = PyBytes_AsString($input);
- if ($1 == NULL) SWIG_fail;
- }
+ $1 = svn_swig_py_string_to_cstring($input, TRUE, "$symname", "$1_name");
+ if (PyErr_Occurred()) SWIG_fail;
}
#endif
Modified: subversion/branches/swig-py3/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c?rev=1853592&r1=1853591&r2=1853592&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c (original)
+++ subversion/branches/swig-py3/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c Thu Feb 14 16:48:40 2019
@@ -506,6 +506,36 @@ void svn_swig_py_svn_exception(svn_error
/*** 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)
{
@@ -540,32 +570,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 (! PyBytes_Check(ob))
{
- PyErr_SetString(PyExc_TypeError, "not a bytes object");
return NULL;
}
- return apr_pstrdup(pool, PyBytes_AsString(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 (! PyBytes_Check(ob))
{
- PyErr_SetString(PyExc_TypeError, "not a bytes object");
return NULL;
}
- return svn_string_create(PyBytes_AsString(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,
@@ -1055,6 +1142,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)
{
@@ -1079,11 +1169,18 @@ 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))
+ if (!propname)
+ {
+ if (!PyErr_Occurred())
+ {
+ PyErr_SetString(PyExc_TypeError, TYPE_ERROR_DICT_STRING_KEY);
+ }
+ Py_DECREF(keys);
+ return NULL;
+ }
+ const char *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;
}
@@ -1116,6 +1213,15 @@ 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);
+ if (!pathname)
+ {
+ if (!PyErr_Occurred())
+ {
+ PyErr_SetString(PyExc_TypeError, TYPE_ERROR_DICT_STRING_KEY);
+ }
+ Py_DECREF(keys);
+ return NULL;
+ }
const svn_rangelist_t *ranges = svn_swig_py_seq_to_array(value,
sizeof(const svn_merge_range_t *),
svn_swig_py_unwrap_struct_ptr,
@@ -1123,10 +1229,10 @@ apr_hash_t *svn_swig_py_mergeinfo_from_d
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;
}
@@ -1161,11 +1267,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;
}
@@ -1199,11 +1312,18 @@ 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))
+ if (!propname)
+ {
+ if (!PyErr_Occurred())
+ {
+ PyErr_SetString(PyExc_TypeError, TYPE_ERROR_DICT_STRING_KEY);
+ }
+ Py_DECREF(keys);
+ return NULL;
+ }
+ svn_string_t *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;
}
@@ -1241,8 +1361,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;
}
@@ -1296,8 +1418,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;
}
@@ -1321,8 +1445,21 @@ svn_swig_py_unwrap_string(PyObject *sour
void *baton)
{
const char **ptr_dest = destination;
- *ptr_dest = PyBytes_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
@@ -1494,7 +1631,7 @@ commit_item_array_to_list(const apr_arra
}
-
+
/*** Errors ***/
/* Convert a given SubversionException to an svn_error_t. On failure returns
@@ -2546,14 +2683,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;
+ /* 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 */
- const char* fname = PyBytes_AsString(py_file);
apr_err = apr_file_open(&apr_file, fname,
APR_CREATE | APR_READ | APR_WRITE,
APR_OS_DEFAULT, pool);
@@ -3707,7 +3853,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);
}
Modified: subversion/branches/swig-py3/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h?rev=1853592&r1=1853591&r2=1853592&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h (original)
+++ subversion/branches/swig-py3/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h Thu Feb 14 16:48:40 2019
@@ -107,6 +107,17 @@ void svn_swig_py_svn_exception(svn_error
+/* 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);
Modified: subversion/branches/swig-py3/subversion/bindings/swig/python/tests/client.py
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/bindings/swig/python/tests/client.py?rev=1853592&r1=1853591&r2=1853592&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/bindings/swig/python/tests/client.py (original)
+++ subversion/branches/swig-py3/subversion/bindings/swig/python/tests/client.py Thu Feb 14 16:48:40 2019
@@ -52,6 +52,10 @@ class SubversionClientTestCase(unittest.
"""An implementation of svn_log_entry_receiver_t."""
self.received_revisions.append(log_entry.revision)
+ def log_entry_receiver_whole(self, log_entry, pool):
+ """An implementation of svn_log_entry_receiver_t, holds whole log entries."""
+ self.received_log_entries.append(log_entry)
+
def setUp(self):
"""Set up authentication and client context"""
self.client_ctx = client.svn_client_create_context()
@@ -243,6 +247,33 @@ class SubversionClientTestCase(unittest.
self.assertEqual(self.received_revisions, list(range(0, 5)))
+ def test_log5_revprops(self):
+ """Test svn_client_log5 revprops (for typemap(in) apr_array_t *STRINGLIST)"""
+ directory = urljoin(self.repos_uri+b"/", b"trunk/dir1")
+ start = core.svn_opt_revision_t()
+ end = core.svn_opt_revision_t()
+ core.svn_opt_parse_revision(start, end, b"4:0")
+ rev_range = core.svn_opt_revision_range_t()
+ rev_range.start = start
+ rev_range.end = end
+
+ self.received_log_entries = []
+
+ # (Python 3: pass tuple of bytes and str mixture as revprops argment)
+ client.log5((directory,), start, (rev_range,), 1, True, False, False,
+ ('svn:author', b'svn:log'),
+ self.log_entry_receiver_whole, self.client_ctx)
+ self.assertEqual(len(self.received_log_entries), 1)
+ revprops = self.received_log_entries[0].revprops
+ self.assertEqual(revprops[b'svn:log'], b"More directories.")
+ self.assertEqual(revprops[b'svn:author'], b"john")
+ with self.assertRaises(KeyError):
+ commit_date = revprops['svn:date']
+ with self.assertRaises(UnicodeEncodeError):
+ client.log5((directory,), start, (rev_range,), 1, True, False, False,
+ (u'svn:\udc61uthor', b'svn:log'),
+ self.log_entry_receiver_whole, self.client_ctx)
+
def test_uuid_from_url(self):
"""Test svn_client_uuid_from_url on a file:// URL"""
self.assertTrue(isinstance(
Modified: subversion/branches/swig-py3/subversion/bindings/swig/python/tests/core.py
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/bindings/swig/python/tests/core.py?rev=1853592&r1=1853591&r2=1853592&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/bindings/swig/python/tests/core.py (original)
+++ subversion/branches/swig-py3/subversion/bindings/swig/python/tests/core.py Thu Feb 14 16:48:40 2019
@@ -21,6 +21,9 @@
import unittest
import os
import tempfile
+import sys
+
+IS_PY3 = sys.version_info[0] >= 3
import svn.core, svn.client
import utils
@@ -220,13 +223,55 @@ class SubversionCoreTestCase(unittest.Te
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_write(stream, 16)
+ # Check UnicodeEncodeError
+ # o1_str = b'Python\x00\xa4\xd1\xa4\xa4\xa4\xbd\xa4\xf3\r\n'
+ # ostr_unicode = o1_str.decode('ascii', 'surrogateescape')
+ ostr_unicode = (u'Python\x00'
+ u'\udca4\udcd1\udca4\udca4\udca4\udcbd\udca4\udcf3\r\n')
+ with self.assertRaises(UnicodeEncodeError):
+ svn.core.svn_stream_write(stream, ostr_unicode)
svn.core.svn_stream_close(stream)
- def test_stream_write(self):
+ # As default codec of Python 2 is 'ascii', conversion from unicode to bytes
+ # will be success only if all characters of target strings are in the range
+ # of \u0000 ~ \u007f.
+ @unittest.skipUnless(IS_PY3, "test for Python 3 only")
+ def test_stream_write_str(self):
+ o1_str = u'Python\x00\u3071\u3044\u305d\83093\r\n'
+ o2_str = u'subVersioN\x00\u3055\u3076\u3070\u30fc\u3058\u3087\u3093'
+ o3_str = u'swig\x00\u3059\u3046\u3043\u3050\rend'
+ out_str = o1_str + o2_str + o3_str
+ rewrite_str = u'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.encode('UTF-8')))
+ svn.core.svn_stream_seek(stream, None)
+ self.assertEqual(svn.core.svn_stream_read_full(stream, 4096),
+ out_str.encode('UTF-8'))
+ svn.core.svn_stream_seek(stream, None)
+ svn.core.svn_stream_skip(stream, len(o1_str.encode('UTF-8')))
+ self.assertEqual(svn.core.svn_stream_write(stream, rewrite_str),
+ len(rewrite_str.encode('UTF-8')))
+ 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.encode('UTF-8')):]
+ + o3_str ).encode('UTF-8'))
+ svn.core.svn_stream_close(stream)
+ finally:
+ try:
+ os.remove(fname)
+ except OSError:
+ pass
+
+ def test_stream_write_bytes(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')
Modified: subversion/branches/swig-py3/subversion/bindings/swig/python/tests/run_all.py
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/bindings/swig/python/tests/run_all.py?rev=1853592&r1=1853591&r2=1853592&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/bindings/swig/python/tests/run_all.py (original)
+++ subversion/branches/swig-py3/subversion/bindings/swig/python/tests/run_all.py Thu Feb 14 16:48:40 2019
@@ -21,7 +21,7 @@
import sys
import unittest, setup_path
import mergeinfo, core, client, delta, checksum, pool, fs, ra, wc, repository, \
- auth, trac.versioncontrol.tests
+ auth, trac.versioncontrol.tests, typemap
from svn.core import svn_cache_config_get, svn_cache_config_set
# Run all tests
@@ -47,6 +47,7 @@ def suite():
s.addTest(repository.suite())
s.addTest(auth.suite())
s.addTest(trac.versioncontrol.tests.suite())
+ s.addTest(typemap.suite())
return s
if __name__ == '__main__':
Added: subversion/branches/swig-py3/subversion/bindings/swig/python/tests/typemap.py
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/bindings/swig/python/tests/typemap.py?rev=1853592&view=auto
==============================================================================
--- subversion/branches/swig-py3/subversion/bindings/swig/python/tests/typemap.py (added)
+++ subversion/branches/swig-py3/subversion/bindings/swig/python/tests/typemap.py Thu Feb 14 16:48:40 2019
@@ -0,0 +1,133 @@
+#
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#
+import unittest
+import os
+import tempfile
+
+import svn.core
+
+class SubversionTypemapTestCase(unittest.TestCase):
+ """Test cases for the SWIG typemaps argments and return values transration"""
+
+ def test_char_ptr_in(self):
+ """Check %typemap(in) IN_STRING works correctly"""
+ self.assertEqual(svn.core.svn_path_canonicalize(b'foo'), b'foo')
+ self.assertEqual(svn.core.svn_dirent_join(b'foo', 'bar'), b'foo/bar')
+ with self.assertRaises(TypeError) as cm:
+ svn.core.svn_dirent_join(None, b'bar')
+ self.assertEqual(str(cm.exception),
+ "svn_dirent_join() argument base must be"
+ " bytes or str, not %s" % None.__class__.__name__)
+ with self.assertRaises(TypeError) as cm:
+ svn.core.svn_dirent_join(b'foo', self)
+ self.assertEqual(str(cm.exception),
+ "svn_dirent_join() argument component must be"
+ " bytes or str, not %s" % self.__class__.__name__)
+ with self.assertRaises(TypeError) as cm:
+ svn.core.svn_dirent_join('foo', 10)
+ self.assertEqual(str(cm.exception),
+ "svn_dirent_join() argument component must be"
+ " bytes or str, not int")
+
+ def test_char_ptr_in_unicode_exception(self):
+ """Check %typemap(in) IN_STRING handles unicode encode error correctly"""
+ with self.assertRaises(UnicodeEncodeError):
+ svn.core.svn_dirent_join(b'foo', u'\udc62\udc61\udc72')
+
+ def test_char_ptr_may_be_null(self):
+ """Check %typemap(in) char *MAY_BE_NULL works correctly"""
+ cfg = svn.core.svn_config_create2(False, False)
+ self.assertEqual(svn.core.svn_config_get(cfg, b'foo', b'bar', b'baz'),
+ b'baz')
+ self.assertEqual(svn.core.svn_config_get(cfg, b'foo', b'bar', 'baz'),
+ b'baz')
+ self.assertIsNone(svn.core.svn_config_get(cfg, b'foo', b'bar', None))
+ with self.assertRaises(TypeError) as cm:
+ svn.core.svn_config_get(cfg, b'foo', b'bar', self)
+ self.assertEqual(str(cm.exception),
+ "svn_config_get() argument default_value"
+ " must be bytes or str or None, not %s"
+ % self.__class__.__name__)
+
+ def test_char_ptr_may_be_null_unicode_exception(self):
+ """Check %typemap(in) char *MAY_BE_NULL handles unicode encode error correctly"""
+ cfg = svn.core.svn_config_create2(False, False)
+ with self.assertRaises(UnicodeEncodeError):
+ svn.core.svn_config_get(cfg, u'f\udc6fo', b'bar', None)
+
+ def test_make_string_from_ob(self):
+ """Check make_string_from_ob and make_svn_string_from_ob work correctly"""
+ source_props = { b'a' : b'foo',
+ b'b' : 'foo',
+ 'c' : b'' }
+ target_props = { b'a' : '',
+ 'b' : 'bar',
+ b'c' : b'baz' }
+ expected = { b'a' : b'',
+ b'b' : b'bar',
+ b'c' : b'baz' }
+ self.assertEqual(svn.core.svn_prop_diffs(target_props, source_props),
+ expected)
+
+ def test_prophash_from_dict_null_value(self):
+ """Check make_svn_string_from_ob_maybe_null work correctly"""
+ source_props = { 'a' : 'foo',
+ 'b' : 'foo',
+ 'c' : None }
+ target_props = { 'a' : None,
+ 'b' : 'bar',
+ 'c' : 'baz' }
+ expected = { b'a' : None,
+ b'b' : b'bar',
+ b'c' : b'baz' }
+ self.assertEqual(svn.core.svn_prop_diffs(target_props, source_props),
+ expected)
+
+ def test_make_string_from_ob_unicode_exception(self):
+ """Check make_string_from_ob handles unicode encode error correctly"""
+ source_props = { b'a' : b'foo',
+ b'b' : u'foo',
+ u'\udc63' : b'' }
+ target_props = { b'a' : u'',
+ u'b' : u'bar',
+ b'c' : b'baz' }
+ with self.assertRaises(UnicodeEncodeError):
+ svn.core.svn_prop_diffs(target_props, source_props)
+
+ def test_make_svn_string_from_ob_unicode_exception(self):
+ """Check make_string_from_ob handles unicode encode error correctly"""
+ source_props = { b'a' : b'foo',
+ b'b' : 'foo',
+ u'c' : b'' }
+ target_props = { b'a' : u'',
+ u'b' : u'b\udc61r',
+ b'c' : b'baz' }
+ with self.assertRaises(UnicodeEncodeError):
+ svn.core.svn_prop_diffs(target_props, source_props)
+
+
+def suite():
+ return unittest.defaultTestLoader.loadTestsFromTestCase(
+ SubversionTypemapTestCase)
+
+if __name__ == '__main__':
+ runner = unittest.TextTestRunner()
+ runner.run(suite())
Propchange: subversion/branches/swig-py3/subversion/bindings/swig/python/tests/typemap.py
------------------------------------------------------------------------------
svn:eol-style = native