Skip to content

Commit

Permalink
Merge branch 'main' into fast_range_v2
Browse files Browse the repository at this point in the history
  • Loading branch information
eendebakpt authored Mar 9, 2024
2 parents a110276 + db8f423 commit 602501b
Show file tree
Hide file tree
Showing 23 changed files with 884 additions and 216 deletions.
25 changes: 24 additions & 1 deletion Doc/c-api/hash.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
PyHash API
----------

See also the :c:member:`PyTypeObject.tp_hash` member.
See also the :c:member:`PyTypeObject.tp_hash` member and :ref:`numeric-hash`.

.. c:type:: Py_hash_t
Expand All @@ -17,6 +17,29 @@ See also the :c:member:`PyTypeObject.tp_hash` member.

.. versionadded:: 3.2

.. c:macro:: PyHASH_MODULUS
The `Mersenne prime <https://en.wikipedia.org/wiki/Mersenne_prime>`_ ``P = 2**n -1``, used for numeric hash scheme.

.. versionadded:: 3.13

.. c:macro:: PyHASH_BITS
The exponent ``n`` of ``P`` in :c:macro:`PyHASH_MODULUS`.

.. versionadded:: 3.13

.. c:macro:: PyHASH_INF
The hash value returned for a positive infinity.

.. versionadded:: 3.13

.. c:macro:: PyHASH_IMAG
The multiplier used for the imaginary part of a complex number.

.. versionadded:: 3.13

.. c:type:: PyHash_FuncDef
Expand Down
4 changes: 2 additions & 2 deletions Doc/library/dataclasses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,7 @@ Using dataclasses, *if* this code was valid::
class D:
x: list = [] # This code raises ValueError
def add(self, element):
self.x += element
self.x.append(element)

it would generate code similar to::

Expand All @@ -728,7 +728,7 @@ it would generate code similar to::
def __init__(self, x=x):
self.x = x
def add(self, element):
self.x += element
self.x.append(element)

assert D().x is D().x

Expand Down
7 changes: 7 additions & 0 deletions Doc/library/enum.rst
Original file line number Diff line number Diff line change
Expand Up @@ -279,13 +279,20 @@ Data Types
>>> Color.RED.value
1

Value of the member, can be set in :meth:`~object.__new__`.

.. note:: Enum member values

Member values can be anything: :class:`int`, :class:`str`, etc. If
the exact value is unimportant you may use :class:`auto` instances and an
appropriate value will be chosen for you. See :class:`auto` for the
details.

While mutable/unhashable values, such as :class:`dict`, :class:`list` or
a mutable :class:`~dataclasses.dataclass`, can be used, they will have a
quadratic performance impact during creation relative to the
total number of mutable/unhashable values in the enum.

.. attribute:: Enum._name_

Name of the member.
Expand Down
2 changes: 2 additions & 0 deletions Doc/library/resource.rst
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ platform.

The largest area of mapped memory which the process may occupy.

.. availability:: FreeBSD >= 11.


.. data:: RLIMIT_AS

Expand Down
120 changes: 120 additions & 0 deletions Doc/library/unittest.mock.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2831,3 +2831,123 @@ Sealing mocks
>>> mock.not_submock.attribute2 # This won't raise.

.. versionadded:: 3.7


Order of precedence of :attr:`side_effect`, :attr:`return_value` and *wraps*
----------------------------------------------------------------------------

The order of their precedence is:

1. :attr:`~Mock.side_effect`
2. :attr:`~Mock.return_value`
3. *wraps*

If all three are set, mock will return the value from :attr:`~Mock.side_effect`,
ignoring :attr:`~Mock.return_value` and the wrapped object altogether. If any
two are set, the one with the higher precedence will return the value.
Regardless of the order of which was set first, the order of precedence
remains unchanged.

>>> from unittest.mock import Mock
>>> class Order:
... @staticmethod
... def get_value():
... return "third"
...
>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.get_value.side_effect = ["first"]
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'first'

As ``None`` is the default value of :attr:`~Mock.side_effect`, if you reassign
its value back to ``None``, the order of precedence will be checked between
:attr:`~Mock.return_value` and the wrapped object, ignoring
:attr:`~Mock.side_effect`.

>>> order_mock.get_value.side_effect = None
>>> order_mock.get_value()
'second'

If the value being returned by :attr:`~Mock.side_effect` is :data:`DEFAULT`,
it is ignored and the order of precedence moves to the successor to obtain the
value to return.

>>> from unittest.mock import DEFAULT
>>> order_mock.get_value.side_effect = [DEFAULT]
>>> order_mock.get_value()
'second'

