1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
from pypy.interpreter.error import OperationError, oefmt
from pypy.module.cpyext.api import (
cpython_api, CANNOT_FAIL, Py_ssize_t, PyVarObject, PY_SSIZE_T_MAX,
PY_SSIZE_T_MIN)
from pypy.module.cpyext.pyobject import PyObject, PyObjectP, from_ref, make_ref
from rpython.rtyper.lltypesystem import rffi, lltype
from rpython.rlib.rarithmetic import widen
from rpython.tool.sourcetools import func_with_new_name
from pypy.objspace.std import newformat
@cpython_api([PyObject, PyObject], Py_ssize_t, error=-1)
def PyNumber_AsSsize_t(space, w_obj, w_exc):
"""Returns o converted to a Py_ssize_t value if o can be interpreted as an
integer. If o can be converted to a Python int or long but the attempt to
convert to a Py_ssize_t value would raise an OverflowError, then the
exc argument is the type of exception that will be raised (usually
IndexError or OverflowError). If exc is NULL, then the
exception is cleared and the value is clipped to PY_SSIZE_T_MIN for a negative
integer or PY_SSIZE_T_MAX for a positive integer.
"""
try:
return space.int_w(w_obj) #XXX: this is wrong on win64
except OperationError as e:
if e.match(space, space.w_OverflowError):
if not w_exc:
if space.isinstance_w(w_obj, space.w_long):
if w_obj.bigint_w(space).get_sign() < 0:
return PY_SSIZE_T_MIN
else:
return PY_SSIZE_T_MAX
# XXX not sure this is ever reached?
# CPython does _PyLong_Sign(value) < 0 which is equivalent to
# Py_SIZE(value) < 0 which is value->ob_size
pyobj = make_ref(space, w_obj)
if rffi.cast(PyVarObject, pyobj).c_ob_size < 0:
return PY_SSIZE_T_MIN
else:
return PY_SSIZE_T_MAX
else:
raise oefmt(w_exc, "cannot fit '%T' into an index-sized integer", w_obj)
else:
raise
@cpython_api([PyObject], PyObject)
def PyNumber_Long(space, w_obj):
"""Returns the o converted to a long integer object on success, or NULL on
failure. This is the equivalent of the Python expression long(o)."""
return space.call_function(space.w_int, w_obj)
@cpython_api([PyObject], PyObject)
def PyNumber_Index(space, w_obj):
"""Returns the o converted to a Python int or long on success or NULL with a
TypeError exception raised on failure.
"""
return space.index(w_obj)
@cpython_api([PyObject, rffi.INT_real], PyObject)
def PyNumber_ToBase(space, w_obj, base):
"""Returns the integer n converted to base as a string with a base
marker of '0b', '0o', or '0x' if applicable. When
base is not 2, 8, 10, or 16, the format is 'x#num' where x is the
base. If n is not an int object, it is converted with
PyNumber_Index() first.
"""
base = widen(base)
if not (base == 2 or base == 8 or base == 10 or base ==16):
# In Python3.7 this becomes a SystemError. Before that, CPython would
# assert in debug or segfault in release. bpo 38643
raise oefmt(space.w_SystemError,
"PyNumber_ToBase: base must be 2, 8, 10 or 16")
w_index = space.index(w_obj)
# A slight hack to call the internal _*_to_base method, which
# accepts an int base rather than a str spec
formatter = newformat.unicode_formatter(space, '')
try:
value = space.int_w(w_index)
except OperationError as e:
if not e.match(space, space.w_OverflowError):
raise
value = space.bigint_w(w_index)
return space.newtext(formatter._long_to_base(base, value))
return space.newtext(formatter._int_to_base(base, value))
def func_rename(newname):
return lambda func: func_with_new_name(func, newname)
def make_numbermethod(cname, spacemeth):
@cpython_api([PyObject, PyObject], PyObject)
@func_rename(cname)
def PyNumber_Method(space, w_o1, w_o2):
meth = getattr(space, spacemeth)
return meth(w_o1, w_o2)
return PyNumber_Method
def make_unary_numbermethod(name, spacemeth):
@cpython_api([PyObject], PyObject)
@func_rename(cname)
def PyNumber_Method(space, w_o1):
meth = getattr(space, spacemeth)
return meth(w_o1)
return PyNumber_Method
def make_inplace_numbermethod(cname, spacemeth):
spacemeth = 'inplace_' + spacemeth.rstrip('_')
@cpython_api([PyObject, PyObject], PyObject)
@func_rename(cname)
def PyNumber_Method(space, w_o1, w_o2):
meth = getattr(space, spacemeth)
return meth(w_o1, w_o2)
return PyNumber_Method
for name, spacemeth in [
('Add', 'add'),
('Subtract', 'sub'),
('Multiply', 'mul'),
('Divide', 'div'),
('FloorDivide', 'floordiv'),
('TrueDivide', 'truediv'),
('Remainder', 'mod'),
('Lshift', 'lshift'),
('Rshift', 'rshift'),
('And', 'and_'),
('Xor', 'xor'),
('Or', 'or_'),
('Divmod', 'divmod'),
('MatrixMultiply', 'matmul')]:
cname = 'PyNumber_%s' % (name,)
globals()[cname] = make_numbermethod(cname, spacemeth)
if name != 'Divmod':
cname = 'PyNumber_InPlace%s' % (name,)
globals()[cname] = make_inplace_numbermethod(cname, spacemeth)
for name, spacemeth in [
('Negative', 'neg'),
('Positive', 'pos'),
('Absolute', 'abs'),
('Invert', 'invert')]:
cname = 'PyNumber_%s' % (name,)
globals()[cname] = make_unary_numbermethod(cname, spacemeth)
@cpython_api([PyObject, PyObject, PyObject], PyObject)
def PyNumber_Power(space, w_o1, w_o2, w_o3):
return space.pow(w_o1, w_o2, w_o3)
@cpython_api([PyObject, PyObject, PyObject], PyObject)
def PyNumber_InPlacePower(space, w_o1, w_o2, w_o3):
if not space.is_w(w_o3, space.w_None):
raise oefmt(space.w_ValueError,
"PyNumber_InPlacePower with non-None modulus is not "
"supported")
return space.inplace_pow(w_o1, w_o2)
|