summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Ayd <william.ayd@icloud.com>2020-04-01 22:53:00 -0700
committerGitHub <noreply@github.com>2020-04-02 07:53:00 +0200
commit2c8e7b2204622380c20ee4b1f2a9aa59938fe33a (patch)
treec4fba6b5e4f3d3d8f3f5e313400432688fa454b4
parente9d7fd45a2e829ee93980b1c0612f8476a46431d (diff)
downloadcython-2c8e7b2204622380c20ee4b1f2a9aa59938fe33a.tar.gz
Optimize builtin str() calls (GH-3478)
-rw-r--r--Cython/Compiler/Optimize.py32
-rw-r--r--Cython/Utility/StringTools.c19
-rw-r--r--tests/run/strfunction.pyx32
3 files changed, 83 insertions, 0 deletions
diff --git a/Cython/Compiler/Optimize.py b/Cython/Compiler/Optimize.py
index 65f5bf2d0..e12abb22a 100644
--- a/Cython/Compiler/Optimize.py
+++ b/Cython/Compiler/Optimize.py
@@ -2312,6 +2312,38 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
return ExprNodes.CachedBuiltinMethodCallNode(
node, function.obj, attr_name, arg_list)
+ PyObject_String_func_type = PyrexTypes.CFuncType(
+ Builtin.str_type, [
+ PyrexTypes.CFuncTypeArg("obj", PyrexTypes.py_object_type, None)
+ ])
+
+ def _handle_simple_function_str(self, node, function, pos_args):
+ """Optimize single argument calls to str().
+ """
+ if len(pos_args) != 1:
+ if len(pos_args) == 0:
+ return ExprNodes.StringNode(node.pos, value=EncodedString(), constant_result='')
+ return node
+ arg = pos_args[0]
+
+ if arg.type is Builtin.str_type:
+ if not arg.may_be_none():
+ return arg
+
+ cname = "__Pyx_PyStr_Str"
+ utility_code = UtilityCode.load_cached('PyStr_Str', 'StringTools.c')
+ else:
+ cname = '__Pyx_PyObject_Str'
+ utility_code = UtilityCode.load_cached('PyObject_Str', 'StringTools.c')
+
+ return ExprNodes.PythonCapiCallNode(
+ node.pos, cname, self.PyObject_String_func_type,
+ args=pos_args,
+ is_temp=node.is_temp,
+ utility_code=utility_code,
+ py_name="str"
+ )
+
PyObject_Unicode_func_type = PyrexTypes.CFuncType(
Builtin.unicode_type, [
PyrexTypes.CFuncTypeArg("obj", PyrexTypes.py_object_type, None)
diff --git a/Cython/Utility/StringTools.c b/Cython/Utility/StringTools.c
index 9b20c4a2b..29a151d80 100644
--- a/Cython/Utility/StringTools.c
+++ b/Cython/Utility/StringTools.c
@@ -1219,3 +1219,22 @@ static CYTHON_INLINE PyObject* __Pyx_PyUnicode_Unicode(PyObject *obj) {
#define __Pyx_PyObject_Unicode(obj) \
(likely(PyUnicode_CheckExact(obj)) ? __Pyx_NewRef(obj) : PyObject_Unicode(obj))
#endif
+
+
+//////////////////// PyStr_Str.proto ////////////////////
+
+static CYTHON_INLINE PyObject* __Pyx_PyStr_Str(PyObject *obj);/*proto*/
+
+//////////////////// PyStr_Str ////////////////////
+
+static CYTHON_INLINE PyObject* __Pyx_PyStr_Str(PyObject *obj) {
+ if (unlikely(obj == Py_None))
+ obj = PYIDENT("None");
+ return __Pyx_NewRef(obj);
+}
+
+
+//////////////////// PyObject_Str.proto ////////////////////
+
+#define __Pyx_PyObject_Str(obj) \
+ (likely(PyString_CheckExact(obj)) ? __Pyx_NewRef(obj) : PyObject_Str(obj))
diff --git a/tests/run/strfunction.pyx b/tests/run/strfunction.pyx
index dc6adaf79..d4a1c95d4 100644
--- a/tests/run/strfunction.pyx
+++ b/tests/run/strfunction.pyx
@@ -5,6 +5,8 @@ __doc__ = u"""
'test'
"""
+cimport cython
+
s = str
z = str('test')
@@ -39,3 +41,33 @@ def sub(string):
#def csub(string):
# return csubs(string)
+
+
+@cython.test_fail_if_path_exists("//SimpleCallNode")
+@cython.test_assert_path_exists("//PythonCapiCallNode")
+def typed(str s):
+ """
+ >>> print(typed(None))
+ None
+ >>> type(typed(None)) is type(typed(None))
+ True
+ >>> print(typed('abc'))
+ abc
+ >>> type(typed('abc')) is type(typed('abc'))
+ True
+ """
+ return str(s)
+
+
+@cython.test_fail_if_path_exists(
+ "//SimpleCallNode",
+ "//PythonCapiCallNode",
+)
+def typed_not_none(str s not None):
+ """
+ >>> print(typed('abc'))
+ abc
+ >>> type(typed('abc')) is type(typed('abc'))
+ True
+ """
+ return str(s)