Skip to content

Commit

Permalink
Merge branch 'main' into pythongh-124218-unique-id
Browse files Browse the repository at this point in the history
  • Loading branch information
colesbury authored Oct 1, 2024
2 parents e5ec9f7 + 5aa91c5 commit 5bc5d38
Show file tree
Hide file tree
Showing 24 changed files with 115 additions and 374 deletions.
10 changes: 9 additions & 1 deletion Doc/library/dataclasses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ Module contents
:func:`!astuple` raises :exc:`TypeError` if *obj* is not a dataclass
instance.

.. function:: make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False, module=None)
.. function:: make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False, module=None, decorator=dataclass)

Creates a new dataclass with name *cls_name*, fields as defined
in *fields*, base classes as given in *bases*, and initialized
Expand All @@ -415,6 +415,11 @@ Module contents
of the dataclass is set to that value.
By default, it is set to the module name of the caller.

The *decorator* parameter is a callable that will be used to create the dataclass.
It should take the class object as a first argument and the same keyword arguments
as :func:`@dataclass <dataclass>`. By default, the :func:`@dataclass <dataclass>`
function is used.

This function is not strictly required, because any Python
mechanism for creating a new class with :attr:`!__annotations__` can
then apply the :func:`@dataclass <dataclass>` function to convert that class to
Expand All @@ -438,6 +443,9 @@ Module contents
def add_one(self):
return self.x + 1

.. versionadded:: 3.14
Added the *decorator* parameter.

.. function:: replace(obj, /, **changes)

Creates a new object of the same type as *obj*, replacing
Expand Down
5 changes: 3 additions & 2 deletions Doc/using/windows.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ available for application-local distributions.

As specified in :pep:`11`, a Python release only supports a Windows platform
while Microsoft considers the platform under extended support. This means that
Python |version| supports Windows 8.1 and newer. If you require Windows 7
support, please install Python 3.8.
Python |version| supports Windows 10 and newer. If you require Windows 7
support, please install Python 3.8. If you require Windows 8.1 support,
please install Python 3.12.

