Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions Doc/c-api/allocation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,6 @@ Allocating Objects on the Heap
* :c:member:`~PyTypeObject.tp_alloc`


.. c:function:: void PyObject_Del(void *op)

Same as :c:func:`PyObject_Free`.

.. c:var:: PyObject _Py_NoneStruct

Object which is visible in Python as ``None``. This should only be accessed
Expand All @@ -156,3 +152,35 @@ Allocating Objects on the Heap
:ref:`moduleobjects`
To allocate and create extension modules.


Deprecated aliases
^^^^^^^^^^^^^^^^^^

These are :term:`soft deprecated` aliases to existing functions and macros.
They exist solely for backwards compatibility.


.. list-table::
:widths: auto
:header-rows: 1

* * Deprecated alias
* Function
* * .. c:macro:: PyObject_NEW(type, typeobj)
* :c:macro:`PyObject_New`
* * .. c:macro:: PyObject_NEW_VAR(type, typeobj, n)
* :c:macro:`PyObject_NewVar`
* * .. c:macro:: PyObject_INIT(op, typeobj)
* :c:func:`PyObject_Init`
* * .. c:macro:: PyObject_INIT_VAR(op, typeobj, n)
* :c:func:`PyObject_InitVar`
* * .. c:macro:: PyObject_MALLOC(n)
* :c:func:`PyObject_Malloc`
* * .. c:macro:: PyObject_REALLOC(p, n)
* :c:func:`PyObject_Realloc`
* * .. c:macro:: PyObject_FREE(p)
* :c:func:`PyObject_Free`
* * .. c:macro:: PyObject_DEL(p)
* :c:func:`PyObject_Free`
* * .. c:macro:: PyObject_Del(p)
* :c:func:`PyObject_Free`
9 changes: 8 additions & 1 deletion Doc/c-api/weakref.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,14 @@ as much as it can.

.. c:function:: int PyWeakref_CheckRef(PyObject *ob)

Return non-zero if *ob* is a reference object. This function always succeeds.
Return non-zero if *ob* is a reference object or a subclass of the reference
type. This function always succeeds.


.. c:function:: int PyWeakref_CheckRefExact(PyObject *ob)

Return non-zero if *ob* is a reference object, but not a subclass of the
reference type. This function always succeeds.


.. c:function:: int PyWeakref_CheckProxy(PyObject *ob)
Expand Down
6 changes: 6 additions & 0 deletions Doc/deprecations/pending-removal-in-3.17.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ Pending removal in Python 3.17
(Contributed by Shantanu Jain in :gh:`91896`.)


* :mod:`encodings`:

- Passing non-ascii *encoding* names to :func:`encodings.normalize_encoding`
is deprecated and scheduled for removal in Python 3.17.
(Contributed by Stan Ulbrych in :gh:`136702`)

* :mod:`typing`:

- Before Python 3.14, old-style unions were implemented using the private class
Expand Down
4 changes: 2 additions & 2 deletions Include/cpython/pyhash.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@

/* Parameters used for the numeric hash implementation. See notes for
_Py_HashDouble in Python/pyhash.c. Numeric hashes are based on
reduction modulo the prime 2**_PyHASH_BITS - 1. */
reduction modulo the prime 2**PyHASH_BITS - 1. */

#if SIZEOF_VOID_P >= 8
# define PyHASH_BITS 61
#else
# define PyHASH_BITS 31
#endif

#define PyHASH_MODULUS (((size_t)1 << _PyHASH_BITS) - 1)
#define PyHASH_MODULUS (((size_t)1 << PyHASH_BITS) - 1)
#define PyHASH_INF 314159
#define PyHASH_IMAG PyHASH_MULTIPLIER

