Author: Amaury Forgeot d'Arc <[email protected]>
Branch: decimal-libmpdec
Changeset: r71451:563cb51db85c
Date: 2014-05-02 23:59 +0200
http://bitbucket.org/pypy/pypy/changeset/563cb51db85c/
Log: Really start the Decimal type: some constructors.
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
@@ -1,5 +1,7 @@
from pypy.interpreter.mixedmodule import MixedModule
from rpython.rlib import rmpdec
+from pypy.module._decimal import interp_signals
+
class Module(MixedModule):
appleveldefs = {
@@ -16,8 +18,6 @@
}
for name in rmpdec.ROUND_CONSTANTS:
interpleveldefs[name] = 'space.wrap(%r)' % name
- for name in ('DecimalException', 'Clamped', 'Rounded', 'Inexact',
- 'Subnormal', 'Underflow', 'Overflow', 'DivisionByZero',
- 'InvalidOperation', 'FloatOperation'):
+ for name in interp_signals.SIGNAL_NAMES:
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
@@ -7,6 +7,7 @@
from pypy.interpreter.typedef import (
TypeDef, GetSetProperty, interp_attrproperty_w)
from pypy.interpreter.executioncontext import ExecutionContext
+from pypy.module._decimal import interp_signals
# The SignalDict is a MutableMapping that provides access to the
@@ -50,13 +51,23 @@
class W_Context(W_Root):
def __init__(self, space):
- self.ctx = lltype.malloc(rmpdec.MPD_CONTEXT_PTR.TO, 1, flavor='raw',
+ self.ctx = lltype.malloc(rmpdec.MPD_CONTEXT_PTR.TO, flavor='raw',
+ zero=True,
track_allocation=False)
self.w_flags = space.call_function(state_get(space).W_SignalDict)
def __del__(self):
if self.ctx:
- lltype.free(self.ctx, flavor='raw')
+ lltype.free(self.ctx, flavor='raw', track_allocation=False)
+
+ 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:
+ 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)
def copy_w(self, space):
w_copy = W_Context(space)
@@ -148,3 +159,20 @@
def setcontext(space, w_context):
ec = space.getexecutioncontext()
ec.decimal_context = w_context
+
+class ConvContext:
+ def __init__(self, context, exact):
+ self.exact = exact
+ 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
+
+ def __exit__(self, *args):
+ if self.exact:
+ lltype.free(self.ctx, flavor='raw')
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,14 +1,101 @@
from rpython.rlib import rmpdec
+from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.interpreter.baseobjspace import W_Root
+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)
+from pypy.module._decimal import interp_context
IEEE_CONTEXT_MAX_BITS = rmpdec.MPD_IEEE_CONTEXT_MAX_BITS
MAX_PREC = rmpdec.MPD_MAX_PREC
+# 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):
- pass
+ hash = -1
+
+ def __init__(self, space):
+ self.mpd = lltype.malloc(rmpdec.MPD_PTR.TO, flavor='raw')
+ self.data = lltype.malloc(rffi.UINTP.TO, DEC_MINALLOC, flavor='raw')
+ rffi.setintfield(self.mpd, 'c_flags',
+ rmpdec.MPD_STATIC | rmpdec.MPD_STATIC_DATA)
+ self.mpd.c_exp = 0
+ self.mpd.c_digits = 0
+ self.mpd.c_len = 0
+ self.mpd.c_alloc = DEC_MINALLOC
+ self.mpd.c_data = self.data
+
+ def __del__(self):
+ if self.mpd:
+ lltype.free(self.mpd, flavor='raw')
+ if self.data:
+ lltype.free(self.data, flavor='raw')
+
+ def compare(self, space, w_other, op):
+ if not isinstance(w_other, W_Decimal): # So far
+ return space.w_NotImplemented
+ with lltype.scoped_alloc(rffi.CArrayPtr(rffi.UINT).TO, 1) as
status_ptr:
+ r = rmpdec.mpd_qcmp(self.mpd, w_other.mpd, status_ptr)
+ if op == 'eq':
+ return space.wrap(r == 0)
+ else:
+ return space.w_NotImplemented
+
+ def descr_eq(self, space, w_other):
+ return self.compare(space, w_other, 'eq')
+
+
+# Constructors
+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])
+
+ 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])
+ return w_result
+
+def decimal_from_unicode(space, w_subtype, w_value, context, exact=True,
+ strip_whitespace=True):
+ s = space.str_w(w_value) # XXX numeric_as_ascii() is different
+ if strip_whitespace:
+ s = s.strip()
+ return decimal_from_cstring(space, w_subtype, s, context, exact=exact)
+
+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)
+
+@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)
+ return decimal_from_object(space, w_subtype, w_value, context,
+ exact=True)
W_Decimal.typedef = TypeDef(
- 'Decimal')
+ 'Decimal',
+ __new__ = interp2app(descr_new_decimal),
+ __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,3 +1,12 @@
+SIGNAL_NAMES = (
+ 'DecimalException', 'Clamped', 'Rounded', 'Inexact',
+ 'Subnormal', 'Underflow', 'Overflow', 'DivisionByZero',
+ 'InvalidOperation', 'FloatOperation')
+
+def flags_as_exception(space, flags):
+ raise ValueError(hex(flags))
+
+
class SignalState:
def __init__(self, space):
self.w_DecimalException = space.call_function(
diff --git a/pypy/module/_decimal/test/test_decimal.py
b/pypy/module/_decimal/test/test_decimal.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_decimal/test/test_decimal.py
@@ -0,0 +1,8 @@
+class AppTestExplicitConstruction:
+ spaceconfig = dict(usemodules=('_decimal',))
+
+ def test_explicit_empty(self):
+ import _decimal
+ Decimal = _decimal.Decimal
+ assert Decimal() == Decimal("0")
+
diff --git a/rpython/rlib/rmpdec.py b/rpython/rlib/rmpdec.py
--- a/rpython/rlib/rmpdec.py
+++ b/rpython/rlib/rmpdec.py
@@ -2,7 +2,7 @@
import sys
from rpython.translator.tool.cbuild import ExternalCompilationInfo
-from rpython.rtyper.lltypesystem import rffi
+from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.rtyper.tool import rffi_platform as platform
from rpython.conftest import cdir
@@ -25,6 +25,7 @@
libdir.join('convolute.c'),
libdir.join('constants.c'),
libdir.join('context.c'),
+ libdir.join('io.c'),
libdir.join('fourstep.c'),
libdir.join('sixstep.c'),
libdir.join('transpose.c'),
@@ -35,10 +36,11 @@
libdir.join('memory.c'),
],
export_symbols=[
- "mpd_getprec", "mpd_getemin", "mpd_getemax", "mpd_getround",
- "mpd_getclamp",
- "mpd_qsetprec", "mpd_qsetemin", "mpd_qsetemax", "mpd_qsetround",
- "mpd_qsetclamp",
+ "mpd_qset_ssize", "mpd_qset_string",
+ "mpd_getprec", "mpd_getemin", "mpd_getemax", "mpd_getround",
"mpd_getclamp",
+ "mpd_qsetprec", "mpd_qsetemin", "mpd_qsetemax", "mpd_qsetround",
"mpd_qsetclamp",
+ "mpd_maxcontext",
+ "mpd_qcmp",
],
compile_extra=compile_extra,
libraries=['m'],
@@ -56,13 +58,28 @@
MPD_IEEE_CONTEXT_MAX_BITS = platform.ConstantInteger(
'MPD_IEEE_CONTEXT_MAX_BITS')
MPD_MAX_PREC = platform.ConstantInteger('MPD_MAX_PREC')
+ 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)
+ MPD_T = platform.Struct('mpd_t',
+ [('flags', rffi.UINT),
+ ('exp', rffi.SSIZE_T),
+ ('digits', rffi.SSIZE_T),
+ ('len', rffi.SSIZE_T),
+ ('alloc', rffi.SSIZE_T),
+ ('data', rffi.UINTP),
+ ])
MPD_CONTEXT_T = platform.Struct('mpd_context_t',
- [])
+ [('traps', rffi.UINT),
+ ('status', rffi.UINT),
+ ])
+
globals().update(platform.configure(CConfig))
@@ -70,8 +87,16 @@
def external(name, args, result, **kwds):
return rffi.llexternal(name, args, result, compilation_info=eci, **kwds)
-MPD_CONTEXT_PTR = rffi.CArrayPtr(MPD_CONTEXT_T)
+MPD_PTR = lltype.Ptr(MPD_T)
+MPD_CONTEXT_PTR = lltype.Ptr(MPD_CONTEXT_T)
+# Initialization
+mpd_qset_ssize = external(
+ '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)
+
+# Context operations
mpd_getprec = external(
'mpd_getprec', [MPD_CONTEXT_PTR], rffi.SSIZE_T)
mpd_getemin = external(
@@ -94,3 +119,9 @@
mpd_qsetclamp = external(
'mpd_qsetclamp', [MPD_CONTEXT_PTR, rffi.INT], rffi.INT)
+mpd_maxcontext = external(
+ 'mpd_maxcontext', [MPD_CONTEXT_PTR], lltype.Void)
+
+# 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