When :class:`Mock` wraps an object, the default value of
:attr:`~Mock.return_value` will be :data:`DEFAULT`.

>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.return_value
sentinel.DEFAULT
>>> order_mock.get_value.return_value
sentinel.DEFAULT

The order of precedence will ignore this value and it will move to the last
successor which is the wrapped object.

As the real call is being made to the wrapped object, creating an instance of
this mock will return the real instance of the class. The positional arguments,
if any, required by the wrapped object must be passed.

>>> order_mock_instance = order_mock()
>>> isinstance(order_mock_instance, Order)
True
>>> order_mock_instance.get_value()
'third'

>>> order_mock.get_value.return_value = DEFAULT
>>> order_mock.get_value()
'third'

>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'second'

But if you assign ``None`` to it, this will not be ignored as it is an
explicit assignment. So, the order of precedence will not move to the wrapped
object.

>>> order_mock.get_value.return_value = None
>>> order_mock.get_value() is None
True

Even if you set all three at once when initializing the mock, the order of
precedence remains the same:

>>> order_mock = Mock(spec=Order, wraps=Order,
... **{"get_value.side_effect": ["first"],
... "get_value.return_value": "second"}
... )
...
>>> order_mock.get_value()
'first'
>>> order_mock.get_value.side_effect = None
>>> order_mock.get_value()
'second'
>>> order_mock.get_value.return_value = DEFAULT
>>> order_mock.get_value()
'third'

If :attr:`~Mock.side_effect` is exhausted, the order of precedence will not
cause a value to be obtained from the successors. Instead, ``StopIteration``
exception is raised.

>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.get_value.side_effect = ["first side effect value",
... "another side effect value"]
>>> order_mock.get_value.return_value = "second"

>>> order_mock.get_value()
'first side effect value'
>>> order_mock.get_value()
'another side effect value'

>>> order_mock.get_value()
Traceback (most recent call last):
...
StopIteration
16 changes: 11 additions & 5 deletions Include/cpython/pyhash.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,20 @@
reduction modulo the prime 2**_PyHASH_BITS - 1. */

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

#define _PyHASH_MODULUS (((size_t)1 << _PyHASH_BITS) - 1)
#define _PyHASH_INF 314159
#define _PyHASH_IMAG _PyHASH_MULTIPLIER
#define PyHASH_MODULUS (((size_t)1 << _PyHASH_BITS) - 1)
#define PyHASH_INF 314159
#define PyHASH_IMAG _PyHASH_MULTIPLIER

/* Aliases kept for backward compatibility with Python 3.12 */
#define _PyHASH_BITS PyHASH_BITS
#define _PyHASH_MODULUS PyHASH_MODULUS
#define _PyHASH_INF PyHASH_INF
#define _PyHASH_IMAG PyHASH_IMAG