Expand Down
4 changes: 4 additions & 0 deletions Lib/email/_header_value_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,10 @@ def params(self):
value = urllib.parse.unquote(value, encoding='latin-1')
else:
try:
# Explicitly look up the codec for warning generation, see gh-140030
# Can be removed in 3.17
import codecs
codecs.lookup(charset)
value = value.decode(charset, 'surrogateescape')
except (LookupError, UnicodeEncodeError):
# XXX: there should really be a custom defect for
Expand Down
4 changes: 4 additions & 0 deletions Lib/email/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,10 @@ def collapse_rfc2231_value(value, errors='replace',
charset = fallback_charset
rawbytes = bytes(text, 'raw-unicode-escape')
try:
# Explicitly look up the codec for warning generation, see gh-140030
# Can be removed in 3.17
import codecs
codecs.lookup(charset)
return str(rawbytes, charset, errors)
except LookupError:
# charset is not a known codec.
Expand Down
8 changes: 7 additions & 1 deletion Lib/encodings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

(c) Copyright CNRI, All Rights Reserved. NO WARRANTY.

"""#"
"""

import codecs
import sys
Expand Down Expand Up @@ -56,6 +56,12 @@ def normalize_encoding(encoding):
if isinstance(encoding, bytes):
encoding = str(encoding, "ascii")

if not encoding.isascii():
import warnings
warnings.warn(
"Support for non-ascii encoding names will be removed in 3.17",
DeprecationWarning, stacklevel=2)

return _normalize_encoding(encoding)

def search_function(encoding):
Expand Down
10 changes: 7 additions & 3 deletions Lib/test/test_codecs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3886,22 +3886,26 @@ def search_function(encoding):
self.assertEqual(codecs.lookup('TEST.AAA 8'), ('test.aaa-8', 2, 3, 4))
self.assertEqual(codecs.lookup('TEST.AAA---8'), ('test.aaa---8', 2, 3, 4))
self.assertEqual(codecs.lookup('TEST.AAA 8'), ('test.aaa---8', 2, 3, 4))
self.assertEqual(codecs.lookup('TEST.AAA\xe9\u20ac-8'), ('test.aaa\xe9\u20ac-8', 2, 3, 4))
self.assertEqual(codecs.lookup('TEST.AAA.8'), ('test.aaa.8', 2, 3, 4))
self.assertEqual(codecs.lookup('TEST.AAA...8'), ('test.aaa...8', 2, 3, 4))
with self.assertWarns(DeprecationWarning):
self.assertEqual(codecs.lookup('TEST.AAA\xe9\u20ac-8'), ('test.aaa\xe9\u20ac-8', 2, 3, 4))

def test_encodings_normalize_encoding(self):
# encodings.normalize_encoding() ignores non-ASCII characters.
normalize = encodings.normalize_encoding
self.assertEqual(normalize('utf_8'), 'utf_8')
self.assertEqual(normalize('utf\xE9\u20AC\U0010ffff-8'), 'utf_8')
self.assertEqual(normalize('utf 8'), 'utf_8')
# encodings.normalize_encoding() doesn't convert
# characters to lower case.
self.assertEqual(normalize('UTF 8'), 'UTF_8')
self.assertEqual(normalize('utf.8'), 'utf.8')
self.assertEqual(normalize('utf...8'), 'utf...8')

# Non-ASCII *encoding* is deprecated.
with self.assertWarnsRegex(DeprecationWarning,
"Support for non-ascii encoding names will be removed in 3.17"):
self.assertEqual(normalize('utf\xE9\u20AC\U0010ffff-8'), 'utf_8')


if __name__ == "__main__":
unittest.main()
3 changes: 2 additions & 1 deletion Lib/test/test_email/test_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -5738,7 +5738,8 @@ def test_rfc2231_bad_character_in_encoding(self):

"""
msg = email.message_from_string(m)
self.assertEqual(msg.get_filename(), 'myfile.txt')
with self.assertWarns(DeprecationWarning):
self.assertEqual(msg.get_filename(), 'myfile.txt')

def test_rfc2231_single_tick_in_filename_extended(self):
eq = self.assertEqual
Expand Down
10 changes: 9 additions & 1 deletion Lib/test/test_email/test_headerregistry.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,15 @@ def content_type_as_value(self,
decoded = args[2] if l>2 and args[2] is not DITTO else source
header = 'Content-Type:' + ' ' if source else ''
folded = args[3] if l>3 else header + decoded + '\n'
h = self.make_header('Content-Type', source)
# Both rfc2231 test cases with utf-8%E2%80%9D raise warnings,
# clear encoding cache to ensure test isolation.
if 'utf-8%E2%80%9D' in source and 'ascii' not in source:
import encodings
encodings._cache.clear()
with self.assertWarns(DeprecationWarning):
h = self.make_header('Content-Type', source)
else:
h = self.make_header('Content-Type', source)
self.assertEqual(h.content_type, content_type)
self.assertEqual(h.maintype, maintype)
self.assertEqual(h.subtype, subtype)
Expand Down
18 changes: 7 additions & 11 deletions Lib/test/test_raise.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,18 +186,14 @@ def test_class_cause(self):
self.fail("No exception raised")

def test_class_cause_nonexception_result(self):
class ConstructsNone(BaseException):
@classmethod
# See https://github.com/python/cpython/issues/140530.
class ConstructMortal(BaseException):
def __new__(*args, **kwargs):
return None
try:
raise IndexError from ConstructsNone
except TypeError as e:
self.assertIn("should have returned an instance of BaseException", str(e))
except IndexError:
self.fail("Wrong kind of exception raised")
else:
self.fail("No exception raised")
return ["mortal value"]

msg = ".*should have returned an instance of BaseException.*"
with self.assertRaisesRegex(TypeError, msg):
raise IndexError from ConstructMortal

def test_instance_cause(self):
cause = KeyError()
Expand Down
10 changes: 8 additions & 2 deletions Lib/test/test_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -1176,7 +1176,10 @@ def testInterfaceNameIndex(self):
'socket.if_indextoname() not available.')
@support.skip_android_selinux('if_indextoname')
def testInvalidInterfaceIndexToName(self):
self.assertRaises(OSError, socket.if_indextoname, 0)
with self.assertRaises(OSError) as cm:
socket.if_indextoname(0)
self.assertIsNotNone(cm.exception.errno)

self.assertRaises(ValueError, socket.if_indextoname, -1)
self.assertRaises(OverflowError, socket.if_indextoname, 2**1000)
self.assertRaises(TypeError, socket.if_indextoname, '_DEADBEEF')
Expand All @@ -1196,8 +1199,11 @@ def testInvalidInterfaceIndexToName(self):
'socket.if_nametoindex() not available.')
@support.skip_android_selinux('if_nametoindex')
def testInvalidInterfaceNameToIndex(self):
with self.assertRaises(OSError) as cm:
socket.if_nametoindex("_DEADBEEF")
self.assertIsNotNone(cm.exception.errno)

self.assertRaises(TypeError, socket.if_nametoindex, 0)
self.assertRaises(OSError, socket.if_nametoindex, '_DEADBEEF')

@unittest.skipUnless(hasattr(sys, 'getrefcount'),
'test needs sys.getrefcount()')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix a reference leak when ``raise exc from cause`` fails. Patch by Bénédikt
Tran.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:mod:`encodings`: Deprecate passing a non-ascii *encoding* name to
:func:`encodings.normalize_encoding` and schedule removal of support for
Python 3.17.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Correctly set :attr:`~OSError.errno` when :func:`socket.if_nametoindex` or
:func:`socket.if_indextoname` raise an :exc:`OSError`. Patch by Bénédikt
Tran.
6 changes: 3 additions & 3 deletions Modules/_decimal/_decimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -5799,15 +5799,15 @@ _decimal_Decimal___floor___impl(PyObject *self, PyTypeObject *cls)
static Py_hash_t
_dec_hash(PyDecObject *v)
{
#if defined(CONFIG_64) && _PyHASH_BITS == 61
#if defined(CONFIG_64) && PyHASH_BITS == 61
/* 2**61 - 1 */
mpd_uint_t p_data[1] = {2305843009213693951ULL};
mpd_t p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA, 0, 19, 1, 1, p_data};
/* Inverse of 10 modulo p */
mpd_uint_t inv10_p_data[1] = {2075258708292324556ULL};
mpd_t inv10_p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA,
0, 19, 1, 1, inv10_p_data};
#elif defined(CONFIG_32) && _PyHASH_BITS == 31
#elif defined(CONFIG_32) && PyHASH_BITS == 31
/* 2**31 - 1 */
mpd_uint_t p_data[2] = {147483647UL, 2};
mpd_t p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA, 0, 10, 2, 2, p_data};
Expand All @@ -5816,7 +5816,7 @@ _dec_hash(PyDecObject *v)
mpd_t inv10_p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA,
0, 10, 2, 2, inv10_p_data};
#else
#error "No valid combination of CONFIG_64, CONFIG_32 and _PyHASH_BITS"
#error "No valid combination of CONFIG_64, CONFIG_32 and PyHASH_BITS"
#endif
const Py_hash_t py_hash_inf = 314159;
mpd_uint_t ten_data[1] = {10};
Expand Down
5 changes: 3 additions & 2 deletions Modules/socketmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -7294,10 +7294,10 @@ _socket_if_nametoindex_impl(PyObject *module, PyObject *oname)
unsigned long index;
#endif

errno = ENODEV; // in case 'if_nametoindex' does not set errno
index = if_nametoindex(PyBytes_AS_STRING(oname));
if (index == 0) {
/* if_nametoindex() doesn't set errno */
PyErr_SetString(PyExc_OSError, "no interface with this name");
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}

Expand All @@ -7317,6 +7317,7 @@ static PyObject *
_socket_if_indextoname_impl(PyObject *module, NET_IFINDEX index)
/*[clinic end generated code: output=e48bc324993052e0 input=c93f753d0cf6d7d1]*/
{
errno = ENXIO; // in case 'if_indextoname' does not set errno
char name[IF_NAMESIZE + 1];
if (if_indextoname(index, name) == NULL) {
PyErr_SetFromErrno(PyExc_OSError);
Expand Down
2 changes: 1 addition & 1 deletion Objects/complexobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ complex_hash(PyObject *op)
* compare equal must have the same hash value, so that
* hash(x + 0*j) must equal hash(x).
*/
combined = hashreal + _PyHASH_IMAG * hashimag;
combined = hashreal + PyHASH_IMAG * hashimag;
if (combined == (Py_uhash_t)-1)
combined = (Py_uhash_t)-2;
return (Py_hash_t)combined;
Expand Down
38 changes: 19 additions & 19 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3703,36 +3703,36 @@ long_hash(PyObject *obj)
#endif

while (--i >= 0) {
/* Here x is a quantity in the range [0, _PyHASH_MODULUS); we
/* Here x is a quantity in the range [0, PyHASH_MODULUS); we
want to compute x * 2**PyLong_SHIFT + v->long_value.ob_digit[i] modulo
_PyHASH_MODULUS.
PyHASH_MODULUS.

The computation of x * 2**PyLong_SHIFT % _PyHASH_MODULUS
The computation of x * 2**PyLong_SHIFT % PyHASH_MODULUS
amounts to a rotation of the bits of x. To see this, write

x * 2**PyLong_SHIFT = y * 2**_PyHASH_BITS + z
x * 2**PyLong_SHIFT = y * 2**PyHASH_BITS + z

where y = x >> (_PyHASH_BITS - PyLong_SHIFT) gives the top
where y = x >> (PyHASH_BITS - PyLong_SHIFT) gives the top
PyLong_SHIFT bits of x (those that are shifted out of the
original _PyHASH_BITS bits, and z = (x << PyLong_SHIFT) &
_PyHASH_MODULUS gives the bottom _PyHASH_BITS - PyLong_SHIFT
bits of x, shifted up. Then since 2**_PyHASH_BITS is
congruent to 1 modulo _PyHASH_MODULUS, y*2**_PyHASH_BITS is
congruent to y modulo _PyHASH_MODULUS. So
original PyHASH_BITS bits, and z = (x << PyLong_SHIFT) &
PyHASH_MODULUS gives the bottom PyHASH_BITS - PyLong_SHIFT
bits of x, shifted up. Then since 2**PyHASH_BITS is
congruent to 1 modulo PyHASH_MODULUS, y*2**PyHASH_BITS is
congruent to y modulo PyHASH_MODULUS. So

x * 2**PyLong_SHIFT = y + z (mod _PyHASH_MODULUS).
x * 2**PyLong_SHIFT = y + z (mod PyHASH_MODULUS).

The right-hand side is just the result of rotating the
_PyHASH_BITS bits of x left by PyLong_SHIFT places; since
not all _PyHASH_BITS bits of x are 1s, the same is true
after rotation, so 0 <= y+z < _PyHASH_MODULUS and y + z is
PyHASH_BITS bits of x left by PyLong_SHIFT places; since
not all PyHASH_BITS bits of x are 1s, the same is true
after rotation, so 0 <= y+z < PyHASH_MODULUS and y + z is
the reduction of x*2**PyLong_SHIFT modulo
_PyHASH_MODULUS. */
x = ((x << PyLong_SHIFT) & _PyHASH_MODULUS) |
(x >> (_PyHASH_BITS - PyLong_SHIFT));
PyHASH_MODULUS. */
x = ((x << PyLong_SHIFT) & PyHASH_MODULUS) |
(x >> (PyHASH_BITS - PyLong_SHIFT));
x += v->long_value.ob_digit[i];
if (x >= _PyHASH_MODULUS)
x -= _PyHASH_MODULUS;
if (x >= PyHASH_MODULUS)
x -= PyHASH_MODULUS;
}
x = x * sign;
if (x == (Py_uhash_t)-1)
Expand Down
1 change: 1 addition & 0 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -2148,6 +2148,7 @@ do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause)
"calling %R should have returned an instance of "
"BaseException, not %R",
cause, Py_TYPE(fixed_cause));
Py_DECREF(fixed_cause);
goto raise_error;
}
Py_DECREF(cause);
Expand Down
Loading
Loading