Skip to content

Commit 2d4a048

Browse files
committed
Fixes in PyUnicode_FromFormat
1 parent 9c3d6ab commit 2d4a048

File tree

2 files changed

+124
-4
lines changed
  • graalpython
    • com.oracle.graal.python.test/src/tests/cpyext
    • com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi

2 files changed

+124
-4
lines changed

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_unicode.py

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
# SOFTWARE.
3939

4040
import sys
41+
import re
4142

4243
from . import CPyExtTestCase, CPyExtFunction, unhandled_error_compare, GRAALPYTHON
4344

@@ -154,6 +155,29 @@ class Dummy():
154155
pass
155156

156157

158+
class Displayable:
159+
def __str__(self):
160+
return 'str: 文字列'
161+
162+
def __repr__(self):
163+
return 'repr: 文字列'
164+
165+
166+
class BrokenDisplayable:
167+
def __str__(self):
168+
raise NotImplementedError
169+
170+
def __repr__(self):
171+
raise NotImplementedError
172+
173+
174+
def compare_ptr_string(x, y):
175+
if isinstance(x, str) and isinstance(y, str):
176+
x = re.sub(r'0x[0-9a-fA-F]+', '0xPLACEHOLDER', x)
177+
y = re.sub(r'0x[0-9a-fA-F]+', '0xPLACEHOLDER', y)
178+
return unhandled_error_compare(x, y)
179+
180+
157181
def gen_intern_args():
158182
args = (
159183
("some text",),
@@ -199,9 +223,11 @@ def compile_module(self, name):
199223
)
200224

