Skip to content

Commit ce51b18

Browse files
authored
Merge pull request #52 from python/master
[mypyc] Support str.replace (python#10088)
2 parents d386b0f + 15bd486 commit ce51b18

File tree

6 files changed

+74
-0
lines changed

6 files changed

+74
-0
lines changed

mypyc/lib-rt/CPy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ static inline char CPyDict_CheckSize(PyObject *dict, CPyTagged size) {
380380

381381
PyObject *CPyStr_GetItem(PyObject *str, CPyTagged index);
382382
PyObject *CPyStr_Split(PyObject *str, PyObject *sep, CPyTagged max_split);
383+
PyObject *CPyStr_Replace(PyObject *str, PyObject *old_substr, PyObject *new_substr, CPyTagged max_replace);
383384
PyObject *CPyStr_Append(PyObject *o1, PyObject *o2);
384385
PyObject *CPyStr_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end);
385386
bool CPyStr_Startswith(PyObject *self, PyObject *subobj);

mypyc/lib-rt/str_ops.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ PyObject *CPyStr_Split(PyObject *str, PyObject *sep, CPyTagged max_split)
5353
return PyUnicode_Split(str, sep, temp_max_split);
5454
}
5555

56+
PyObject *CPyStr_Replace(PyObject *str, PyObject *old_substr, PyObject *new_substr, CPyTagged max_replace)
57+
{
58+
Py_ssize_t temp_max_replace = CPyTagged_AsSsize_t(max_replace);
59+
if (temp_max_replace == -1 && PyErr_Occurred()) {
60+
PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C ssize_t");
61+
return NULL;
62+
}
63+
return PyUnicode_Replace(str, old_substr, new_substr, temp_max_replace);
64+
}
65+
5666
bool CPyStr_Startswith(PyObject *self, PyObject *subobj) {
5767
Py_ssize_t start = 0;
5868
Py_ssize_t end = PyUnicode_GET_LENGTH(self);

mypyc/primitives/str_ops.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,20 @@
109109
return_type=object_rprimitive,
110110
c_function_name='CPyStr_GetSlice',
111111
error_kind=ERR_MAGIC)
112+
113+
# str.replace(old, new)
114+
method_op(
115+
name='replace',
116+
arg_types=[str_rprimitive, str_rprimitive, str_rprimitive],
117+
return_type=str_rprimitive,
118+
c_function_name="PyUnicode_Replace",
119+
error_kind=ERR_MAGIC,
120+
extra_int_constants=[(-1, c_int_rprimitive)])
121+
122+
# str.replace(old, new, count)
123+
method_op(
124+
name='replace',
125+
arg_types=[str_rprimitive, str_rprimitive, str_rprimitive, int_rprimitive],
126+
return_type=str_rprimitive,
127+
c_function_name="CPyStr_Replace",
128+
error_kind=ERR_MAGIC)

mypyc/test-data/fixtures/ir.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def format(self, *args: Any, **kwargs: Any) -> str: ...
7575
def upper(self) -> str: pass
7676
def startswith(self, x: str, start: int=..., end: int=...) -> bool: pass
7777
def endswith(self, x: str, start: int=..., end: int=...) -> bool: pass
78+
def replace(self, old: str, new: str, maxcount: Optional[int] = None) -> str: pass
7879

7980
class float:
8081
def __init__(self, x: object) -> None: pass

mypyc/test-data/irbuild-str.test

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,39 @@ L3:
103103
r5 = r0 != 0
104104
return r5
105105

106+
[case testStrReplace]
107+
from typing import Optional
108+
109+
def do_replace(s: str, old_substr: str, new_substr: str, max_count: Optional[int] = None) -> str:
110+
if max_count is not None:
111+
return s.replace(old_substr, new_substr, max_count)
112+
else:
113+
return s.replace(old_substr, new_substr)
114+
[out]
115+
def do_replace(s, old_substr, new_substr, max_count):
116+
s, old_substr, new_substr :: str
117+
max_count :: union[int, None]
118+
r0, r1 :: object
119+
r2, r3 :: bit
120+
r4 :: int
121+
r5, r6 :: str
122+
L0:
123+
if is_error(max_count) goto L1 else goto L2
124+
L1:
125+
r0 = box(None, 1)
126+
max_count = r0
127+
L2:
128+
r1 = box(None, 1)
129+
r2 = max_count == r1
130+
r3 = r2 ^ 1
131+
if r3 goto L3 else goto L4 :: bool
132+
L3:
133+
r4 = unbox(int, max_count)
134+
r5 = CPyStr_Replace(s, old_substr, new_substr, r4)
135+
return r5
136+
L4:
137+
r6 = PyUnicode_Replace(s, old_substr, new_substr, -1)
138+
return r6
139+
L5:
140+
unreachable
141+

mypyc/test-data/run-strings.test

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,12 @@ def test_slicing() -> None:
150150
assert s[1:big_int] == "oobar"
151151
assert s[big_int:] == ""
152152
assert s[-big_int:-1] == "fooba"
153+
154+
def test_str_replace() -> None:
155+
a = "foofoofoo"
156+
assert a.replace("foo", "bar") == "barbarbar"
157+
assert a.replace("foo", "bar", -1) == "barbarbar"
158+
assert a.replace("foo", "bar", 1) == "barfoofoo"
159+
assert a.replace("foo", "bar", 4) == "barbarbar"
160+
assert a.replace("aaa", "bar") == "foofoofoo"
161+
assert a.replace("ofo", "xyzw") == "foxyzwxyzwo"

0 commit comments

Comments
 (0)