aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatti Picus <matti.picus@gmail.com>2023-09-27 15:46:49 +0300
committerMatti Picus <matti.picus@gmail.com>2023-09-27 15:46:49 +0300
commitd74e54767b5aff6f158b14815a84fd03d39ceb2f (patch)
tree7d99a505520640b63a9967a0b072c7ce344c2229
parentmerge py3.9 (diff)
parentmerge default (diff)
downloadpypy-d74e54767b5aff6f158b14815a84fd03d39ceb2f.tar.gz
pypy-d74e54767b5aff6f158b14815a84fd03d39ceb2f.tar.bz2
pypy-d74e54767b5aff6f158b14815a84fd03d39ceb2f.zip
merge py3.9, ignoring stdlib updates
-rw-r--r--lib-python/3/ssl.py35
-rw-r--r--lib-python/3/tarfile.py11
-rw-r--r--lib_pypy/_cffi_ssl/_stdssl/__init__.py24
-rw-r--r--lib_pypy/_cffi_ssl/_stdssl/certificate.py2
-rw-r--r--lib_pypy/_testcapimodule.c39
-rw-r--r--pypy/doc/conf.py2
-rw-r--r--pypy/doc/release-v7.3.13.rst178
-rw-r--r--pypy/module/_cffi_backend/cdlopen.py2
-rw-r--r--pypy/module/cpyext/include/patchlevel.h4
-rw-r--r--pypy/module/cpyext/longobject.py28
-rw-r--r--pypy/module/cpyext/number.py2
-rw-r--r--pypy/module/cpyext/test/test_longobject.py39
-rw-r--r--pypy/module/pypyjit/test_pypy_c/test_misc.py3
-rw-r--r--pypy/module/pypyjit/test_pypy_c/test_string.py6
-rw-r--r--pypy/module/sys/version.py2
-rw-r--r--pypy/objspace/std/longobject.py14
-rw-r--r--pypy/objspace/std/marshal_impl.py2
-rw-r--r--rpython/doc/conf.py2
-rw-r--r--rpython/rlib/rbigint.py343
-rw-r--r--rpython/rlib/test/test_rbigint.py5
20 files changed, 553 insertions, 190 deletions
diff --git a/lib-python/3/ssl.py b/lib-python/3/ssl.py
index c115fbb2aa..5cea032a0d 100644
--- a/lib-python/3/ssl.py
+++ b/lib-python/3/ssl.py
@@ -1033,7 +1033,7 @@ class SSLSocket(socket):
)
self = cls.__new__(cls, **kwargs)
super(SSLSocket, self).__init__(**kwargs)
- self.settimeout(sock.gettimeout())
+ sock_timeout = sock.gettimeout()
sock.detach()
self._context = context
@@ -1052,9 +1052,42 @@ class SSLSocket(socket):
if e.errno != errno.ENOTCONN:
raise
connected = False
+ blocking = self.getblocking()
+ self.setblocking(False)
+ try:
+ # We are not connected so this is not supposed to block, but
+ # testing revealed otherwise on macOS and Windows so we do
+ # the non-blocking dance regardless. Our raise when any data
+ # is found means consuming the data is harmless.
+ notconn_pre_handshake_data = self.recv(1)
+ except OSError as e:
+ # EINVAL occurs for recv(1) on non-connected on unix sockets.
+ if e.errno not in (errno.ENOTCONN, errno.EINVAL):
+ raise
+ notconn_pre_handshake_data = b''
+ self.setblocking(blocking)
+ if notconn_pre_handshake_data:
+ # This prevents pending data sent to the socket before it was
+ # closed from escaping to the caller who could otherwise
+ # presume it came through a successful TLS connection.
+ reason = "Closed before TLS handshake with data in recv buffer."
+ notconn_pre_handshake_data_error = SSLError(e.errno, reason)
+ # Add the SSLError attributes that _ssl.c always adds.
+ notconn_pre_handshake_data_error.reason = reason
+ notconn_pre_handshake_data_error.library = None
+ try:
+ self.close()
+ except OSError:
+ pass
+ try:
+ raise notconn_pre_handshake_data_error
+ finally:
+ # Explicitly break the reference cycle.
+ notconn_pre_handshake_data_error = None
else:
connected = True
+ self.settimeout(sock_timeout) # Must come after setblocking() calls.
self._connected = connected
if connected:
# create the SSL object
diff --git a/lib-python/3/tarfile.py b/lib-python/3/tarfile.py
index 40599f27bc..495349f08f 100644
--- a/lib-python/3/tarfile.py
+++ b/lib-python/3/tarfile.py
@@ -741,7 +741,7 @@ class SpecialFileError(FilterError):
class AbsoluteLinkError(FilterError):
def __init__(self, tarinfo):
self.tarinfo = tarinfo
- super().__init__(f'{tarinfo.name!r} is a symlink to an absolute path')
+ super().__init__(f'{tarinfo.name!r} is a link to an absolute path')
class LinkOutsideDestinationError(FilterError):
def __init__(self, tarinfo, path):
@@ -801,7 +801,14 @@ def _get_filtered_attrs(member, dest_path, for_data=True):
if member.islnk() or member.issym():
if os.path.isabs(member.linkname):
raise AbsoluteLinkError(member)
- target_path = os.path.realpath(os.path.join(dest_path, member.linkname))
+ if member.issym():
+ target_path = os.path.join(dest_path,
+ os.path.dirname(name),
+ member.linkname)
+ else:
+ target_path = os.path.join(dest_path,
+ member.linkname)
+ target_path = os.path.realpath(target_path)
if os.path.commonpath([target_path, dest_path]) != dest_path:
raise LinkOutsideDestinationError(member, target_path)
return new_attrs
diff --git a/lib_pypy/_cffi_ssl/_stdssl/__init__.py b/lib_pypy/_cffi_ssl/_stdssl/__init__.py
index 9e0c472318..4c579c9e2a 100644
--- a/lib_pypy/_cffi_ssl/_stdssl/__init__.py
+++ b/lib_pypy/_cffi_ssl/_stdssl/__init__.py
@@ -1,5 +1,6 @@
-import sys
import os
+import pathlib
+import sys
import time
import _thread
import warnings
@@ -1007,11 +1008,15 @@ class _SSLSocket(object):
def _fs_decode(name):
return name.decode(sys.getfilesystemencoding())
-def _fs_converter(name):
+def _fs_converter(name, argname):
""" name must not be None """
+ if isinstance(name, pathlib.Path):
+ name = str(name)
if isinstance(name, str):
return name.encode(sys.getfilesystemencoding())
- return bytes(name)
+ elif isinstance(name, bytes):
+ return bytes(name)
+ raise TypeError(f"{argname} should be a valid filesystem path")
def cipher_to_tuple(cipher):
@@ -1461,7 +1466,7 @@ class _SSLContext(object):
prev_errno = ffi.errno
try:
ffi.errno = 0
- certfilebuf = _str_to_ffi_buffer(certfile)
+ certfilebuf = _str_to_ffi_buffer(_fs_converter(certfile, "certfile"))
ret = lib.SSL_CTX_use_certificate_chain_file(self.ctx, certfilebuf)
if ret != 1:
if pw_info.operationerror:
@@ -1475,7 +1480,7 @@ class _SSLContext(object):
raise ssl_error(None)
ffi.errno = 0
- buf = _str_to_ffi_buffer(keyfile)
+ buf = _str_to_ffi_buffer(_fs_converter(keyfile, "keyfile"))
ret = lib.SSL_CTX_use_PrivateKey_file(self.ctx, buf,
lib.SSL_FILETYPE_PEM)
if ret != 1:
@@ -1531,14 +1536,15 @@ class _SSLContext(object):
# load cafile or capath
if cafile is not None or capath is not None:
+ print(cafile, capath)
if cafile is None:
cafilebuf = ffi.NULL
else:
- cafilebuf = _str_to_ffi_buffer(cafile)
+ cafilebuf = _str_to_ffi_buffer(_fs_converter(cafile, "cafile"))
if capath is None:
capathbuf = ffi.NULL
else:
- capathbuf = _str_to_ffi_buffer(capath)
+ capathbuf = _str_to_ffi_buffer(_fs_converter(capath, "capath"))
ret = lib.SSL_CTX_load_verify_locations(self.ctx, cafilebuf, capathbuf)
if ret != 1:
_errno = ffi.errno
@@ -1705,7 +1711,7 @@ class _SSLContext(object):
ffi.errno = 0
if filepath is None:
raise TypeError("filepath must not be None")
- buf = _fs_converter(filepath)
+ buf = _fs_converter(filepath, "filepath")
mode = ffi.new("char[]",b"rb")
ffi.errno = 0
bio = lib.BIO_new_file(buf, mode)
@@ -1758,7 +1764,7 @@ class _SSLContext(object):
# needs to be zero terminated
if name is None:
raise TypeError()
- buf = _fs_converter(name)
+ buf = _fs_converter(name, "name")
nid = lib.OBJ_sn2nid(buf)
if nid == 0:
raise ValueError("unknown elliptic curve name '%s'" % name)
diff --git a/lib_pypy/_cffi_ssl/_stdssl/certificate.py b/lib_pypy/_cffi_ssl/_stdssl/certificate.py
index cba387a63c..283d9cbe05 100644
--- a/lib_pypy/_cffi_ssl/_stdssl/certificate.py
+++ b/lib_pypy/_cffi_ssl/_stdssl/certificate.py
@@ -399,7 +399,7 @@ def _test_decode_cert(path):
lib.BIO_free(cert)
raise ssl_error("Can't malloc memory to read file")
try:
- epath = path.encode()
+ epath = str(path).encode()
if lib.BIO_read_filename(cert, epath) <= 0:
raise ssl_error("Can't open file")
diff --git a/lib_pypy/_testcapimodule.c b/lib_pypy/_testcapimodule.c
index b2274f8b1a..6bffdf6dac 100644
--- a/lib_pypy/_testcapimodule.c
+++ b/lib_pypy/_testcapimodule.c
@@ -2116,6 +2116,42 @@ unicode_asutf8andsize(PyObject *self, PyObject *args)
return Py_BuildValue("(Nn)", result, utf8_len);
}
+/* Test PyUnicode_DecodeUTF8() */
+static PyObject *
+unicode_decodeutf8(PyObject *self, PyObject *args)
+{
+ const char *data;
+ Py_ssize_t size;
+ const char *errors = NULL;
+
+ if (!PyArg_ParseTuple(args, "y#|z", &data, &size, &errors))
+ return NULL;
+
+ return PyUnicode_DecodeUTF8(data, size, errors);
+}
+
+#ifndef PYPY_VERSION
+/* Test PyUnicode_DecodeUTF8Stateful() */
+static PyObject *
+unicode_decodeutf8stateful(PyObject *self, PyObject *args)
+{
+ const char *data;
+ Py_ssize_t size;
+ const char *errors = NULL;
+ Py_ssize_t consumed = 123456789;
+ PyObject *result;
+
+ if (!PyArg_ParseTuple(args, "y#|z", &data, &size, &errors))
+ return NULL;
+
+ result = PyUnicode_DecodeUTF8Stateful(data, size, errors, &consumed);
+ if (!result) {
+ return NULL;
+ }
+ return Py_BuildValue("(Nn)", result, consumed);
+}
+#endif
+
static PyObject *
unicode_findchar(PyObject *self, PyObject *args)
{
@@ -5905,8 +5941,9 @@ static PyMethodDef TestMethods[] = {
{"unicode_asucs4", unicode_asucs4, METH_VARARGS},
{"unicode_asutf8", unicode_asutf8, METH_VARARGS},
{"unicode_asutf8andsize", unicode_asutf8andsize, METH_VARARGS},
- {"unicode_findchar", unicode_findchar, METH_VARARGS},
+ {"unicode_decodeutf8", unicode_decodeutf8, METH_VARARGS},
#ifndef PYPY_VERSION
+ {"unicode_decodeutf8stateful",unicode_decodeutf8stateful, METH_VARARGS}, {"unicode_findchar", unicode_findchar, METH_VARARGS},
{"unicode_copycharacters", unicode_copycharacters, METH_VARARGS},
#endif
#if USE_UNICODE_WCHAR_CACHE
diff --git a/pypy/doc/conf.py b/pypy/doc/conf.py
index 55b562c8ff..bfe2e309ed 100644
--- a/pypy/doc/conf.py
+++ b/pypy/doc/conf.py
@@ -89,7 +89,7 @@ copyright = u'2023, The PyPy Project'
# The short X.Y version.
version = '7.3'
# The full version, including alpha/beta/rc tags.
-release = '7.3.13alpha'
+release = '7.3.14'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/pypy/doc/release-v7.3.13.rst b/pypy/doc/release-v7.3.13.rst
new file mode 100644
index 0000000000..e97213eda6
--- /dev/null
+++ b/pypy/doc/release-v7.3.13.rst
@@ -0,0 +1,178 @@
+==================================================
+PyPy v7.3.13: release of python 2.7, 3.9, and 3.10
+==================================================
+
+
+The PyPy team is proud to release version 7.3.13 of PyPy.
+This is primarily a bug-fix release. CPython released security patches, and
+this release also improves the ability to use multi-phase module initialization
+from the C-API.
+
+The release includes three different interpreters:
+
+ - PyPy2.7, which is an interpreter supporting the syntax and the features of
+ Python 2.7 including the stdlib for CPython 2.7.18+ (the ``+`` is for
+ backported security updates)
+
+ - PyPy3.9, which is an interpreter supporting the syntax and the features of
+ Python 3.9, including the stdlib for CPython 3.9.18.
+
+ - PyPy3.10, which is an interpreter supporting the syntax and the features of
+ Python 3.10, including the stdlib for CPython 3.10.13. Note it requires at
+ least cython 0.29.35 or cython 3.0.0b3
+
+The interpreters are based on much the same codebase, thus the multiple
+release. This is a micro release, all APIs are compatible with the other 7.3
+releases. It follows after 7.3.12 release on June 16, 2023.
+
+We recommend updating. You can find links to download the v7.3.13 releases here:
+
+ https://pypy.org/download.html
+
+We would like to thank our donors for the continued support of the PyPy
+project. If PyPy is not quite good enough for your needs, we are available for
+`direct consulting`_ work. If PyPy is helping you out, we would love to hear about
+it and encourage submissions to our blog_ via a pull request
+to https://github.com/pypy/pypy.org
+
+We would also like to thank our contributors and encourage new people to join
+the project. PyPy has many layers and we need help with all of them: bug fixes,
+`PyPy`_ and `RPython`_ documentation improvements, or general `help`_ with making
+RPython's JIT even better.
+
+If you are a python library maintainer and use C-extensions, please consider
+making a HPy_ / CFFI_ / cppyy_ version of your library that would be performant
+on PyPy. In any case, both `cibuildwheel`_ and the `multibuild system`_ support
+building wheels for PyPy.
+
+.. _`PyPy`: index.html
+.. _`RPython`: https://rpython.readthedocs.org
+.. _`help`: project-ideas.html
+.. _CFFI: https://cffi.readthedocs.io
+.. _cppyy: https://cppyy.readthedocs.io
+.. _`multibuild system`: https://github.com/matthew-brett/multibuild
+.. _`cibuildwheel`: https://github.com/joerick/cibuildwheel
+.. _blog: https://pypy.org/blog
+.. _HPy: https://hpyproject.org/
+.. _was sponsored: https://www.pypy.org/posts/2022/07/m1-support-for-pypy.html
+.. _direct consulting: https://www.pypy.org/pypy-sponsors.html
+.. _has built: https://www.pypy.org/posts/2022/11/pypy-and-conda-forge.html
+
+What is PyPy?
+=============
+
+PyPy is a Python interpreter, a drop-in replacement for CPython
+It's fast (`PyPy and CPython 3.7.4`_ performance
+comparison) due to its integrated tracing JIT compiler.
+
+We also welcome developers of other `dynamic languages`_ to see what RPython
+can do for them.
+
+We provide binary builds for:
+
+ * **x86** machines on most common operating systems
+ (Linux 32/64 bits, Mac OS 64 bits, Windows 64 bits)
+
+ * 64-bit **ARM** machines running Linux (``aarch64``).
+
+ * Apple **M1 arm64** machines (``macos_arm64``).
+
+ * **s390x** running Linux
+
+PyPy support Windows 32-bit, Linux PPC64 big- and little-endian, and Linux ARM
+32 bit, but does not release binaries. Please reach out to us if you wish to
+sponsor binary releases for those platforms. Downstream packagers provide
+binary builds for debian, Fedora, conda, OpenBSD, FreeBSD, Gentoo, and more.
+
+.. _`PyPy and CPython 3.7.4`: https://speed.pypy.org
+.. _`dynamic languages`: https://rpython.readthedocs.io/en/latest/examples.html
+
+Changelog
+=========
+
+For all versions
+----------------
+- Update to ssl 1.1.1w, or 3.0.10 when embedding
+ libraries for a portable build
+
+Bugfixes
+~~~~~~~~
+- Report exceptions that are created when forcing an oefmt as unraisable (issue
+ _3978)
+
+Speedups and enhancements
+~~~~~~~~~~~~~~~~~~~~~~~~~
+- Do type-erasure for rpython dicts and lists more generally, unifying all gced
+ pointer types into one implementation (as opposed to just unifying all
+ *instances* together, but not eg strings). This makes pypy-c ~700KiB smaller.
+- Stop allocating three lists of length 256 for the registers in the MIFrames
+ and the blackhole frames. Almost all jitcodes have much smaller frame sizes.
+ Seems to make tracing a little bit faster.
+- Improve ``strip()`` to not force the object
+- Make sure that ``sys.flags`` are not in a cell in the sys module to speed up
+ access (especially important on py3.x, where every bytes.decode call checks
+ sys.flags.utf8_mode)
+- Speed up the flowspace a lot for huge functions
+- Make ``UnicodeIO`` store its data in a list of ``r_int32``, as opposed to
+ using the rpython (Python2.7) unicode type. we want to get rid of the unicode
+ type and also it requires an extra copy all the time.
+- Make every ``rbigint`` one word smaller by storing the sign in the size field
+
+Python 3.9+
+-----------
+- Create c-extension modules used in tests as part of the build (in
+ package.py), not as part of testing
+
+Bugfixes
+~~~~~~~~
+- More selectively clear ``StopIteration`` exceptions on ``tp_iternext`` (issue
+ _3956)
+- Copy less when creating a venv by using a ``PYPY_PORTABLE_DEPS.txt`` file to
+ state which dlls to copy in a portable build (issue _3611)
+- On macos ``sendifle`` can return an error while sending part of the file
+ (issue _3964)
+- Fixes on both app-level and C level for ``Py_TPFLAGS_BASETYPE=0`` (issue
+ _2742). Also set ``PyType_Type.tp_itemsize`` to ``sizeof(PyMemberDef)`` like
+ on CPython
+- Fix ``PyType_FromSpecWithBases`` to correctly use ``Py_tp_doc``,
+ ``Py_tp_members`` in spec, fix ``__module__`` assignment, better handle
+ ``__name__`` and ``tp_name``
+- Hide ``functools`` wrappers from the stack when reporting warnings (issue
+ _3988)
+- Fix edge case of datetime isoformat parsing (issue _3989)
+- Accept NULL ``tp_doc`` (_bpo-41832)
+- Align ``nb_int`` with ``PyNumber_Long`` (to get consistent error messages)
+- Handle ``pathlib.Path`` objects in ``_ssl`` (issue _4002)
+- Implement ``_PyLong_AsInt`` which is not part of the stable API but used in
+ testing
+
+Speedups and enhancements
+~~~~~~~~~~~~~~~~~~~~~~~~~
+- Avoid compiling a new regex where not needed (in email, csv, and
+ elsewhere) (issue _3961)
+
+Python 3.10
+-----------
+
+Bugfixes
+~~~~~~~~
+- Fix, test locking in HMAC update (issue _3962)
+- When re-assigning to ``type.__bases__``, rebuild the cpyext type struct
+ (issue _3976)
+- Add missing slot macro ``Py_am_send`` (issue _3990)
+
+.. _bpo-41832: https://bugs.python.org/issue41832
+.. _GH-100242: https://github.com/python/cpython/issues/100242
+.. _2742: https://foss.heptapod.net/pypy/pypy/-/issues/2742
+.. _3611: https://foss.heptapod.net/pypy/pypy/-/issues/3611
+.. _3956: https://foss.heptapod.net/pypy/pypy/-/issues/3956
+.. _3961: https://foss.heptapod.net/pypy/pypy/-/issues/3961
+.. _3962: https://foss.heptapod.net/pypy/pypy/-/issues/3962
+.. _3964: https://foss.heptapod.net/pypy/pypy/-/issues/3964
+.. _3976: https://foss.heptapod.net/pypy/pypy/-/issues/3976
+.. _3978: https://foss.heptapod.net/pypy/pypy/-/issues/3978
+.. _3988: https://foss.heptapod.net/pypy/pypy/-/issues/3988
+.. _3989: https://foss.heptapod.net/pypy/pypy/-/issues/3989
+.. _3990: https://foss.heptapod.net/pypy/pypy/-/issues/3990
+.. _4002: https://foss.heptapod.net/pypy/pypy/-/issues/4002
+
diff --git a/pypy/module/_cffi_backend/cdlopen.py b/pypy/module/_cffi_backend/cdlopen.py
index e204c0fc21..61d69fe9ff 100644
--- a/pypy/module/_cffi_backend/cdlopen.py
+++ b/pypy/module/_cffi_backend/cdlopen.py
@@ -157,7 +157,7 @@ def ffiobj_init(ffi, module_name, version, types, w_globals,
ll_set_cdl_realize_global_int(nglobs[i])
bigint = space.bigint_w(w_integer)
ullvalue = bigint.ulonglongmask()
- rffi.setintfield(nintconsts[i], 'neg', int(bigint.sign <= 0))
+ rffi.setintfield(nintconsts[i], 'neg', int(bigint.get_sign() <= 0))
rffi.setintfield(nintconsts[i], 'value', ullvalue)
ffi.ctxobj.ctx.c_globals = nglobs
rffi.setintfield(ffi.ctxobj.ctx, 'c_num_globals', n)
diff --git a/pypy/module/cpyext/include/patchlevel.h b/pypy/module/cpyext/include/patchlevel.h
index 10788e65fb..00698e1c00 100644
--- a/pypy/module/cpyext/include/patchlevel.h
+++ b/pypy/module/cpyext/include/patchlevel.h
@@ -32,8 +32,8 @@
* module/sys/version.py
* doc/conf.py
*/
-#define PYPY_VERSION "7.3.13-alpha0"
-#define PYPY_VERSION_NUM 0x07030d00
+#define PYPY_VERSION "7.3.14-alpha0"
+#define PYPY_VERSION_NUM 0x07030e00
/* Defined to mean a PyPy where cpyext holds more regular references
to PyObjects, e.g. staying alive as long as the internal PyPy object
stays alive. */
diff --git a/pypy/module/cpyext/longobject.py b/pypy/module/cpyext/longobject.py
index fd61909288..6ac86657d5 100644
--- a/pypy/module/cpyext/longobject.py
+++ b/pypy/module/cpyext/longobject.py
@@ -52,6 +52,8 @@ ULONG_MASK = (2 ** (8 * rffi.sizeof(rffi.ULONG)) -1)
ULONG_MAX = (2 ** (8 * rffi.sizeof(rffi.ULONG)) -1)
LONG_MAX = (2 ** (8 * rffi.sizeof(rffi.ULONG) - 1) -1)
LONG_MIN = (-2 ** (8 * rffi.sizeof(rffi.ULONG) - 1))
+INT_MAX = (2 ** (8 * rffi.sizeof(rffi.UINT) - 1) -1)
+INT_MIN = (-2 ** (8 * rffi.sizeof(rffi.UINT) - 1))
need_to_check = maxint > ULONG_MAX
@cpython_api([PyObject], rffi.ULONG, error=-1)
@@ -118,6 +120,30 @@ def PyLong_AsLong(space, w_long):
"Python int too large to convert to C long")
return rffi.cast(rffi.LONG, val)
+@cpython_api([PyObject], rffi.INT, error=-1)
+def _PyLong_AsInt(space, w_long):
+ """
+ Get a C int from an int object or any object that has an __int__
+ method. Return -1 and set an error if overflow occurs.
+ """
+ try:
+ if space.lookup(w_long, '__index__'):
+ val = space.int_w(space.index(w_long))
+ else:
+ val = space.int_w(space.int(w_long))
+ except OperationError as e:
+ if e.match(space, space.w_ValueError):
+ e.w_type = space.w_OverflowError
+ if (e.match(space, space.w_OverflowError) and
+ space.isinstance_w(w_long, space.w_int)):
+ raise oefmt(space.w_OverflowError,
+ "Python int too large to convert to C int")
+ raise e
+ if val > INT_MAX or val < INT_MIN:
+ raise oefmt(space.w_OverflowError,
+ "Python int too large to convert to C int")
+ return rffi.cast(rffi.INT, val)
+
@cpython_api([PyObject], Py_ssize_t, error=-1)
def PyLong_AsSsize_t(space, w_long):
"""Return a C Py_ssize_t representation of the contents of pylong. If
@@ -295,7 +321,7 @@ def _PyLong_NumBits(space, w_long):
@cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
def _PyLong_Sign(space, w_long):
bigint = space.bigint_w(w_long)
- return bigint.sign
+ return bigint.get_sign()
CONST_UCHARP = lltype.Ptr(lltype.Array(rffi.UCHAR, hints={'nolength': True,
'render_as_const': True}))
diff --git a/pypy/module/cpyext/number.py b/pypy/module/cpyext/number.py
index 0c2b2d7b1b..0f8de1964f 100644
--- a/pypy/module/cpyext/number.py
+++ b/pypy/module/cpyext/number.py
@@ -24,7 +24,7 @@ def PyNumber_AsSsize_t(space, w_obj, w_exc):
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).sign < 0:
+ if w_obj.bigint_w(space).get_sign() < 0:
return PY_SSIZE_T_MIN
else:
return PY_SSIZE_T_MAX
diff --git a/pypy/module/cpyext/test/test_longobject.py b/pypy/module/cpyext/test/test_longobject.py
index 34a4254885..65e0c0f27c 100644
--- a/pypy/module/cpyext/test/test_longobject.py
+++ b/pypy/module/cpyext/test/test_longobject.py
@@ -352,6 +352,14 @@ class AppTestLongObject(AppTestCpythonExtensionBase):
}
return PyLong_FromLong(n);
"""),
+ ("as_int", "METH_O",
+ """
+ int n = _PyLong_AsInt(args);
+ if (n == -1 && PyErr_Occurred()) {
+ return NULL;
+ }
+ return PyLong_FromLong(n);
+ """),
("long_max", "METH_NOARGS",
"""
return PyLong_FromLong(LONG_MAX);
@@ -362,6 +370,16 @@ class AppTestLongObject(AppTestCpythonExtensionBase):
return PyLong_FromLong(LONG_MIN);
"""
),
+ ("int_max", "METH_NOARGS",
+ """
+ return PyLong_FromLong(INT_MAX);
+ """
+ ),
+ ("int_min", "METH_NOARGS",
+ """
+ return PyLong_FromLong(INT_MIN);
+ """
+ ),
])
assert module.as_long(123) == 123
assert module.as_long(-1) == -1
@@ -384,6 +402,27 @@ class AppTestLongObject(AppTestCpythonExtensionBase):
# new for python3.8: first try __index__
assert module.as_long(a) == 42
+ assert module.as_int(123) == 123
+ assert module.as_int(-1) == -1
+ assert module.as_int(1.23) == 1
+ INT_MAX = module.int_max()
+ INT_MIN = module.int_min()
+ assert module.as_int(INT_MAX) == INT_MAX
+ raises(OverflowError, module.as_int, INT_MAX+ 1)
+ assert module.as_int(INT_MIN) == INT_MIN
+ raises(OverflowError, module.as_int, INT_MIN - 1)
+ class A:
+ def __index__(self):
+ return 42
+
+ def __int__(self):
+ return 21
+
+ a = A()
+ assert int(a) == 21
+ # new for python3.8: first try __index__
+ assert module.as_int(a) == 42
+
def test_strtol(self):
module = self.import_extension('foo', [
("from_str", "METH_NOARGS",
diff --git a/pypy/module/pypyjit/test_pypy_c/test_misc.py b/pypy/module/pypyjit/test_pypy_c/test_misc.py
index 8c05d56b7c..08e2d6e64b 100644
--- a/pypy/module/pypyjit/test_pypy_c/test_misc.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_misc.py
@@ -432,8 +432,7 @@ class TestMisc(BaseTestPyPyC):
log = self.run(main, [3000])
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
- i79 = int_lt(i76, 0)
- guard_false(i79, descr=...)
+ ...
i80 = int_ge(i76, i33)
guard_false(i80, descr=...)
i82 = int_add(i76, 1)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py
index ef454893d4..089970fe61 100644
--- a/pypy/module/pypyjit/test_pypy_c/test_string.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_string.py
@@ -408,7 +408,7 @@ class TestString(BaseTestPyPyC):
def test_strip_doesnt_escape_bytes(self):
log = self.run("""
def main(n):
- l = [unicode(x).decode("ascii") for x in range(10000)]
+ l = [str(x).decode("ascii") for x in range(10000)]
res = 0
for data in l:
res += len(data.strip(b'1')) # ID: striparg
@@ -430,7 +430,7 @@ class TestString(BaseTestPyPyC):
log = self.run("""
def main(n):
uni = b'\xc3\xa4'.decode("utf-8")
- l = [unicode(x) + uni + unicode(x) for x in range(10000)]
+ l = [str(x) + uni + str(x) for x in range(10000)]
res = 0
for data in l:
res += len(data.strip(u'1')) # ID: stripnone
@@ -451,7 +451,7 @@ class TestString(BaseTestPyPyC):
def test_unicode_strip_doesnt_escape_uniobject_ascii(self):
log = self.run("""
def main(n):
- l = [unicode(x) for x in range(10000)]
+ l = [str(x) for x in range(10000)]
res = 0
for data in l:
res += len(data.strip(u'1')) # ID: stripnone
diff --git a/pypy/module/sys/version.py b/pypy/module/sys/version.py
index ea0f014249..8a3877309e 100644
--- a/pypy/module/sys/version.py
+++ b/pypy/module/sys/version.py
@@ -15,7 +15,7 @@ CPYTHON_API_VERSION = 1013 #XXX # sync with include/modsupport.h
# make sure to keep PYPY_VERSION in sync with:
# module/cpyext/include/patchlevel.h
# doc/conf.py
-PYPY_VERSION = (7, 3, 13, "alpha", 0)
+PYPY_VERSION = (7, 3, 14, "alpha", 0)
import pypy
diff --git a/pypy/objspace/std/longobject.py b/pypy/objspace/std/longobject.py
index 43b2643188..fe3bc0d8fe 100644
--- a/pypy/objspace/std/longobject.py
+++ b/pypy/objspace/std/longobject.py
@@ -212,7 +212,7 @@ class W_LongObject(W_AbstractLongObject):
exponent = w_exponent.asbigint()
if space.is_none(w_modulus):
- if exponent.sign < 0:
+ if exponent.get_sign() < 0:
self = self.descr_float(space)
w_exponent = w_exponent.descr_float(space)
return space.pow(self, w_exponent, space.w_None)
@@ -223,7 +223,7 @@ class W_LongObject(W_AbstractLongObject):
return space.w_NotImplemented
base = self.num
- if exponent.sign < 0:
+ if exponent.get_sign() < 0:
w_base = invmod(space, self, space.abs(w_modulus))
if isinstance(w_base, W_IntObject):
w_base = w_base.as_w_long(space)
@@ -345,12 +345,12 @@ class W_LongObject(W_AbstractLongObject):
return descr_binop, descr_rbinop
def _lshift(self, space, w_other):
- if w_other.asbigint().sign < 0:
+ if w_other.asbigint().get_sign() < 0:
raise oefmt(space.w_ValueError, "negative shift count")
try:
shift = w_other.asbigint().toint()
except OverflowError: # b too big
- if self.num.sign == 0:
+ if self.num.get_sign() == 0:
return self
raise oefmt(space.w_OverflowError, "shift count too large")
return W_LongObject(self.num.lshift(shift))
@@ -363,12 +363,12 @@ class W_LongObject(W_AbstractLongObject):
descr_lshift, descr_rlshift = _make_descr_binop(_lshift, _int_lshift)
def _rshift(self, space, w_other):
- if w_other.asbigint().sign < 0:
+ if w_other.asbigint().get_sign() < 0:
raise oefmt(space.w_ValueError, "negative shift count")
try:
shift = w_other.asbigint().toint()
except OverflowError:
- if self.num.sign < 0:
+ if self.num.get_sign() < 0:
return space.newint(-1)
return space.newint(0)
raise oefmt(space.w_OverflowError, "shift count too large")
@@ -463,7 +463,7 @@ def _hash_long(v):
if x >= HASH_MODULUS:
x -= HASH_MODULUS
i -= 1
- h = intmask(intmask(x) * v.sign)
+ h = intmask(intmask(x) * v.get_sign())
return h - (h == -1)
diff --git a/pypy/objspace/std/marshal_impl.py b/pypy/objspace/std/marshal_impl.py
index af4fc1429a..3660b83872 100644
--- a/pypy/objspace/std/marshal_impl.py
+++ b/pypy/objspace/std/marshal_impl.py
@@ -201,7 +201,7 @@ def marshal_long(space, w_long, m):
SHIFT = 15
MASK = (1 << SHIFT) - 1
num = space.bigint_w(w_long)
- sign = num.sign
+ sign = num.get_sign()
num = num.abs()
total_length = (num.bit_length() + (SHIFT - 1)) / SHIFT
m.put_int(total_length * sign)
diff --git a/rpython/doc/conf.py b/rpython/doc/conf.py
index 7d72156c6e..b3ac8399b8 100644
--- a/rpython/doc/conf.py
+++ b/rpython/doc/conf.py
@@ -81,7 +81,7 @@ copyright = u'2023, The PyPy Project'
# The short X.Y version.
version = '7.3'
# The full version, including alpha/beta/rc tags.
-release = '7.3.13'
+release = '7.3.14'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/rpython/rlib/rbigint.py b/rpython/rlib/rbigint.py
index 7ad26bfea2..6a976d7f76 100644
--- a/rpython/rlib/rbigint.py
+++ b/rpython/rlib/rbigint.py
@@ -154,7 +154,7 @@ def intsign(i):
class rbigint(object):
"""This is a reimplementation of longs using a list of digits."""
_immutable_ = True
- _immutable_fields_ = ["_digits[*]", "size", "sign"]
+ _immutable_fields_ = ["_digits[*]", "_size"]
def __init__(self, digits=NULLDIGITS, sign=0, size=0):
if not we_are_translated():
@@ -163,9 +163,13 @@ class rbigint(object):
self._digits = digits
assert size >= 0
- self.size = size or len(digits)
+ self._size = (size or len(digits)) * sign
- self.sign = sign
+ def get_sign(self):
+ return intsign(self._size)
+
+ def _set_sign(self, sign):
+ self._size = abs(self._size) * sign
# __eq__ and __ne__ method exist for testing only, they are not RPython!
@not_rpython
@@ -209,7 +213,9 @@ class rbigint(object):
setdigit._always_inline_ = True
def numdigits(self):
- w = self.size
+ w = abs(self._size)
+ if not w:
+ w = 1
assert w > 0
return w
numdigits._always_inline_ = True
@@ -386,7 +392,7 @@ class rbigint(object):
def tobytes(self, nbytes, byteorder, signed):
if byteorder not in ('big', 'little'):
raise InvalidEndiannessError()
- if not signed and self.sign == -1:
+ if not signed and self.get_sign() == -1:
raise InvalidSignednessError()
bswap = byteorder == 'big'
@@ -400,7 +406,7 @@ class rbigint(object):
for i in range(0, imax):
d = self.widedigit(i)
- if self.sign == -1:
+ if self.get_sign() == -1:
d = (d ^ MASK) + carry
carry = d >> SHIFT
d &= MASK
@@ -408,7 +414,7 @@ class rbigint(object):
accum |= d << accumbits
if i == imax - 1:
# Avoid bogus 0's
- s = d ^ MASK if self.sign == -1 else d
+ s = d ^ MASK if self.get_sign() == -1 else d
while s:
s >>= 1
accumbits += 1
@@ -429,14 +435,14 @@ class rbigint(object):
raise OverflowError()
j += 1
- if self.sign == -1:
+ if self.get_sign() == -1:
# Add a sign bit
accum |= (~_widen_digit(0)) << accumbits
result.append(chr(accum & 0xFF))
if j < nbytes:
- signbyte = 0xFF if self.sign == -1 else 0
+ signbyte = 0xFF if self.get_sign() == -1 else 0
result.append_multiple_char(chr(signbyte), nbytes - j)
digits = result.build()
@@ -444,7 +450,7 @@ class rbigint(object):
if j == nbytes and nbytes > 0 and signed:
# If not already set, we cannot contain the sign bit
msb = digits[-1]
- if (self.sign == -1) != (ord(msb) >= 0x80):
+ if (self.get_sign() == -1) != (ord(msb) >= 0x80):
raise OverflowError()
if bswap:
@@ -455,6 +461,7 @@ class rbigint(object):
digits = ''.join([digits[i] for i in range(length-1, -1, -1)])
return digits
+ @jit.elidable
def toint(self):
"""
Get an integer from a bigint object.
@@ -468,7 +475,7 @@ class rbigint(object):
def _toint_helper(self):
x = self._touint_helper()
# Haven't lost any bits so far
- if self.sign >= 0:
+ if self.get_sign() >= 0:
res = intmask(x)
if res < 0:
raise OverflowError
@@ -490,7 +497,7 @@ class rbigint(object):
x = self._touint_helper()
except OverflowError:
return False
- if self.sign >= 0:
+ if self.get_sign() >= 0:
res = intmask(x)
return res >= 0
else:
@@ -502,11 +509,11 @@ class rbigint(object):
return _AsLongLong(self)
def tobool(self):
- return self.sign != 0
+ return self.get_sign() != 0
@jit.elidable
def touint(self):
- if self.sign == -1:
+ if self.get_sign() == -1:
raise ValueError("cannot convert negative integer to unsigned int")
return self._touint_helper()
@@ -524,7 +531,7 @@ class rbigint(object):
@jit.elidable
def toulonglong(self):
- if self.sign == -1:
+ if self.get_sign() == -1:
raise ValueError("cannot convert negative integer to unsigned int")
return _AsULonglong_ignore_sign(self)
@@ -565,7 +572,7 @@ class rbigint(object):
@jit.elidable
def eq(self, other):
- if (self.sign != other.sign or
+ if (self.get_sign() != other.get_sign() or
self.numdigits() != other.numdigits()):
return False
@@ -587,7 +594,7 @@ class rbigint(object):
if self.numdigits() > 1:
return False
- return (self.sign * self.digit(0)) == iother
+ return (self.get_sign() * self.digit(0)) == iother
def ne(self, other):
return not self.eq(other)
@@ -597,19 +604,21 @@ class rbigint(object):
@jit.elidable
def lt(self, other):
- if self.sign > other.sign:
+ selfsign = self.get_sign()
+ othersign = other.get_sign()
+ if selfsign > othersign:
return False
- if self.sign < other.sign:
+ if selfsign < othersign:
return True
ld1 = self.numdigits()
ld2 = other.numdigits()
if ld1 > ld2:
- if other.sign > 0:
+ if othersign > 0:
return False
else:
return True
elif ld1 < ld2:
- if other.sign > 0:
+ if othersign > 0:
return True
else:
return False
@@ -618,12 +627,12 @@ class rbigint(object):
d1 = self.digit(i)
d2 = other.digit(i)
if d1 < d2:
- if other.sign > 0:
+ if othersign > 0:
return True
else:
return False
elif d1 > d2:
- if other.sign > 0:
+ if othersign > 0:
return False
else:
return True
@@ -670,86 +679,94 @@ class rbigint(object):
@jit.elidable
def add(self, other):
- if self.sign == 0:
+ selfsign = self.get_sign()
+ othersign = other.get_sign()
+ if selfsign == 0:
return other
- if other.sign == 0:
+ if othersign == 0:
return self
- if self.sign == other.sign:
+ if selfsign == othersign:
result = _x_add(self, other)
else:
result = _x_sub(other, self)
- result.sign *= other.sign
+ result._set_sign(result.get_sign() * othersign)
return result
@jit.elidable
def int_add(self, iother):
+ selfsign = self.get_sign()
if not int_in_valid_range(iother):
# Fallback to long.
return self.add(rbigint.fromint(iother))
- elif self.sign == 0:
+ elif selfsign == 0:
return rbigint.fromint(iother)
elif iother == 0:
return self
- sign = intsign(iother)
- if self.sign == sign:
+ othersign = intsign(iother)
+ if selfsign == othersign:
result = _x_int_add(self, iother)
else:
result = _x_int_sub(self, iother)
- result.sign *= -1
- result.sign *= sign
+ result._set_sign(-result.get_sign())
+ result._set_sign(result.get_sign() * othersign)
return result
@jit.elidable
def sub(self, other):
- if other.sign == 0:
+ selfsign = self.get_sign()
+ othersign = other.get_sign()
+ if othersign == 0:
return self
- elif self.sign == 0:
- return rbigint(other._digits[:other.numdigits()], -other.sign, other.numdigits())
- elif self.sign == other.sign:
+ elif selfsign == 0:
+ return rbigint(other._digits[:other.numdigits()], -othersign, other.numdigits())
+ elif selfsign == othersign:
result = _x_sub(self, other)
else:
result = _x_add(self, other)
- result.sign *= self.sign
+ result._set_sign(result.get_sign() * selfsign)
return result
@jit.elidable
def int_sub(self, iother):
+ selfsign = self.get_sign()
if not int_in_valid_range(iother):
# Fallback to long.
return self.sub(rbigint.fromint(iother))
elif iother == 0:
return self
- elif self.sign == 0:
+ elif selfsign == 0:
return rbigint.fromint(-iother)
- elif self.sign == intsign(iother):
+ elif selfsign == intsign(iother):
result = _x_int_sub(self, iother)
else:
result = _x_int_add(self, iother)
- result.sign *= self.sign
+ result._set_sign(result.get_sign() * selfsign)
return result
@jit.elidable
def mul(self, other):
selfsize = self.numdigits()
othersize = other.numdigits()
+ selfsign = self.get_sign()
+ othersign = other.get_sign()
if selfsize > othersize:
self, other, selfsize, othersize = other, self, othersize, selfsize
- if self.sign == 0 or other.sign == 0:
+ if selfsign == 0 or othersign == 0:
return NULLRBIGINT
if selfsize == 1:
if self._digits[0] == ONEDIGIT:
- return rbigint(other._digits[:othersize], self.sign * other.sign, othersize)
+ return rbigint(other._digits[:othersize], selfsign * othersign, othersize)
elif othersize == 1:
res = other.uwidedigit(0) * self.udigit(0)
carry = res >> SHIFT
if carry:
- return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * other.sign, 2)
+ return rbigint([_store_digit(res & MASK), _store_digit(carry)], selfsign * othersign, 2)
else:
- return rbigint([_store_digit(res & MASK)], self.sign * other.sign, 1)
+ return rbigint([_store_digit(res & MASK)], selfsign * othersign, 1)
result = _x_mul(self, other, self.digit(0))
elif USE_KARATSUBA:
@@ -765,7 +782,7 @@ class rbigint(object):
else:
result = _x_mul(self, other)
- result.sign = self.sign * other.sign
+ result._set_sign(selfsign * othersign)
return result
@jit.elidable
@@ -774,7 +791,8 @@ class rbigint(object):
# Fallback to long.
return self.mul(rbigint.fromint(iother))
- if self.sign == 0 or iother == 0:
+ selfsign = self.get_sign()
+ if selfsign == 0 or iother == 0:
return NULLRBIGINT
asize = self.numdigits()
@@ -785,21 +803,21 @@ class rbigint(object):
if digit == 1:
if othersign == 1:
return self
- return rbigint(self._digits[:asize], self.sign * othersign, asize)
+ return rbigint(self._digits[:asize], selfsign * othersign, asize)
elif asize == 1:
udigit = r_uint(digit)
res = self.uwidedigit(0) * udigit
carry = res >> SHIFT
if carry:
- return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * othersign, 2)
+ return rbigint([_store_digit(res & MASK), _store_digit(carry)], selfsign * othersign, 2)
else:
- return rbigint([_store_digit(res & MASK)], self.sign * othersign, 1)
+ return rbigint([_store_digit(res & MASK)], selfsign * othersign, 1)
elif digit & (digit - 1) == 0:
result = self.lqshift(ptwotable[digit])
else:
result = _muladd1(self, digit)
- result.sign = self.sign * othersign
+ result._set_sign(selfsign * othersign)
return result
@jit.elidable
@@ -826,7 +844,8 @@ class rbigint(object):
digit = abs(iother)
assert digit > 0
- if self.sign == 1 and iother > 0:
+ selfsign = self.get_sign()
+ if selfsign == 1 and iother > 0:
if digit == 1:
return self
elif digit & (digit - 1) == 0:
@@ -834,11 +853,12 @@ class rbigint(object):
div, mod = _divrem1(self, digit)
- if mod != 0 and self.sign * intsign(iother) == -1:
- if div.sign == 0:
+ othersign = intsign(iother)
+ if mod != 0 and selfsign * othersign == -1:
+ if div.get_sign() == 0:
return ONENEGATIVERBIGINT
div = div.int_add(1)
- div.sign = self.sign * intsign(iother)
+ div._set_sign(selfsign * othersign)
div._normalize()
return div
@@ -853,7 +873,8 @@ class rbigint(object):
def int_mod(self, iother):
if iother == 0:
raise ZeroDivisionError("long division or modulo by zero")
- if self.sign == 0:
+ selfsign = self.get_sign()
+ if selfsign == 0:
return NULLRBIGINT
elif not int_in_valid_range(iother):
@@ -875,17 +896,18 @@ class rbigint(object):
rem = _int_rem_core(self, digit)
if rem == 0:
return NULLRBIGINT
- mod = rbigint([rem], -1 if self.sign < 0 else 1, 1)
+ mod = rbigint([rem], -1 if selfsign < 0 else 1, 1)
- if mod.sign * intsign(iother) == -1:
+ if mod.get_sign() * intsign(iother) == -1:
mod = mod.int_add(iother)
return mod
@jit.elidable
def int_mod_int_result(self, iother):
+ selfsign = self.get_sign()
if iother == 0:
raise ZeroDivisionError("long division or modulo by zero")
- if self.sign == 0:
+ if selfsign == 0:
return 0
elif not int_in_valid_range(iother):
@@ -904,7 +926,7 @@ class rbigint(object):
elif digit & (digit - 1) == 0:
mod = self.int_and_(digit - 1).toint() # XXX improve
else:
- mod = _int_rem_core(self, digit) * self.sign
+ mod = _int_rem_core(self, digit) * selfsign
if intsign(mod) * intsign(iother) == -1:
mod = mod + iother
return mod
@@ -927,12 +949,14 @@ class rbigint(object):
have different signs. We then subtract one from the 'div'
part of the outcome to keep the invariant intact.
"""
- if other.sign == 0:
+ selfsign = self.get_sign()
+ othersign = other.get_sign()
+ if othersign == 0:
raise ZeroDivisionError("long division or modulo by zero")
- if self.sign == 0:
+ if selfsign == 0:
return TWO_NULLRBIGINTS
- if other.numdigits() == 1 and not (-1 == other.sign != self.sign):
- otherint = other.digit(0) * other.sign
+ if other.numdigits() == 1 and not (-1 == othersign != selfsign):
+ otherint = other.digit(0) * othersign
assert int_in_valid_range(otherint)
return self.int_divmod(otherint)
@@ -948,9 +972,9 @@ class rbigint(object):
def _divmod_small(self, other):
div, mod = _divrem(self, other)
- if mod.sign * other.sign == -1:
+ if mod.get_sign() * other.get_sign() == -1:
mod = mod.add(other)
- if div.sign == 0:
+ if div.get_sign() == 0:
return ONENEGATIVERBIGINT, mod
div = div.int_sub(1)
return div, mod
@@ -962,8 +986,9 @@ class rbigint(object):
if iother == 0:
raise ZeroDivisionError("long division or modulo by zero")
- wsign = intsign(iother)
- if not int_in_valid_range(iother) or (wsign == -1 and self.sign != wsign):
+ selfsign = self.get_sign()
+ othersign = intsign(iother)
+ if not int_in_valid_range(iother) or (othersign == -1 and selfsign != othersign):
# Just fallback.
return self.divmod(rbigint.fromint(iother))
@@ -972,15 +997,15 @@ class rbigint(object):
div, mod = _divrem1(self, digit)
# _divrem1 doesn't fix the sign
- if div.size == 1 and div._digits[0] == NULLDIGIT:
- div.sign = 0
+ if div._size == 0:
+ assert div.get_sign() == 0
else:
- div.sign = self.sign * wsign
- if self.sign < 0:
+ div._set_sign(selfsign * othersign)
+ if selfsign < 0:
mod = -mod
- if mod and self.sign * wsign == -1:
+ if mod and selfsign * othersign == -1:
mod += iother
- if div.sign == 0:
+ if div.get_sign() == 0:
div = ONENEGATIVERBIGINT
else:
div = div.int_sub(1)
@@ -989,13 +1014,15 @@ class rbigint(object):
@jit.elidable
def pow(self, other, modulus=None):
+ selfsign = self.get_sign()
+ othersign = other.get_sign()
negativeOutput = False # if x<0 return negative output
# 5-ary values. If the exponent is large enough, table is
# precomputed so that table[i] == self**i % modulus for i in range(32).
# python translation: the table is computed when needed.
- if other.sign < 0: # if exponent is negative
+ if othersign < 0: # if exponent is negative
if modulus is not None:
raise TypeError(
"pow() 2nd argument "
@@ -1005,10 +1032,11 @@ class rbigint(object):
size_b = UDIGIT_TYPE(other.numdigits())
if modulus is not None:
- if modulus.sign == 0:
+ modulussign = modulus.get_sign()
+ if modulussign == 0:
raise ValueError("pow() 3rd argument cannot be 0")
- if modulus.sign < 0:
+ if modulussign < 0:
negativeOutput = True
modulus = modulus.neg()
@@ -1026,11 +1054,11 @@ class rbigint(object):
# base % modulus instead.
# We could _always_ do this reduction, but mod() isn't cheap,
# so we only do it when it buys something.
- if self.sign < 0 or self.numdigits() > modulus.numdigits():
+ if selfsign < 0 or self.numdigits() > modulus.numdigits():
self = self.mod(modulus)
- elif other.sign == 0:
+ elif othersign == 0:
return ONERBIGINT
- elif self.sign == 0:
+ elif selfsign == 0:
return NULLRBIGINT
elif size_b == 1:
if other._digits[0] == ONEDIGIT:
@@ -1039,13 +1067,13 @@ class rbigint(object):
adigit = self.digit(0)
digit = other.digit(0)
if adigit == 1:
- if self.sign == -1 and digit % 2:
+ if selfsign == -1 and digit % 2:
return ONENEGATIVERBIGINT
return ONERBIGINT
elif adigit & (adigit - 1) == 0:
ret = self.lshift(((digit-1)*(ptwotable[adigit]-1)) + digit-1)
- if self.sign == -1 and not digit % 2:
- ret.sign = 1
+ if selfsign == -1 and not digit % 2:
+ ret._set_sign(1)
return ret
# At this point self, other, and modulus are guaranteed non-negative UNLESS
@@ -1117,7 +1145,7 @@ class rbigint(object):
#
assert j == -5
- if negativeOutput and z.sign != 0:
+ if negativeOutput and z.get_sign() != 0:
z = z.sub(modulus)
return z
@@ -1136,12 +1164,14 @@ class rbigint(object):
"cannot be negative when 3rd argument specified")
raise ValueError("bigint pow() too negative")
+ selfsign = self.get_sign()
assert iother >= 0
if modulus is not None:
- if modulus.sign == 0:
+ modulussign = modulus.get_sign()
+ if modulussign == 0:
raise ValueError("pow() 3rd argument cannot be 0")
- if modulus.sign < 0:
+ if modulussign < 0:
negativeOutput = True
modulus = modulus.neg()
@@ -1159,24 +1189,24 @@ class rbigint(object):
# base % modulus instead.
# We could _always_ do this reduction, but mod() isn't cheap,
# so we only do it when it buys something.
- if self.sign < 0 or self.numdigits() > modulus.numdigits():
+ if selfsign < 0 or self.numdigits() > modulus.numdigits():
self = self.mod(modulus)
elif iother == 0:
return ONERBIGINT
- elif self.sign == 0:
+ elif selfsign == 0:
return NULLRBIGINT
elif iother == 1:
return self
elif self.numdigits() == 1:
adigit = self.digit(0)
if adigit == 1:
- if self.sign == -1 and iother % 2:
+ if selfsign == -1 and iother % 2:
return ONENEGATIVERBIGINT
return ONERBIGINT
elif adigit & (adigit - 1) == 0:
ret = self.lshift(((iother-1)*(ptwotable[adigit]-1)) + iother-1)
- if self.sign == -1 and not iother % 2:
- ret.sign = 1
+ if selfsign == -1 and not iother % 2:
+ ret._set_sign(1)
return ret
# At this point self, iother, and modulus are guaranteed non-negative UNLESS
@@ -1194,34 +1224,36 @@ class rbigint(object):
z = _help_mult(z, self, modulus)
j >>= 1
- if negativeOutput and z.sign != 0:
+ if negativeOutput and z.get_sign() != 0:
z = z.sub(modulus)
return z
@jit.elidable
def neg(self):
- return rbigint(self._digits, -self.sign, self.numdigits())
+ return rbigint(self._digits, -self.get_sign(), self.numdigits())
@jit.elidable
def abs(self):
- if self.sign != -1:
+ selfsign = self.get_sign()
+ if selfsign != -1:
return self
- return rbigint(self._digits, 1, self.numdigits())
+ return rbigint(self._digits, abs(selfsign), self.numdigits())
@jit.elidable
def invert(self): #Implement ~x as -(x + 1)
- if self.sign == 0:
+ if self.get_sign() == 0:
return ONENEGATIVERBIGINT
ret = self.int_add(1)
- ret.sign = -ret.sign
+ ret._set_sign(-ret.get_sign())
return ret
@jit.elidable
def lshift(self, int_other):
+ selfsign = self.get_sign()
if int_other < 0:
raise ValueError("negative shift count")
- elif int_other == 0 or self.sign == 0:
+ elif int_other == 0 or selfsign == 0:
return self
# wordshift, remshift = divmod(int_other, SHIFT)
@@ -1230,11 +1262,11 @@ class rbigint(object):
if not remshift:
# So we can avoid problems with eq, AND avoid the need for normalize.
- return rbigint([NULLDIGIT] * wordshift + self._digits, self.sign, self.numdigits() + wordshift)
+ return rbigint([NULLDIGIT] * wordshift + self._digits, selfsign, self.numdigits() + wordshift)
oldsize = self.numdigits()
newsize = oldsize + wordshift + 1
- z = rbigint([NULLDIGIT] * newsize, self.sign, newsize)
+ z = rbigint([NULLDIGIT] * newsize, selfsign, newsize)
accum = _unsigned_widen_digit(0)
j = 0
while j < oldsize:
@@ -1258,8 +1290,9 @@ class rbigint(object):
assert int_other > 0
oldsize = self.numdigits()
+ selfsign = self.get_sign()
- z = rbigint([NULLDIGIT] * (oldsize + 1), self.sign, (oldsize + 1))
+ z = rbigint([NULLDIGIT] * (oldsize + 1), selfsign, (oldsize + 1))
accum = _unsigned_widen_digit(0)
i = 0
while i < oldsize:
@@ -1278,7 +1311,8 @@ class rbigint(object):
raise ValueError("negative shift count")
elif int_other == 0:
return self
- if self.sign == -1 and not dont_invert:
+ selfsign = self.get_sign()
+ if selfsign == -1 and not dont_invert:
a = self.invert().rshift(int_other)
return a.invert()
@@ -1289,7 +1323,7 @@ class rbigint(object):
loshift = int_other % SHIFT
hishift = SHIFT - loshift
- z = rbigint([NULLDIGIT] * newsize, self.sign, newsize)
+ z = rbigint([NULLDIGIT] * newsize, selfsign, newsize)
i = 0
while i < newsize:
newdigit = (self.digit(wordshift) >> loshift)
@@ -1312,7 +1346,8 @@ class rbigint(object):
return NULLRBIGINT
hishift = SHIFT - loshift
- z = rbigint([NULLDIGIT] * newsize, self.sign, newsize)
+ selfsign = self.get_sign()
+ z = rbigint([NULLDIGIT] * newsize, selfsign, newsize)
i = 0
while i < newsize:
@@ -1407,7 +1442,7 @@ class rbigint(object):
@jit.elidable
def oct(self):
- if self.sign == 0:
+ if self.get_sign() == 0:
return '0L'
else:
return _format(self, BASE8, '0', 'L')
@@ -1437,9 +1472,9 @@ class rbigint(object):
for d in digits:
l = l << SHIFT
l += intmask(d)
- result = l * self.sign
+ result = l * self.get_sign()
if result == 0:
- assert self.sign == 0
+ assert self.get_sign() == 0
return result
def _normalize(self):
@@ -1449,9 +1484,9 @@ class rbigint(object):
i -= 1
assert i > 0
- self.size = i
+ self._size = i * self.get_sign()
if i == 1 and self._digits[0] == NULLDIGIT:
- self.sign = 0
+ self._size = 0
self._digits = NULLDIGITS
_normalize._always_inline_ = True
@@ -1498,7 +1533,7 @@ class rbigint(object):
def __repr__(self):
return "<rbigint digits=%s, sign=%s, size=%d, len=%d, %s>" % (self._digits,
- self.sign, self.numdigits(), len(self._digits),
+ self.get_sign(), self.numdigits(), len(self._digits),
self.tolong())
ONERBIGINT = rbigint([ONEDIGIT], 1, 1)
@@ -1911,14 +1946,14 @@ def _k_mul(a, b):
# 2. t1 <- ah*bh, and copy into high digits of result.
t1 = ah.mul(bh)
- assert t1.sign >= 0
+ assert t1.get_sign() >= 0
assert 2*shift + t1.numdigits() <= ret.numdigits()
for i in range(t1.numdigits()):
ret._digits[2*shift + i] = t1._digits[i]
# 3. t2 <- al*bl, and copy into the low digits.
t2 = al.mul(bl)
- assert t2.sign >= 0
+ assert t2.get_sign() >= 0
assert t2.numdigits() <= 2*shift # no overlap with high digits
for i in range(t2.numdigits()):
ret._digits[i] = t2._digits[i]
@@ -1938,7 +1973,7 @@ def _k_mul(a, b):
t2 = _x_add(bh, bl)
t3 = t1.mul(t2)
- assert t3.sign >= 0
+ assert t3.get_sign() >= 0
# Add t3. It's not obvious why we can't run out of room here.
# See the (*) comment after this function.
@@ -2249,7 +2284,7 @@ def _divrem(a, b):
size_a = a.numdigits()
size_b = b.numdigits()
- if b.sign == 0:
+ if b.get_sign() == 0:
raise ZeroDivisionError("long division or modulo by zero")
if (size_a < size_b or
@@ -2266,10 +2301,10 @@ def _divrem(a, b):
# The quotient z has the sign of a*b;
# the remainder r has the sign of a,
# so a = b*z + r.
- if a.sign != b.sign:
- z.sign = - z.sign
- if a.sign < 0 and rem.sign != 0:
- rem.sign = - rem.sign
+ if a.get_sign() != b.get_sign():
+ z._set_sign(-z.get_sign())
+ if a.get_sign() < 0 and rem.get_sign() != 0:
+ rem._set_sign(-rem.get_sign())
return z, rem
@@ -2313,7 +2348,7 @@ def div2n1n(a_container, a_startindex, b, n_S):
"""
if n_S <= HOLDER.DIV_LIMIT:
a = _extract_digits(a_container, a_startindex, 2 * n_S)
- if a.sign == 0:
+ if a.get_sign() == 0:
return NULLRBIGINT, NULLRBIGINT
res = _divrem(a, b)
return res
@@ -2349,7 +2384,7 @@ def div3n2n(a12_container, a12_startindex, a3_container, a3_startindex, b, b1, b
"""Helper function for div2n1n; not intended to be called directly."""
q, r = div2n1n(a12_container, a12_startindex, b1, n_S)
# equivalent to r = _full_digits_lshift_then_or(r, n_S, _extract_digits(a_container, a3_startindex, n_S))
- if r.sign == 0:
+ if r.get_sign() == 0:
r = _extract_digits(a3_container, a3_startindex, n_S)
else:
digits = [NULLDIGIT] * (n_S + r.numdigits())
@@ -2363,12 +2398,12 @@ def div3n2n(a12_container, a12_startindex, a3_container, a3_startindex, b, b1, b
index += 1
r = rbigint(digits, 1)
r._normalize()
- if q.sign == 0:
+ if q.get_sign() == 0:
return q, r
r = r.sub(q.mul(b2))
# loop runs at most twice
- while r.sign < 0:
+ while r.get_sign() < 0:
q = q.int_sub(1)
r = r.add(b)
return q, r
@@ -2377,7 +2412,7 @@ def _full_digits_lshift_then_or(a, n, b):
""" equivalent to a.lshift(n * SHIFT).or_(b)
the size of b must be smaller than n
"""
- if a.sign == 0:
+ if a.get_sign() == 0:
return b
bdigits = b.numdigits()
assert bdigits <= n
@@ -2455,13 +2490,13 @@ def divmod_big(a, b):
# follows cr.yp.to/bib/1998/burnikel.ps
if b.eq(NULLRBIGINT):
raise ZeroDivisionError
- elif b.sign < 0:
+ elif b.get_sign() < 0:
q, r = divmod_big(a.neg(), b.neg())
return q, r.neg()
- elif a.sign < 0:
+ elif a.get_sign() < 0:
q, r = divmod_big(a.invert(), b)
return q.invert(), b.add(r.invert())
- elif a.sign == 0:
+ elif a.get_sign() == 0:
return TWO_NULLRBIGINTS
else:
return _divmod_fast_pos(a, b)
@@ -2474,9 +2509,9 @@ def _x_int_lt(a, b, eq=False):
elif b < 0:
osign = -1
- if a.sign > osign:
+ if a.get_sign() > osign:
return False
- elif a.sign < osign:
+ elif a.get_sign() < osign:
return True
digits = a.numdigits()
@@ -2487,7 +2522,7 @@ def _x_int_lt(a, b, eq=False):
else:
return True
- d1 = a.sign * a.digit(0)
+ d1 = a.get_sign() * a.digit(0)
if eq:
if d1 <= b:
return True
@@ -2511,10 +2546,10 @@ def _AsScaledDouble(v):
least one round bit to stand in for the ignored least-significant bits.
"""
NBITS_WANTED = 57
- if v.sign == 0:
+ if v.get_sign() == 0:
return 0.0, 0
i = v.numdigits() - 1
- sign = v.sign
+ sign = v.get_sign()
x = float(v.digit(i))
nbitsneeded = NBITS_WANTED - 1
# Invariant: i Python digits remain unaccounted for.
@@ -2555,7 +2590,7 @@ def _AsDouble(n):
assert DBL_MANT_DIG < r_ulonglong.BITS
# Reduce to case n positive.
- sign = n.sign
+ sign = n.get_sign()
if sign == 0:
return 0.0
elif sign < 0:
@@ -2669,7 +2704,7 @@ def _bigint_true_divide(a, b):
MANT_DIG_BITS = DBL_MANT_DIG % SHIFT
# Reduce to case where a and b are both positive.
- negate = (a.sign < 0) ^ (b.sign < 0)
+ negate = (a.get_sign() < 0) ^ (b.get_sign() < 0)
if not b.tobool():
raise ZeroDivisionError("long division or modulo by zero")
if not a.tobool():
@@ -2825,7 +2860,7 @@ def _format_base2_notzero(a, digits, prefix='', suffix='', max_str_digits=0):
j -= 1
result[next_char_index] = prefix[j]
- if a.sign < 0:
+ if a.get_sign() < 0:
next_char_index -= 1
result[next_char_index] = '-'
@@ -2886,7 +2921,7 @@ def _format_recursive(x, i, output, pts, digits, size_prefix, mindigits, _format
if i < 0:
# this checks whether any digit has been appended yet
if curlen == size_prefix:
- if x.sign != 0:
+ if x.get_sign() != 0:
s = _format_int(x.toint(), digits)
output.append(s)
else:
@@ -2902,14 +2937,14 @@ def _format_recursive(x, i, output, pts, digits, size_prefix, mindigits, _format
_format_recursive(bot, i-1, output, pts, digits, size_prefix, mindigits, _format_int, max_str_digits)
def _format(x, digits, prefix='', suffix='', max_str_digits=0):
- if x.sign == 0:
+ if x.get_sign() == 0:
return prefix + "0" + suffix
base = len(digits)
assert base >= 2 and base <= 36
if (base & (base - 1)) == 0:
# base is 2, 4, 8, 16, ...
return _format_base2_notzero(x, digits, prefix, suffix, max_str_digits)
- negative = x.sign < 0
+ negative = x.get_sign() < 0
if negative:
x = x.neg()
rbase = rbigint.fromint(base)
@@ -2955,12 +2990,12 @@ def _format(x, digits, prefix='', suffix='', max_str_digits=0):
def _bitwise(a, op, b): # '&', '|', '^'
""" Bitwise and/or/xor operations """
- if a.sign < 0:
+ if a.get_sign() < 0:
a = a.invert()
maska = MASK
else:
maska = 0
- if b.sign < 0:
+ if b.get_sign() < 0:
b = b.invert()
maskb = MASK
else:
@@ -3042,7 +3077,7 @@ def _int_bitwise(a, op, b): # '&', '|', '^'
# Fallback to long.
return _bitwise(a, op, rbigint.fromint(b))
- if a.sign < 0:
+ if a.get_sign() < 0:
a = a.invert()
maska = MASK
else:
@@ -3129,13 +3164,13 @@ def _AsLongLong(v):
x = _AsULonglong_ignore_sign(v)
# grr grr grr
if x >= ULONGLONG_BOUND:
- if x == ULONGLONG_BOUND and v.sign < 0:
+ if x == ULONGLONG_BOUND and v.get_sign() < 0:
x = LONGLONG_MIN
else:
raise OverflowError
else:
x = r_longlong(x)
- if v.sign < 0:
+ if v.get_sign() < 0:
x = -x
return x
@@ -3158,7 +3193,7 @@ def make_unsigned_mask_conversion(T):
while i >= 0:
x = (x << SHIFT) + T(v.digit(i))
i -= 1
- if v.sign < 0:
+ if v.get_sign() < 0:
x = -x
return x
return _As_unsigned_mask
@@ -3174,7 +3209,7 @@ def _hash(v):
# hash(x) == hash(x % ULONG_MAX). In particular, this
# implies that hash(x) == hash(x % (2**64-1)).
i = v.numdigits() - 1
- sign = v.sign
+ sign = v.get_sign()
x = r_uint(0)
LONG_BIT_SHIFT = LONG_BIT - SHIFT
while i >= 0:
@@ -3234,8 +3269,8 @@ def _decimalstr_to_bigint(s, start=0, lim=-1):
a = rbigint([_store_digit(dig)], int(dig != 0))
tens = 1
dig = 0
- if sign and a.sign == 1:
- a.sign = -1
+ if sign and a.get_sign() == 1:
+ a._set_sign(-1)
return a
def parse_digit_string(parser):
@@ -3247,7 +3282,7 @@ def parse_digit_string(parser):
# check for errors and potentially remove underscores
s, start, end = parser._all_digits10()
a = _str_to_int_big_base10(s, start, end, HOLDER.STR2INT_LIMIT)
- a.sign *= parser.sign
+ a._set_sign(a.get_sign() * parser.sign)
return a
a = NULLRBIGINT
digitmax = BASE_MAX[base]
@@ -3267,7 +3302,7 @@ def parse_digit_string(parser):
else:
dig = dig * base + digit
baseexp *= base
- a.sign *= parser.sign
+ a._set_sign(a.get_sign() * parser.sign)
return a
@@ -3422,8 +3457,8 @@ def gcd_lehmer(a, b):
if a.lt(b):
a, b = b, a
- while b.size > 1:
- a_ms = a.digit(abs(a.size-1))
+ while b.numdigits() > 1:
+ a_ms = a.digit(abs(a.numdigits()-1))
x = 0
while a_ms & (0xFF << SHIFT-8) == 0:
@@ -3434,12 +3469,12 @@ def gcd_lehmer(a, b):
a_ms <<= 1
x += 1
- a_ms |= a.digit(abs(a.size-2)) >> SHIFT-x
+ a_ms |= a.digit(abs(a.numdigits()-2)) >> SHIFT-x
- if a.size == b.size:
- b_ms = (b.digit(abs(b.size-1)) << x) | (b.digit(abs(b.size-2)) >> SHIFT-x)
- elif a.size == b.size+1:
- b_ms = b.digit(abs(b.size-1)) >> SHIFT-x
+ if a.numdigits() == b.numdigits():
+ b_ms = (b.digit(abs(b.numdigits()-1)) << x) | (b.digit(abs(b.numdigits()-2)) >> SHIFT-x)
+ elif a.numdigits() == b.numdigits()+1:
+ b_ms = b.digit(abs(b.numdigits()-1)) >> SHIFT-x
else:
b_ms = 0
diff --git a/rpython/rlib/test/test_rbigint.py b/rpython/rlib/test/test_rbigint.py
index 2882ecb601..35bce60793 100644
--- a/rpython/rlib/test/test_rbigint.py
+++ b/rpython/rlib/test/test_rbigint.py
@@ -835,8 +835,11 @@ class Test_rbigint(object):
def test_normalize(self):
f1 = bigint([1, 0], 1)
f1._normalize()
- assert f1.size == 1
+ assert f1.numdigits() == 1
f0 = bigint([0], 0)
+ f0._normalize()
+ assert f0.numdigits() == 1
+ assert f0._size == 0
assert f1.sub(f1).eq(f0)
def test_invert(self):