There are a number of different installers available for Windows, each with
certain benefits and downsides.
Expand Down
13 changes: 4 additions & 9 deletions Include/cpython/dictobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,11 @@ typedef struct {
/* Number of items in the dictionary */
Py_ssize_t ma_used;

/* Dictionary version: globally unique, value change each time
the dictionary is modified */
#ifdef Py_BUILD_CORE
/* Bits 0-7 are for dict watchers.
/* This is a private field for CPython's internal use.
* Bits 0-7 are for dict watchers.
* Bits 8-11 are for the watched mutation counter (used by tier2 optimization)
* The remaining bits (12-63) are the actual version tag. */
uint64_t ma_version_tag;
#else
Py_DEPRECATED(3.12) uint64_t ma_version_tag;
#endif
* The remaining bits are not currently used. */
uint64_t _ma_watcher_tag;

PyDictKeysObject *ma_keys;

Expand Down
30 changes: 2 additions & 28 deletions Include/internal/pycore_dict.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,31 +230,6 @@ static inline PyDictUnicodeEntry* DK_UNICODE_ENTRIES(PyDictKeysObject *dk) {
#define DICT_WATCHER_MASK ((1 << DICT_MAX_WATCHERS) - 1)
#define DICT_WATCHER_AND_MODIFICATION_MASK ((1 << (DICT_MAX_WATCHERS + DICT_WATCHED_MUTATION_BITS)) - 1)

#ifdef Py_GIL_DISABLED

#define THREAD_LOCAL_DICT_VERSION_COUNT 256
#define THREAD_LOCAL_DICT_VERSION_BATCH THREAD_LOCAL_DICT_VERSION_COUNT * DICT_VERSION_INCREMENT

static inline uint64_t
dict_next_version(PyInterpreterState *interp)
{
PyThreadState *tstate = PyThreadState_GET();
uint64_t cur_progress = (tstate->dict_global_version &
(THREAD_LOCAL_DICT_VERSION_BATCH - 1));
if (cur_progress == 0) {
uint64_t next = _Py_atomic_add_uint64(&interp->dict_state.global_version,
THREAD_LOCAL_DICT_VERSION_BATCH);
tstate->dict_global_version = next;
}
return tstate->dict_global_version += DICT_VERSION_INCREMENT;
}

#define DICT_NEXT_VERSION(INTERP) dict_next_version(INTERP)

#else
#define DICT_NEXT_VERSION(INTERP) \
((INTERP)->dict_state.global_version += DICT_VERSION_INCREMENT)
#endif

PyAPI_FUNC(void)
_PyDict_SendEvent(int watcher_bits,
Expand All @@ -263,20 +238,19 @@ _PyDict_SendEvent(int watcher_bits,
PyObject *key,
PyObject *value);

static inline uint64_t
static inline void
_PyDict_NotifyEvent(PyInterpreterState *interp,
PyDict_WatchEvent event,
PyDictObject *mp,
PyObject *key,
PyObject *value)
{
assert(Py_REFCNT((PyObject*)mp) > 0);
int watcher_bits = mp->ma_version_tag & DICT_WATCHER_MASK;
int watcher_bits = mp->_ma_watcher_tag & DICT_WATCHER_MASK;
if (watcher_bits) {
RARE_EVENT_STAT_INC(watched_dict_modification);
_PyDict_SendEvent(watcher_bits, event, mp, key, value);
}
return DICT_NEXT_VERSION(interp) | (mp->ma_version_tag & DICT_WATCHER_AND_MODIFICATION_MASK);
}

extern PyDictObject *_PyObject_MaterializeManagedDict(PyObject *obj);
Expand Down
4 changes: 0 additions & 4 deletions Include/internal/pycore_dict_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ extern "C" {
#define DICT_WATCHED_MUTATION_BITS 4

struct _Py_dict_state {
/*Global counter used to set ma_version_tag field of dictionary.
* It is incremented each time that a dictionary is created and each
* time that a dictionary is modified. */
uint64_t global_version;
uint32_t next_keys_version;
PyDict_WatchCallback watchers[DICT_MAX_WATCHERS];
};
Expand Down
6 changes: 4 additions & 2 deletions Lib/asyncio/__main__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ast
import asyncio
import concurrent.futures
import contextvars
import inspect
import os
import site
Expand All @@ -22,6 +23,7 @@ def __init__(self, locals, loop):
self.compile.compiler.flags |= ast.PyCF_ALLOW_TOP_LEVEL_AWAIT

self.loop = loop
self.context = contextvars.copy_context()

def runcode(self, code):
global return_code
Expand Down Expand Up @@ -55,12 +57,12 @@ def callback():
return

try:
repl_future = self.loop.create_task(coro)
repl_future = self.loop.create_task(coro, context=self.context)
futures._chain_future(repl_future, future)
except BaseException as exc:
future.set_exception(exc)

loop.call_soon_threadsafe(callback)
loop.call_soon_threadsafe(callback, context=self.context)

try:
return future.result()
Expand Down
6 changes: 3 additions & 3 deletions Lib/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -1550,7 +1550,7 @@ def _astuple_inner(obj, tuple_factory):
def make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True,
repr=True, eq=True, order=False, unsafe_hash=False,
frozen=False, match_args=True, kw_only=False, slots=False,
weakref_slot=False, module=None):
weakref_slot=False, module=None, decorator=dataclass):
"""Return a new dynamically created dataclass.
The dataclass name will be 'cls_name'. 'fields' is an iterable
Expand Down Expand Up @@ -1630,8 +1630,8 @@ def exec_body_callback(ns):
if module is not None:
cls.__module__ = module

# Apply the normal decorator.
return dataclass(cls, init=init, repr=repr, eq=eq, order=order,
# Apply the normal provided decorator.
return decorator(cls, init=init, repr=repr, eq=eq, order=order,
unsafe_hash=unsafe_hash, frozen=frozen,
match_args=match_args, kw_only=kw_only, slots=slots,
weakref_slot=weakref_slot)
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/support/import_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ def make_legacy_pyc(source):
:return: The file system path to the legacy pyc file.
"""
pyc_file = importlib.util.cache_from_source(source)
up_one = os.path.dirname(os.path.abspath(source))
legacy_pyc = os.path.join(up_one, source + 'c')
assert source.endswith('.py')
legacy_pyc = source + 'c'
shutil.move(pyc_file, legacy_pyc)
return legacy_pyc

Expand Down
17 changes: 17 additions & 0 deletions Lib/test/test_dataclasses/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4317,6 +4317,23 @@ def test_funny_class_names_names(self):
C = make_dataclass(classname, ['a', 'b'])
self.assertEqual(C.__name__, classname)

def test_dataclass_decorator_default(self):
C = make_dataclass('C', [('x', int)], decorator=dataclass)
c = C(10)
self.assertEqual(c.x, 10)

def test_dataclass_custom_decorator(self):
def custom_dataclass(cls, *args, **kwargs):
dc = dataclass(cls, *args, **kwargs)
dc.__custom__ = True
return dc

C = make_dataclass('C', [('x', int)], decorator=custom_dataclass)
c = C(10)
self.assertEqual(c.x, 10)
self.assertEqual(c.__custom__, True)


class TestReplace(unittest.TestCase):
def test(self):
@dataclass(frozen=True)
Expand Down
Loading

0 comments on commit 5bc5d38

Please sign in to comment.