Author: Amaury Forgeot d'Arc <[email protected]>
Branch: decimal-libmpdec
Changeset: r71452:46b622916a28
Date: 2014-05-05 21:22 +0200
http://bitbucket.org/pypy/pypy/changeset/46b622916a28/
Log: More constructors to Decimal, add __str__
diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py
--- a/pypy/interpreter/error.py
+++ b/pypy/interpreter/error.py
@@ -434,11 +434,8 @@
self._value = value
self.setup(w_type)
- def get_w_value(self, space):
- w_value = self._w_value
- if w_value is None:
- self._w_value = w_value = space.wrap(self._value)
- return w_value
+ def _compute_value(self, space):
+ return self._value
@specialize.memo()
def get_operr_class(valuefmt):
diff --git a/pypy/module/_decimal/__init__.py b/pypy/module/_decimal/__init__.py
--- a/pypy/module/_decimal/__init__.py
+++ b/pypy/module/_decimal/__init__.py
@@ -12,12 +12,13 @@
'Context': 'interp_context.W_Context',
'getcontext': 'interp_context.getcontext',
'setcontext': 'interp_context.setcontext',
+ 'DecimalException': 'interp_signals.get(space).w_DecimalException',
'IEEE_CONTEXT_MAX_BITS':
'space.wrap(interp_decimal.IEEE_CONTEXT_MAX_BITS)',
'MAX_PREC': 'space.wrap(interp_decimal.MAX_PREC)',
}
for name in rmpdec.ROUND_CONSTANTS:
interpleveldefs[name] = 'space.wrap(%r)' % name
- for name in interp_signals.SIGNAL_NAMES:
+ for name, flag in interp_signals.SIGNAL_MAP:
interpleveldefs[name] = 'interp_signals.get(space).w_%s' % name
diff --git a/pypy/module/_decimal/interp_context.py
b/pypy/module/_decimal/interp_context.py
--- a/pypy/module/_decimal/interp_context.py
+++ b/pypy/module/_decimal/interp_context.py
@@ -1,7 +1,7 @@
from rpython.rlib import rmpdec
from rpython.rlib.unroll import unrolling_iterable
from rpython.rtyper.lltypesystem import rffi, lltype
-from pypy.interpreter.error import oefmt
+from pypy.interpreter.error import oefmt, OperationError
from pypy.interpreter.baseobjspace import W_Root
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import (
@@ -55,6 +55,7 @@
zero=True,
track_allocation=False)
self.w_flags = space.call_function(state_get(space).W_SignalDict)
+ self.capitals = 0
def __del__(self):
if self.ctx:
@@ -62,12 +63,15 @@
def addstatus(self, space, status):
"Add resulting status to context, and eventually raise an exception."
- self.ctx.c_status |= status
- if status & rmpdec.MPD_Malloc_error:
+ new_status = (rffi.cast(lltype.Signed, status) |
+ rffi.cast(lltype.Signed, self.ctx.c_status))
+ self.ctx.c_status = rffi.cast(rffi.UINT, new_status)
+ if new_status & rmpdec.MPD_Malloc_error:
raise OperationError(space.w_MemoryError, space.w_None)
- if status & self.ctx.c_traps:
- raise interp_signals.flags_as_exception(
- space, self.ctx.c_traps & status)
+ to_trap = (rffi.cast(lltype.Signed, status) &
+ rffi.cast(lltype.Signed, self.ctx.c_traps))
+ if to_trap:
+ raise interp_signals.flags_as_exception(space, to_trap)
def copy_w(self, space):
w_copy = W_Context(space)
@@ -158,21 +162,45 @@
def setcontext(space, w_context):
ec = space.getexecutioncontext()
- ec.decimal_context = w_context
+ ec.decimal_context = space.interp_w(W_Context, w_context)
+
+def ensure_context(space, w_context):
+ context = space.interp_w(W_Context, w_context,
+ can_be_None=True)
+ if context is None:
+ context = getcontext(space)
+ return context
class ConvContext:
- def __init__(self, context, exact):
+ def __init__(self, space, mpd, context, exact):
+ self.space = space
+ self.mpd = mpd
+ self.context = context
self.exact = exact
+
+ def __enter__(self):
if self.exact:
self.ctx = lltype.malloc(rmpdec.MPD_CONTEXT_PTR.TO, flavor='raw',
zero=True)
rmpdec.mpd_maxcontext(self.ctx)
else:
- self.ctx = context.ctx
-
- def __enter__(self):
- return self.ctx
+ self.ctx = self.context.ctx
+ self.status_ptr = lltype.malloc(rffi.CArrayPtr(rffi.UINT).TO, 1,
+ flavor='raw', zero=True)
+ return self.ctx, self.status_ptr
def __exit__(self, *args):
if self.exact:
lltype.free(self.ctx, flavor='raw')
+ # we want exact results
+ status = rffi.cast(lltype.Signed, self.status_ptr[0])
+ if status & (rmpdec.MPD_Inexact |
+ rmpdec.MPD_Rounded |
+ rmpdec.MPD_Clamped):
+ rmpdec.seterror(self.mpd,
+ rmpdec.MPD_Invalid_operation, status_ptr)
+ status = rffi.cast(lltype.Signed, self.status_ptr[0])
+ lltype.free(self.status_ptr, flavor='raw')
+ status &= rmpdec.MPD_Errors
+ # May raise a DecimalException
+ self.context.addstatus(self.space, status)
diff --git a/pypy/module/_decimal/interp_decimal.py
b/pypy/module/_decimal/interp_decimal.py
--- a/pypy/module/_decimal/interp_decimal.py
+++ b/pypy/module/_decimal/interp_decimal.py
@@ -1,6 +1,7 @@
-from rpython.rlib import rmpdec
+from rpython.rlib import rmpdec, rarithmetic, rbigint
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.interpreter.baseobjspace import W_Root
+from pypy.interpreter.error import oefmt, OperationError
from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault
from pypy.interpreter.typedef import (TypeDef, GetSetProperty, descr_get_dict,
descr_set_dict, descr_del_dict)
@@ -12,13 +13,6 @@
# DEC_MINALLOC >= MPD_MINALLOC
DEC_MINALLOC = 4
-def ensure_context(space, w_context):
- context = space.interp_w(interp_context.W_Context, w_context,
- can_be_None=True)
- if context is None:
- context = interp_context.getcontext(space)
- return context
-
class W_Decimal(W_Root):
hash = -1
@@ -39,6 +33,19 @@
if self.data:
lltype.free(self.data, flavor='raw')
+ def descr_str(self, space):
+ context = interp_context.getcontext(space)
+ with lltype.scoped_alloc(rffi.CCHARPP.TO, 1) as cp_ptr:
+ size = rmpdec.mpd_to_sci_size(cp_ptr, self.mpd, context.capitals)
+ if size < 0:
+ raise OperationError(space.w_MemoryError, space.w_None)
+ cp = cp_ptr[0]
+ try:
+ result = rffi.charpsize2str(cp, size)
+ finally:
+ rmpdec.mpd_free(cp)
+ return space.wrap(result) # Convert bytes to unicode
+
def compare(self, space, w_other, op):
if not isinstance(w_other, W_Decimal): # So far
return space.w_NotImplemented
@@ -57,21 +64,18 @@
def decimal_from_ssize(space, w_subtype, value, context, exact=True):
w_result = space.allocate_instance(W_Decimal, w_subtype)
W_Decimal.__init__(w_result, space)
- with lltype.scoped_alloc(rffi.CArrayPtr(rffi.UINT).TO, 1) as status_ptr:
- with interp_context.ConvContext(context, exact) as ctx:
- rmpdec.mpd_qset_ssize(w_result.mpd, value, ctx, status_ptr)
- context.addstatus(space, status_ptr[0])
-
+ with interp_context.ConvContext(
+ space, w_result.mpd, context, exact) as (ctx, status_ptr):
+ rmpdec.mpd_qset_ssize(w_result.mpd, value, ctx, status_ptr)
return w_result
def decimal_from_cstring(space, w_subtype, value, context, exact=True):
w_result = space.allocate_instance(W_Decimal, w_subtype)
W_Decimal.__init__(w_result, space)
- with lltype.scoped_alloc(rffi.CArrayPtr(rffi.UINT).TO, 1) as status_ptr:
- with interp_context.ConvContext(context, exact) as ctx:
- rmpdec.mpd_qset_string(w_result.mpd, value, ctx, status_ptr)
- context.addstatus(space, status_ptr[0])
+ with interp_context.ConvContext(
+ space, w_result.mpd, context, exact) as (ctx, status_ptr):
+ rmpdec.mpd_qset_string(w_result.mpd, value, ctx, status_ptr)
return w_result
def decimal_from_unicode(space, w_subtype, w_value, context, exact=True,
@@ -81,21 +85,58 @@
s = s.strip()
return decimal_from_cstring(space, w_subtype, s, context, exact=exact)
+def decimal_from_long(space, w_subtype, w_value, context, exact=True):
+ w_result = space.allocate_instance(W_Decimal, w_subtype)
+ W_Decimal.__init__(w_result, space)
+
+ value = space.bigint_w(w_value)
+
+ with interp_context.ConvContext(
+ space, w_result.mpd, context, exact) as (ctx, status_ptr):
+ if value.sign == -1:
+ size = value.numdigits()
+ sign = rmpdec.MPD_NEG
+ else:
+ size = value.numdigits()
+ sign = rmpdec.MPD_POS
+ if rbigint.UDIGIT_TYPE.BITS == 32:
+ with lltype.scoped_alloc(rffi.CArrayPtr(rffi.UINT).TO, size) as
digits:
+ for i in range(size):
+ digits[i] = value.udigit(i)
+ rmpdec.mpd_qimport_u32(
+ w_result.mpd, digits, size, sign, PyLong_BASE,
+ ctx, status_ptr)
+ elif rbigint.UDIGIT_TYPE.BITS == 64:
+ # No mpd_qimport_u64, so we convert to a string.
+ return decimal_from_cstring(space, w_subtype, value.str(),
+ context, exact=exact)
+
+ else:
+ raise ValueError("Bad rbigint size")
+ return w_result
+
def decimal_from_object(space, w_subtype, w_value, context, exact=True):
if w_value is None:
return decimal_from_ssize(space, w_subtype, 0, context, exact=exact)
elif space.isinstance_w(w_value, space.w_unicode):
return decimal_from_unicode(space, w_subtype, w_value, context,
- exact=True, strip_whitespace=True)
+ exact=exact, strip_whitespace=exact)
+ elif space.isinstance_w(w_value, space.w_int):
+ return decimal_from_long(space, w_subtype, w_value, context,
+ exact=exact)
+ raise oefmt(space.w_TypeError,
+ "conversion from %N to Decimal is not supported",
+ space.type(w_value))
@unwrap_spec(w_context=WrappedDefault(None))
def descr_new_decimal(space, w_subtype, w_value=None, w_context=None):
- context = ensure_context(space, w_context)
+ context = interp_context.ensure_context(space, w_context)
return decimal_from_object(space, w_subtype, w_value, context,
exact=True)
W_Decimal.typedef = TypeDef(
'Decimal',
__new__ = interp2app(descr_new_decimal),
+ __str__ = interp2app(W_Decimal.descr_str),
__eq__ = interp2app(W_Decimal.descr_eq),
)
diff --git a/pypy/module/_decimal/interp_signals.py
b/pypy/module/_decimal/interp_signals.py
--- a/pypy/module/_decimal/interp_signals.py
+++ b/pypy/module/_decimal/interp_signals.py
@@ -1,9 +1,37 @@
-SIGNAL_NAMES = (
- 'DecimalException', 'Clamped', 'Rounded', 'Inexact',
- 'Subnormal', 'Underflow', 'Overflow', 'DivisionByZero',
- 'InvalidOperation', 'FloatOperation')
+from rpython.rlib import rmpdec
+from rpython.rlib.unroll import unrolling_iterable
+
+SIGNAL_MAP = unrolling_iterable([
+ ('InvalidOperation', rmpdec.MPD_IEEE_Invalid_operation),
+ ('FloatOperation', rmpdec.MPD_Float_operation),
+ ('DivisionByZero', rmpdec.MPD_Division_by_zero),
+ ('Overflow', rmpdec.MPD_Overflow),
+ ('Underflow', rmpdec.MPD_Underflow),
+ ('Subnormal', rmpdec.MPD_Subnormal),
+ ('Inexact', rmpdec.MPD_Inexact),
+ ('Rounded', rmpdec.MPD_Rounded),
+ ('Clamped', rmpdec.MPD_Clamped),
+ ])
+# Exceptions that inherit from InvalidOperation
+COND_MAP = unrolling_iterable([
+ ('InvalidOperation', rmpdec.MPD_Invalid_operation),
+ ('ConversionSyntax', rmpdec.MPD_Conversion_syntax),
+ ('DivisionImpossible', rmpdec.MPD_Division_impossible),
+ ('DivisionUndefined', rmpdec.MPD_Division_undefined),
+ ('InvalidContext', rmpdec.MPD_Invalid_context),
+ ])
def flags_as_exception(space, flags):
+ w_exc = None
+ err_list = []
+ for name, flag in SIGNAL_MAP:
+ if flags & flag:
+ w_exc = getattr(get(space), 'w_' + name)
+ if w_exc is None:
+ raise oefmt(space.w_RuntimeError,
+ "invalid error flag")
+
+
raise ValueError(hex(flags))
diff --git a/pypy/module/_decimal/test/test_decimal.py
b/pypy/module/_decimal/test/test_decimal.py
--- a/pypy/module/_decimal/test/test_decimal.py
+++ b/pypy/module/_decimal/test/test_decimal.py
@@ -1,8 +1,44 @@
class AppTestExplicitConstruction:
spaceconfig = dict(usemodules=('_decimal',))
+ def setup_class(cls):
+ space = cls.space
+ cls.w_decimal = space.call_function(space.builtin.get('__import__'),
+ space.wrap("_decimal"))
+ cls.w_Decimal = space.getattr(cls.w_decimal, space.wrap("Decimal"))
+
def test_explicit_empty(self):
- import _decimal
- Decimal = _decimal.Decimal
+ Decimal = self.Decimal
assert Decimal() == Decimal("0")
+ def test_explicit_from_None(self):
+ Decimal = self.Decimal
+ raises(TypeError, Decimal, None)
+
+ def test_explicit_from_int(self):
+ Decimal = self.decimal.Decimal
+
+ #positive
+ d = Decimal(45)
+ assert str(d) == '45'
+
+ #very large positive
+ d = Decimal(500000123)
+ assert str(d) == '500000123'
+
+ #negative
+ d = Decimal(-45)
+ assert str(d) == '-45'
+
+ #zero
+ d = Decimal(0)
+ assert str(d) == '0'
+
+ # single word longs
+ for n in range(0, 32):
+ for sign in (-1, 1):
+ for x in range(-5, 5):
+ i = sign * (2**n + x)
+ d = Decimal(i)
+ assert str(d) == str(i)
+
diff --git a/rpython/rlib/rmpdec.py b/rpython/rlib/rmpdec.py
--- a/rpython/rlib/rmpdec.py
+++ b/rpython/rlib/rmpdec.py
@@ -40,6 +40,7 @@
"mpd_getprec", "mpd_getemin", "mpd_getemax", "mpd_getround",
"mpd_getclamp",
"mpd_qsetprec", "mpd_qsetemin", "mpd_qsetemax", "mpd_qsetround",
"mpd_qsetclamp",
"mpd_maxcontext",
+ "mpd_to_sci_size",
"mpd_qcmp",
],
compile_extra=compile_extra,
@@ -52,21 +53,34 @@
'ROUND_HALF_UP', 'ROUND_HALF_DOWN', 'ROUND_HALF_EVEN',
'ROUND_05UP', 'ROUND_TRUNC')
+STATUS_FLAGS_CONSTANTS = (
+ 'MPD_Clamped', 'MPD_Conversion_syntax', 'MPD_Division_by_zero',
+ 'MPD_Division_impossible', 'MPD_Division_undefined', 'MPD_Fpu_error',
+ 'MPD_Inexact', 'MPD_Invalid_context', 'MPD_Invalid_operation',
+ 'MPD_Malloc_error', 'MPD_Not_implemented', 'MPD_Overflow',
+ 'MPD_Rounded', 'MPD_Subnormal', 'MPD_Underflow', 'MPD_Max_status',
+ 'MPD_IEEE_Invalid_operation', 'MPD_Errors')
+
class CConfig:
_compilation_info_ = eci
MPD_IEEE_CONTEXT_MAX_BITS = platform.ConstantInteger(
'MPD_IEEE_CONTEXT_MAX_BITS')
MPD_MAX_PREC = platform.ConstantInteger('MPD_MAX_PREC')
+
+ # Flags
+ MPD_POS = platform.ConstantInteger('MPD_POS')
+ MPD_NEG = platform.ConstantInteger('MPD_NEG')
MPD_STATIC = platform.ConstantInteger('MPD_STATIC')
MPD_STATIC_DATA = platform.ConstantInteger('MPD_STATIC_DATA')
- MPD_Malloc_error = platform.ConstantInteger('MPD_Malloc_error')
-
for name in ROUND_CONSTANTS:
name = 'MPD_' + name
locals()[name] = platform.ConstantInteger(name)
+ for name in STATUS_FLAGS_CONSTANTS:
+ locals()[name] = platform.ConstantInteger(name)
+
MPD_T = platform.Struct('mpd_t',
[('flags', rffi.UINT),
('exp', rffi.SSIZE_T),
@@ -83,6 +97,7 @@
globals().update(platform.configure(CConfig))
+MPD_Float_operation = MPD_Not_implemented
def external(name, args, result, **kwds):
return rffi.llexternal(name, args, result, compilation_info=eci, **kwds)
@@ -95,6 +110,9 @@
'mpd_qset_ssize', [MPD_PTR, rffi.SSIZE_T, MPD_CONTEXT_PTR, rffi.UINTP],
lltype.Void)
mpd_qset_string = external(
'mpd_qset_string', [MPD_PTR, rffi.CCHARP, MPD_CONTEXT_PTR, rffi.UINTP],
lltype.Void)
+mpd_qimport_u32 = external(
+ 'mpd_qimport_u32', [MPD_PTR, rffi.UINTP, rffi.SIZE_T,
+ rffi.UCHAR, rffi.UINT, MPD_CONTEXT_PTR, rffi.UINTP],
rffi.SIZE_T)
# Context operations
mpd_getprec = external(
@@ -122,6 +140,13 @@
mpd_maxcontext = external(
'mpd_maxcontext', [MPD_CONTEXT_PTR], lltype.Void)
+mpd_free = external(
+ 'mpd_free', [rffi.VOIDP], lltype.Void, macro=True)
+
+# Conversion
+mpd_to_sci_size = external(
+ 'mpd_to_sci_size', [rffi.CCHARPP, MPD_PTR, rffi.INT], rffi.SSIZE_T)
+
# Operations
mpd_qcmp = external(
'mpd_qcmp', [MPD_PTR, MPD_PTR, rffi.UINTP], rffi.INT)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit