[Python] Add string_view support
Only implemented for Python 3, at least for now.
See #1567
diff --git a/CHANGES.current b/CHANGES.current
index a78117a..aaf0e9e 100644
--- a/CHANGES.current
+++ b/CHANGES.current
@@ -134,7 +134,7 @@
2023-05-08: olly
#1567 Add support for std::string_view (new in C++17) for C#, Java,
- Lua, Perl, PHP, Ruby and Tcl.
+ Lua, Perl, PHP, Python, Ruby and Tcl.
2023-05-08: olly
[PHP] #2544 Wrap overloaded method with both static and non-static
diff --git a/Examples/test-suite/cpp17_string_view.i b/Examples/test-suite/cpp17_string_view.i
index 011dc02..ae10837 100644
--- a/Examples/test-suite/cpp17_string_view.i
+++ b/Examples/test-suite/cpp17_string_view.i
@@ -1,5 +1,5 @@
%module cpp17_string_view
-#if defined SWIGCSHARP || defined SWIGJAVA || defined SWIGLUA || defined SWIGPERL || defined SWIGPHP || defined SWIGRUBY || defined SWIGTCL
+#if defined SWIGCSHARP || defined SWIGJAVA || defined SWIGLUA || defined SWIGPERL || defined SWIGPHP || defined SWIGPYTHON || defined SWIGRUBY || defined SWIGTCL
%include <std_string_view.i>
// throw is invalid in C++17 and later, only SWIG to use it
diff --git a/Examples/test-suite/director_string_view.i b/Examples/test-suite/director_string_view.i
index d95c454..f4eb339 100644
--- a/Examples/test-suite/director_string_view.i
+++ b/Examples/test-suite/director_string_view.i
@@ -1,6 +1,6 @@
%module(directors="1") director_string_view;
-#if defined SWIGCSHARP || defined SWIGJAVA || defined SWIGLUA || defined SWIGPERL || defined SWIGPHP || defined SWIGRUBY
+#if defined SWIGCSHARP || defined SWIGJAVA || defined SWIGLUA || defined SWIGPERL || defined SWIGPHP || defined SWIGPYTHON || defined SWIGRUBY
%include std_string.i
%include std_string_view.i
diff --git a/Examples/test-suite/python/Makefile.in b/Examples/test-suite/python/Makefile.in
index 46e23db..e308445 100644
--- a/Examples/test-suite/python/Makefile.in
+++ b/Examples/test-suite/python/Makefile.in
@@ -22,6 +22,14 @@
top_srcdir = @top_srcdir@
top_builddir = @top_builddir@
+ifneq (,$(PY2))
+# std_string_view.i is not implemented for Python2 so don't try to run tests needing it.
+FAILING_CPP_TESTS = \
+ cpp17_director_string_view \
+ cpp17_string_view \
+
+endif
+
CPP_TEST_CASES += \
callback \
complextest \
diff --git a/Examples/test-suite/python/cpp17_string_view_runme.py b/Examples/test-suite/python/cpp17_string_view_runme.py
new file mode 100644
index 0000000..10b0d4d
--- /dev/null
+++ b/Examples/test-suite/python/cpp17_string_view_runme.py
@@ -0,0 +1,51 @@
+import cpp17_string_view
+
+import sys
+if sys.version_info[0:1] < (3, 0):
+ # string_view.i only supported for Python2
+ sys.exit(0)
+
+# Checking expected use of %typemap(in) std::string_view {}
+cpp17_string_view.test_value("Fee")
+
+# Checking expected result of %typemap(out) std::string_view {}
+if cpp17_string_view.test_value("Fi") != "Fi":
+ raise RuntimeError("Test 1: "+cpp17_string_view.test_value("Fi"))
+
+# Checking expected use of %typemap(in) const std::string_view & {}
+cpp17_string_view.test_const_reference("Fo")
+
+# Checking expected result of %typemap(out) const std.string_view& {}
+if cpp17_string_view.test_const_reference("Fum") != "Fum":
+ raise RuntimeError("Test 3")
+
+# Input and output typemaps for pointers and non-const references to
+# std::string_view are *not* supported; the following tests confirm
+# that none of these cases are slipping through.
+
+stringPtr = cpp17_string_view.test_pointer_out()
+
+cpp17_string_view.test_pointer(stringPtr)
+
+stringPtr = cpp17_string_view.test_const_pointer_out()
+
+cpp17_string_view.test_const_pointer(stringPtr)
+
+stringPtr = cpp17_string_view.test_reference_out()
+
+cpp17_string_view.test_reference(stringPtr)
+
+# Global variables
+s = "initial string"
+if cpp17_string_view.ConstGlobalString != "const global string":
+ raise RuntimeError("ConstGlobalString test")
+
+# Member variables
+myStructure = cpp17_string_view.Structure()
+if myStructure.ConstMemberString != "const member string":
+ raise RuntimeError("ConstMemberString test")
+
+if cpp17_string_view.Structure.ConstStaticMemberString != "const static member string":
+ raise RuntimeError("ConstStaticMemberString test")
+
+cpp17_string_view.test_const_reference_returning_void("foo")
diff --git a/Examples/test-suite/python/director_string_view_runme.py b/Examples/test-suite/python/director_string_view_runme.py
new file mode 100644
index 0000000..4390e2b
--- /dev/null
+++ b/Examples/test-suite/python/director_string_view_runme.py
@@ -0,0 +1,27 @@
+from director_string_view import *
+
+
+class B(A):
+
+ def __init__(self, string):
+ A.__init__(self, string)
+
+ def get_first(self):
+ return A.get_first(self) + " world!"
+
+ def process_text(self, string):
+ A.process_text(self, string)
+ self.smem = "hello"
+
+
+b = B("hello")
+
+b.get(0)
+if b.get_first() != "hello world!":
+ raise RuntimeError("b.get_first(): {}".format(b.get_first()))
+
+
+b.call_process_func()
+
+if b.smem != "hello":
+ raise RuntimeError("smem: {}".format(smem))
diff --git a/Lib/python/std_string_view.i b/Lib/python/std_string_view.i
new file mode 100644
index 0000000..f98b483
--- /dev/null
+++ b/Lib/python/std_string_view.i
@@ -0,0 +1,133 @@
+/* -----------------------------------------------------------------------------
+ * std_string_view.i
+ *
+ * SWIG typemaps for std::string_view types
+ * ----------------------------------------------------------------------------- */
+
+%include <exception.i>
+
+%{
+#include <string_view>
+
+#if PY_VERSION_HEX < 0x03000000
+# error std_string_view.i not supported for Python 2
+#endif
+%}
+
+namespace std {
+
+ %naturalvar string_view;
+
+ class string_view;
+
+ %typemap(typecheck,precedence=SWIG_TYPECHECK_STRINGVIEW) string_view, const string_view& %{
+#ifdef SWIG_PYTHON_STRICT_BYTE_CHAR
+ $1 = PyBytes_Check($input);
+#else
+ $1 = PyUnicode_Check($input);
+#endif
+ %}
+
+ %typemap(in) string_view {
+ Py_ssize_t len;
+%#ifdef SWIG_PYTHON_STRICT_BYTE_CHAR
+ const char *p = PyBytes_AsString($input);
+ if (!p) SWIG_fail;
+ len = PyBytes_Size($input);
+%#else
+ /* Note: The UTF-8 data is cached in the PyObject so remains valid for
+ * the call to C/C++. */
+ const char *p = PyUnicode_AsUTF8AndSize($input, &len);
+ if (!p) SWIG_fail;
+%#endif
+ $1 = std::string_view(p, len);
+ }
+
+ %typemap(in) const string_view& ($*1_ltype temp) {
+ Py_ssize_t len;
+#ifdef SWIG_PYTHON_STRICT_BYTE_CHAR
+ const char *p = PyBytes_AsString($input);
+ if (!p) SWIG_fail;
+ len = PyBytes_Size($input);
+#else
+ /* Note: The UTF-8 data is cached in the PyObject so remains valid for
+ * the call to C/C++. */
+ const char *p = PyUnicode_AsUTF8AndSize($input, &len);
+ if (!p) SWIG_fail;
+#endif
+ temp = std::string_view(p, len);
+ $1 = &temp;
+ }
+
+ %typemap(directorout) string_view {
+ Py_ssize_t len;
+%#ifdef SWIG_PYTHON_STRICT_BYTE_CHAR
+ const char *p = PyBytes_AsString($input);
+ len = PyBytes_Size($input);
+%#else
+ /* Note: The UTF-8 data is cached in the PyObject so remains valid for
+ * the call to C/C++. */
+ const char *p = PyUnicode_AsUTF8AndSize($input, &len);
+%#endif
+ if (p) $result = std::string_view(p, len);
+ }
+
+ %typemap(out) string_view %{
+#ifdef SWIG_PYTHON_STRICT_BYTE_CHAR
+ $result = PyBytes_FromStringAndSize($1.data(), $1.size());
+#else
+ $result = PyUnicode_FromStringAndSize($1.data(), $1.size());
+#endif
+ %}
+
+ %typemap(varout) string_view %{
+#ifdef SWIG_PYTHON_STRICT_BYTE_CHAR
+ $result = PyBytes_FromStringAndSize($1.data(), $1.size());
+#else
+ $result = PyUnicode_FromStringAndSize($1.data(), $1.size());
+#endif
+ %}
+
+ %typemap(directorin) string_view, const string_view& %{
+#ifdef SWIG_PYTHON_STRICT_BYTE_CHAR
+ $input = PyBytes_FromStringAndSize($1.data(), $1.size());
+#else
+ $input = PyUnicode_FromStringAndSize($1.data(), $1.size());
+#endif
+ %}
+
+ %typemap(out) const string_view& %{
+#ifdef SWIG_PYTHON_STRICT_BYTE_CHAR
+ $result = PyBytes_FromStringAndSize($1->data(), $1->size());
+#else
+ $result = PyUnicode_FromStringAndSize($1->data(), $1->size());
+#endif
+ %}
+
+#if 0
+ %typemap(throws) string_view, const string_view& %{
+ {
+ zval swig_exception;
+ ZVAL_STRINGL(&swig_exception, $1.data(), $1.size());
+ zend_throw_exception_object(&swig_exception);
+ goto fail;
+ }
+ %}
+
+ %typemap(throws) string_view*, const string_view* %{
+ {
+ zval swig_exception;
+ ZVAL_STRINGL(&swig_exception, $1->data(), $1->size());
+ zend_throw_exception_object(&swig_exception);
+ goto fail;
+ }
+ %}
+
+ %typemap(in, phptype="string") const string_view& ($*1_ltype temp) %{
+ convert_to_string(&$input);
+ temp = std::string_view(Z_STRVAL($input), Z_STRLEN($input));
+ $1 = &temp;
+ %}
+#endif
+
+}