[pypy-commit] pypy default: Add PyOS_string_to_double to cpyext
flub
noreply at buildbot.pypy.org
Sat Jun 25 19:14:43 CEST 2011
Author: Floris Bruynooghe <flub at devork.be>
Branch:
Changeset: r45121:e4b625ca7535
Date: 2011-06-25 18:18 +0200
http://bitbucket.org/pypy/pypy/changeset/e4b625ca7535/
Log: Add PyOS_string_to_double to cpyext
diff --git a/pypy/module/cpyext/__init__.py b/pypy/module/cpyext/__init__.py
--- a/pypy/module/cpyext/__init__.py
+++ b/pypy/module/cpyext/__init__.py
@@ -65,6 +65,7 @@
import pypy.module.cpyext.memoryobject
import pypy.module.cpyext.codecs
import pypy.module.cpyext.pyfile
+import pypy.module.cpyext.pystrtod
# now that all rffi_platform.Struct types are registered, configure them
api.configure_types()
diff --git a/pypy/module/cpyext/pystrtod.py b/pypy/module/cpyext/pystrtod.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/pystrtod.py
@@ -0,0 +1,68 @@
+import errno
+from pypy.interpreter.error import OperationError
+from pypy.module.cpyext.api import cpython_api
+from pypy.module.cpyext.pyobject import PyObject
+from pypy.rlib import rdtoa
+from pypy.rlib import rfloat
+from pypy.rlib import rposix
+from pypy.rpython.lltypesystem import lltype
+from pypy.rpython.lltypesystem import rffi
+
+
+ at cpython_api([rffi.CCHARP, rffi.CCHARPP, PyObject], rffi.DOUBLE, error=-1.0)
+def PyOS_string_to_double(space, s, endptr, w_overflow_exception):
+ """Convert a string s to a double, raising a Python
+ exception on failure. The set of accepted strings corresponds to
+ the set of strings accepted by Python's float() constructor,
+ except that s must not have leading or trailing whitespace.
+ The conversion is independent of the current locale.
+
+ If endptr is NULL, convert the whole string. Raise
+ ValueError and return -1.0 if the string is not a valid
+ representation of a floating-point number.
+
+ If endptr is not NULL, convert as much of the string as
+ possible and set *endptr to point to the first unconverted
+ character. If no initial segment of the string is the valid
+ representation of a floating-point number, set *endptr to point
+ to the beginning of the string, raise ValueError, and return
+ -1.0.
+
+ If s represents a value that is too large to store in a float
+ (for example, "1e500" is such a string on many platforms) then
+ if overflow_exception is NULL return Py_HUGE_VAL (with
+ an appropriate sign) and don't set any exception. Otherwise,
+ overflow_exception must point to a Python exception object;
+ raise that exception and return -1.0. In both cases, set
+ *endptr to point to the first character after the converted value.
+
+ If any other error occurs during the conversion (for example an
+ out-of-memory error), set the appropriate Python exception and
+ return -1.0.
+ """
+ user_endptr = True
+ try:
+ if not endptr:
+ endptr = lltype.malloc(rffi.CCHARPP.TO, 1, flavor='raw')
+ user_endptr = False
+ result = rdtoa.dg_strtod(s, endptr)
+ endpos = (rffi.cast(rffi.LONG, endptr[0]) -
+ rffi.cast(rffi.LONG, s))
+ if endpos == 0 or (not user_endptr and not endptr[0][0] == '\0'):
+ raise OperationError(
+ space.w_ValueError,
+ space.wrap('invalid input at position %s' % endpos))
+ if rposix.get_errno() == errno.ERANGE:
+ rposix.set_errno(0)
+ if w_overflow_exception is None:
+ if result > 0:
+ return rfloat.INFINITY
+ else:
+ return -rfloat.INFINITY
+ else:
+ raise OperationError(w_overflow_exception,
+ space.wrap('value too large'))
+ return result
+ finally:
+ if not user_endptr:
+ lltype.free(endptr, flavor='raw')
diff --git a/pypy/module/cpyext/test/test_pystrtod.py b/pypy/module/cpyext/test/test_pystrtod.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/test/test_pystrtod.py
@@ -0,0 +1,93 @@
+import math
+
+from pypy.module.cpyext.test.test_api import BaseApiTest
+from pypy.rpython.lltypesystem import rffi
+from pypy.rpython.lltypesystem import lltype
+
+
+class TestPyOS_string_to_double(BaseApiTest):
+
+ def test_simple_float(self, api):
+ s = rffi.str2charp('0.4')
+ null = lltype.nullptr(rffi.CCHARPP.TO)
+ r = api.PyOS_string_to_double(s, null, None)
+ assert r == 0.4
+ rffi.free_charp(s)
+
+ def test_empty_string(self, api):
+ s = rffi.str2charp('')
+ null = lltype.nullptr(rffi.CCHARPP.TO)
+ r = api.PyOS_string_to_double(s, null, None)
+ assert r == -1.0
+ raises(ValueError)
+ api.PyErr_Clear()
+ rffi.free_charp(s)
+
+ def test_bad_string(self, api):
+ s = rffi.str2charp(' 0.4')
+ null = lltype.nullptr(rffi.CCHARPP.TO)
+ r = api.PyOS_string_to_double(s, null, None)
+ assert r == -1.0
+ raises(ValueError)
+ api.PyErr_Clear()
+ rffi.free_charp(s)
+
+ def test_overflow_pos(self, api):
+ s = rffi.str2charp('1e500')
+ null = lltype.nullptr(rffi.CCHARPP.TO)
+ r = api.PyOS_string_to_double(s, null, None)
+ assert math.isinf(r)
+ assert r > 0
+ rffi.free_charp(s)
+
+ def test_overflow_neg(self, api):
+ s = rffi.str2charp('-1e500')
+ null = lltype.nullptr(rffi.CCHARPP.TO)
+ r = api.PyOS_string_to_double(s, null, None)
+ assert math.isinf(r)
+ assert r < 0
+ rffi.free_charp(s)
+
+ def test_overflow_exc(self, space, api):
+ s = rffi.str2charp('1e500')
+ null = lltype.nullptr(rffi.CCHARPP.TO)
+ r = api.PyOS_string_to_double(s, null, space.w_ValueError)
+ assert r == -1.0
+ raises(ValueError)
+ api.PyErr_Clear()
+ rffi.free_charp(s)
+
+ def test_endptr_number(self, api):
+ s = rffi.str2charp('0.4')
+ endp = lltype.malloc(rffi.CCHARPP.TO, 1, flavor='raw')
+ r = api.PyOS_string_to_double(s, endp, None)
+ assert r == 0.4
+ endp_addr = rffi.cast(rffi.LONG, endp[0])
+ s_addr = rffi.cast(rffi.LONG, s)
+ assert endp_addr == s_addr + 3
+ rffi.free_charp(s)
+ lltype.free(endp, flavor='raw')
+
+ def test_endptr_tail(self, api):
+ s = rffi.str2charp('0.4 foo')
+ endp = lltype.malloc(rffi.CCHARPP.TO, 1, flavor='raw')
+ r = api.PyOS_string_to_double(s, endp, None)
+ assert r == 0.4
+ endp_addr = rffi.cast(rffi.LONG, endp[0])
+ s_addr = rffi.cast(rffi.LONG, s)
+ assert endp_addr == s_addr + 3
+ rffi.free_charp(s)
+ lltype.free(endp, flavor='raw')
+
+ def test_endptr_no_conversion(self, api):
+ s = rffi.str2charp('foo')
+ endp = lltype.malloc(rffi.CCHARPP.TO, 1, flavor='raw')
+ r = api.PyOS_string_to_double(s, endp, None)
+ assert r == -1.0
+ raises(ValueError)
+ endp_addr = rffi.cast(rffi.LONG, endp[0])
+ s_addr = rffi.cast(rffi.LONG, s)
+ assert endp_addr == s_addr
+ api.PyErr_Clear()
+ rffi.free_charp(s)
+ lltype.free(endp, flavor='raw')
More information about the pypy-commit
mailing list