/* Helpers for hash functions */
PyAPI_FUNC(Py_hash_t) _Py_HashDouble(PyObject *, double);
Expand Down
27 changes: 24 additions & 3 deletions Include/internal/pycore_gc.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ static inline PyObject* _Py_FROM_GC(PyGC_Head *gc) {
# define _PyGC_BITS_UNREACHABLE (4)
# define _PyGC_BITS_FROZEN (8)
# define _PyGC_BITS_SHARED (16)
# define _PyGC_BITS_SHARED_INLINE (32)
#endif

/* True if the object is currently tracked by the GC. */
Expand Down Expand Up @@ -71,9 +72,12 @@ static inline int _PyObject_GC_MAY_BE_TRACKED(PyObject *obj) {

#ifdef Py_GIL_DISABLED

/* True if an object is shared between multiple threads and
* needs special purpose when freeing to do the possibility
* of in-flight lock-free reads occurring */
/* True if memory the object references is shared between
* multiple threads and needs special purpose when freeing
* those references due to the possibility of in-flight
* lock-free reads occurring. The object is responsible
* for calling _PyMem_FreeDelayed on the referenced
* memory. */
static inline int _PyObject_GC_IS_SHARED(PyObject *op) {
return (op->ob_gc_bits & _PyGC_BITS_SHARED) != 0;
}
Expand All @@ -84,6 +88,23 @@ static inline void _PyObject_GC_SET_SHARED(PyObject *op) {
}
#define _PyObject_GC_SET_SHARED(op) _PyObject_GC_SET_SHARED(_Py_CAST(PyObject*, op))

/* True if the memory of the object is shared between multiple
* threads and needs special purpose when freeing due to
* the possibility of in-flight lock-free reads occurring.
* Objects with this bit that are GC objects will automatically
* delay-freed by PyObject_GC_Del. */
static inline int _PyObject_GC_IS_SHARED_INLINE(PyObject *op) {
return (op->ob_gc_bits & _PyGC_BITS_SHARED_INLINE) != 0;
}
#define _PyObject_GC_IS_SHARED_INLINE(op) \
_PyObject_GC_IS_SHARED_INLINE(_Py_CAST(PyObject*, op))

static inline void _PyObject_GC_SET_SHARED_INLINE(PyObject *op) {
op->ob_gc_bits |= _PyGC_BITS_SHARED_INLINE;
}
#define _PyObject_GC_SET_SHARED_INLINE(op) \
_PyObject_GC_SET_SHARED_INLINE(_Py_CAST(PyObject*, op))

#endif

/* Bit flags for _gc_prev */
Expand Down
3 changes: 3 additions & 0 deletions Include/internal/pycore_pymem.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ extern int _PyMem_DebugEnabled(void);
// Enqueue a pointer to be freed possibly after some delay.
extern void _PyMem_FreeDelayed(void *ptr);

// Enqueue an object to be freed possibly after some delay
extern void _PyObject_FreeDelayed(void *ptr);

// Periodically process delayed free requests.
extern void _PyMem_ProcessDelayed(PyThreadState *tstate);

Expand Down
55 changes: 35 additions & 20 deletions Lib/logging/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@
To use, simply 'import logging.handlers' and log away!
"""

import io, logging, socket, os, pickle, struct, time, re
from stat import ST_DEV, ST_INO, ST_MTIME
import copy
import io
import logging
import os
import pickle
import queue
import re
import socket
import struct
import threading
import copy
import time

#
# Some constants...
Expand Down Expand Up @@ -269,7 +275,7 @@ def __init__(self, filename, when='h', interval=1, backupCount=0,
# path object (see Issue #27493), but self.baseFilename will be a string
filename = self.baseFilename
if os.path.exists(filename):
t = os.stat(filename)[ST_MTIME]
t = int(os.stat(filename).st_mtime)
else:
t = int(time.time())
self.rolloverAt = self.computeRollover(t)
Expand Down Expand Up @@ -459,8 +465,7 @@ class WatchedFileHandler(logging.FileHandler):
This handler is not appropriate for use under Windows, because
under Windows open files cannot be moved or renamed - logging
opens the files with exclusive locks - and so there is no need
for such a handler. Furthermore, ST_INO is not supported under
Windows; stat always returns zero for this value.
for such a handler.
This handler is based on a suggestion and patch by Chad J.
Schroeder.
Expand All @@ -476,9 +481,11 @@ def __init__(self, filename, mode='a', encoding=None, delay=False,
self._statstream()

def _statstream(self):
if self.stream:
sres = os.fstat(self.stream.fileno())
self.dev, self.ino = sres[ST_DEV], sres[ST_INO]
if self.stream is None:
return
sres = os.fstat(self.stream.fileno())
self.dev = sres.st_dev
self.ino = sres.st_ino

def reopenIfNeeded(self):
"""
Expand All @@ -488,25 +495,33 @@ def reopenIfNeeded(self):
has, close the old stream and reopen the file to get the
current stream.
"""
if self.stream is None:
return

# Reduce the chance of race conditions by stat'ing by path only
# once and then fstat'ing our new fd if we opened a new log stream.
# See issue #14632: Thanks to John Mulligan for the problem report
# and patch.
try:
# stat the file by path, checking for existence
sres = os.stat(self.baseFilename)

# compare file system stat with that of our stream file handle
reopen = (sres.st_dev != self.dev or sres.st_ino != self.ino)
except FileNotFoundError:
sres = None
# compare file system stat with that of our stream file handle
if not sres or sres[ST_DEV] != self.dev or sres[ST_INO] != self.ino:
if self.stream is not None:
# we have an open file handle, clean it up
self.stream.flush()
self.stream.close()
self.stream = None # See Issue #21742: _open () might fail.
# open a new file handle and get new stat info from that fd
self.stream = self._open()
self._statstream()
reopen = True

if not reopen:
return

# we have an open file handle, clean it up
self.stream.flush()
self.stream.close()
self.stream = None # See Issue #21742: _open () might fail.

# open a new file handle and get new stat info from that fd
self.stream = self._open()
self._statstream()

def emit(self, record):
"""
Expand Down
Loading

0 comments on commit 602501b

Please sign in to comment.