201225
test_PyUnicode_FromFormat0 = CPyExtFunction(
202-
_reference_fromformat,
226+
lambda args: args[0].replace('%%', '%'),
203227
lambda: (
204228
("hello, world!",),
229+
("<%%>",),
230+
("<%6>",),
205231
),
206232
code="""PyObject* wrap_PyUnicode_FromFormat0(char* fmt) {
207233
return PyUnicode_FromFormat(fmt);
@@ -227,6 +253,91 @@ def compile_module(self, name):
227253
cmpfunc=unhandled_error_compare
228254
)
229255

256+
test_PyUnicode_FromFormat_U = CPyExtFunction(
257+
lambda args: f'obj({args[1]})',
258+
lambda: (
259+
("obj(%U)", "str"),
260+
),
261+
resultspec="O",
262+
argspec='sO',
263+
arguments=["char* fmt", "PyObject* obj"],
264+
callfunction="PyUnicode_FromFormat",
265+
cmpfunc=unhandled_error_compare
266+
)
267+
268+
test_PyUnicode_FromFormat_V = CPyExtFunction(
269+
lambda args: f'obj({args[1] or args[2]})',
270+
lambda: (
271+
("obj(%V)", "str", "fallback"),
272+
("obj(%V)", None, "fallback"),
273+
),
274+
code="""PyObject* wrap_PyUnicode_FromFormat_V(char* fmt, PyObject* obj, char* fallback) {
275+
if (obj == Py_None)
276+
obj = NULL;
277+
return PyUnicode_FromFormat(fmt, obj, fallback);
278+
}
279+
""",
280+
resultspec="O",
281+
argspec='sOs',
282+
arguments=["char* fmt", "PyObject* obj", "char* fallback"],
283+
callfunction="wrap_PyUnicode_FromFormat_V",
284+
cmpfunc=unhandled_error_compare
285+
)
286+
287+
test_PyUnicode_FromFormat_S = CPyExtFunction(
288+
lambda args: f'obj({args[1]})',
289+
lambda: (
290+
("obj(%S)", "str"),
291+
("obj(%S)", Displayable()),
292+
("obj(%S)", BrokenDisplayable()),
293+
),
294+
resultspec="O",
295+
argspec='sO',
296+
arguments=["char* fmt", "PyObject* obj"],
297+
callfunction="PyUnicode_FromFormat",
298+
cmpfunc=unhandled_error_compare
299+
)
300+
301+
test_PyUnicode_FromFormat_R = CPyExtFunction(
302+
lambda args: f'obj({args[1]!r})',
303+
lambda: (
304+
("obj(%R)", "str"),
305+
("obj(%R)", Displayable()),
306+
("obj(%R)", BrokenDisplayable()),
307+
),
308+
resultspec="O",
309+
argspec='sO',
310+
arguments=["char* fmt", "PyObject* obj"],
311+
callfunction="PyUnicode_FromFormat",
312+
cmpfunc=unhandled_error_compare
313+
)
314+
315+
test_PyUnicode_FromFormat_A = CPyExtFunction(
316+
lambda args: f'obj({args[1]!a})',
317+
lambda: (
318+
("obj(%A)", "str"),
319+
("obj(%A)", Displayable()),
320+
("obj(%A)", BrokenDisplayable()),
321+
),
322+
resultspec="O",
323+
argspec='sO',
324+
arguments=["char* fmt", "PyObject* obj"],
325+
callfunction="PyUnicode_FromFormat",
326+
cmpfunc=unhandled_error_compare
327+
)
328+
329+
test_PyUnicode_FromFormat_p = CPyExtFunction(
330+
lambda args: "ptr(0xdeadbeef)",
331+
lambda: (
332+
("ptr(%p)", object()),
333+
),
334+
resultspec="O",
335+
argspec='sO',
336+
arguments=["char* fmt", "PyObject* obj"],
337+
callfunction="PyUnicode_FromFormat",
338+
cmpfunc=compare_ptr_string
339+
)
340+
230341
test_PyUnicode_FromFormat4 = CPyExtFunction(
231342
_reference_fromformat,
232343
lambda: (

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3274,7 +3274,7 @@ public abstract static class UnicodeFromFormatNode extends Node {
32743274

32753275
private static Matcher match(String formatStr) {
32763276
if (pattern == null) {
3277-
pattern = Pattern.compile("%(?<flags>[-\\+ #0])?(?<width>\\d+)?(\\.(?<prec>\\d+))?(?<len>(l|ll|z))?(?<spec>[%cduixsAUVSR])");
3277+
pattern = Pattern.compile("%(?<flags>[-+ #0])?(?<width>\\d+)?(\\.(?<prec>\\d+))?(?<len>(l|ll|z))?(?<spec>[%cduixspAUVSR])");
32783278
}
32793279
return pattern.matcher(formatStr);
32803280
}
@@ -3298,6 +3298,7 @@ Object doGeneric(String format, Object vaList) {
32983298
current.set(this);
32993299
StringBuilder result = new StringBuilder();
33003300
int vaArgIdx = 0;
3301+
Object unicodeObj;
33013302
try {
33023303
Matcher matcher = match(format);
33033304
int cur = 0;
@@ -3320,6 +3321,7 @@ Object doGeneric(String format, Object vaList) {
33203321
case '%':
33213322
// %%
33223323
result.append('%');
3324+
valid = true;
33233325
break;
33243326
case 'c':
33253327
int ordinal = getAndCastToInt(getVaArgsNode, interopLibrary, raiseNode, vaList, vaArgIdx, LLVMType.int_t);
@@ -3393,8 +3395,15 @@ Object doGeneric(String format, Object vaList) {
33933395
break;
33943396
case 's':
33953397
// %s
3396-
Object unicodeObj = fromCharPointerNode.execute(getVaArgsNode.getCharPtr(vaList, vaArgIdx));
3397-
String sValue = castToJavaStringNode.execute(unicodeObj);
3398+
Object charPtr = getVaArgsNode.getCharPtr(vaList, vaArgIdx);
3399+
String sValue;
3400+
if (interopLibrary.isNull(charPtr)) {
3401+
// CPython would segfault. Let's make debugging easier for ourselves
3402+
sValue = "(NULL)";
3403+
} else {
3404+
unicodeObj = fromCharPointerNode.execute(charPtr);
3405+
sValue = castToJavaStringNode.execute(unicodeObj);
3406+
}
33983407
try {
33993408
if (prec == -1) {
34003409
result.append(sValue);

0 commit comments

Comments
 (0)