8000 Merge branch 'main' into docs-warnings-curses · python/cpython@ec2de2e · GitHub
[go: up one dir, main page]

Skip to content

Commit ec2de2e

Browse files
authored
Merge branch 'main' into docs-warnings-curses
2 parents 31bf5f4 + 654d44b commit ec2de2e

File tree

10 files changed

+193
-96
lines changed

10 files changed

+193
-96
lines changed

Include/internal/pycore_call.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,16 @@ _PyObject_FastCallTstate(PyThreadState *tstate, PyObject *func, PyObject *const
116116
return _PyObject_VectorcallTstate(tstate, func, args, (size_t)nargs, NULL);
117117
}
118118

119+
PyObject *const *
120+
_PyStack_UnpackDict(PyThreadState *tstate,
121+
PyObject *const *args, Py_ssize_t nargs,
122+
PyObject *kwargs, PyObject *< 8000 span class=pl-c1>*p_kwnames);
123+
124+
void
125+
_PyStack_UnpackDict_Free(PyObject *const *stack, Py_ssize_t nargs,
126+
PyObject *kwnames);
127+
128+
void _PyStack_UnpackDict_FreeNoDecRef(PyObject *const *stack, PyObject *kwnames);
119129

120130
#ifdef __cplusplus
121131
}

Lib/platform.py

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,11 @@
136136
'pl': 200, 'p': 200,
137137
}
138138

139-
_component_re = re.compile(r'([0-9]+|[._+-])')
140139

141140
def _comparable_version(version):
141+
component_re = re.compile(r'([0-9]+|[._+-])')
142142
result = []
143-
for v in _component_re.split(version):
143+
for v in component_re.split(version):
144144
if v not in '._+-':
145145
try:
146146
v = int(v, 10)
@@ -152,11 +152,6 @@ def _comparable_version(version):
152152

153153
### Platform specific APIs
154154

155-
_libc_search = re.compile(b'(__libc_init)'
156-
b'|'
157-
b'(GLIBC_([0-9.]+))'
158-
b'|'
159-
br'(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)', re.ASCII)
160155

161156
def libc_ver(executable=None, lib='', version='', chunksize=16384):
162157

@@ -190,6 +185,12 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384):
190185
# sys.executable is not set.
191186
return lib, version
192187

188+
libc_search = re.compile(b'(__libc_init)'
189+
b'|'
190+
b'(GLIBC_([0-9.]+))'
191+
b'|'
192+
br'(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)', re.ASCII)
193+
193194
V = _comparable_version
194195
# We use os.path.realpath()
195196
# here to work around problems with Cygwin not being
@@ -200,7 +201,7 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384):
200201
pos = 0
201202
while pos < len(binary):
202203
if b'libc' in binary or b'GLIBC' in binary:
203-
m = _libc_search.search(binary, pos)
204+
m = libc_search.search(binary, pos)
204205
else:
205206
m = None
206207
if not m or m.end() == len(binary):
@@ -247,9 +248,6 @@ def _norm_version(version, build=''):
247248
version = '.'.join(strings[:3])
248249
return version
249250

250-
_ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) '
251-
r'.*'
252-
r'\[.* ([\d.]+)\])')
253251

254252
# Examples of VER command output:
255253
#
@@ -295,9 +293,13 @@ def _syscmd_ver(system='', release='', version='',
295293
else:
296294
return system, release, version
297295

296+
ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) '
297+
r'.*'
298+
r'\[.* ([\d.]+)\])')
299+
298300
# Parse the output
299301
info = info.strip()
300-
m = _ver_output.match(info)
302+
m = ver_output.match(info)
301303
if m is not None:
302304
system, release, version = m.groups()
303305
# Strip trailing dots from version and release
@@ -1033,18 +1035,6 @@ def processor():
10331035

10341036
### Various APIs for extracting information from sys.version
10351037

1036-
_sys_version_parser = re.compile(
1037-
r'([\w.+]+)\s*' # "version<space>"
1038-
r'\(#?([^,]+)' # "(#buildno"
1039-
r'(?:,\s*([\w ]*)' # ", builddate"
1040-
r'(?:,\s*([\w :]*))?)?\)\s*' # ", buildtime)<space>"
1041-
r'\[([^\]]+)\]?', re.ASCII) # "[compiler]"
1042-
1043-
_pypy_sys_version_parser = re.compile(
1044-
r'([\w.+]+)\s*'
1045-
r'\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
1046-
r'\[PyPy [^\]]+\]?')
1047-
10481038
_sys_version_cache = {}
10491039

10501040
def _sys_version(sys_version=None):
@@ -1076,10 +1066,17 @@ def _sys_version(sys_version=None):
10761066
if result is not None:
10771067
return result
10781068

1069+
sy 10000 s_version_parser = re.compile(
1070+
r'([\w.+]+)\s*' # "version<space>"
1071+
r'\(#?([^,]+)' # "(#buildno"
1072+
r'(?:,\s*([\w ]*)' # ", builddate"
1073+
r'(?:,\s*([\w :]*))?)?\)\s*' # ", buildtime)<space>"
1074+
r'\[([^\]]+)\]?', re.ASCII) # "[compiler]"
1075+
10791076
if sys.platform.startswith('java'):
10801077
# Jython
10811078
name = 'Jython'
1082-
match = _sys_version_parser.match(sys_version)
1079+
match = sys_version_parser.match(sys_version)
10831080
if match is None:
10841081
raise ValueError(
10851082
'failed to parse Jython sys.version: %s' %
@@ -1091,8 +1088,13 @@ def _sys_version(sys_version=None):
10911088

10921089
elif "PyPy" in sys_version:
10931090
# PyPy
1091+
pypy_sys_version_parser = re.compile(
1092+
r'([\w.+]+)\s*'
1093+
r'\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
1094+
r'\[PyPy [^\]]+\]?')
1095+
10941096
name = "PyPy"
1095-
match = _pypy_sys_version_parser.match(sys_version)
1097+
match = pypy_sys_version_parser.match(sys_version)
10961098
if match is None:
10971099
raise ValueError("failed to parse PyPy sys.version: %s" %
10981100
repr(sys_version))
@@ -1101,7 +1103,7 @@ def _sys_version(sys_version=None):
11011103

11021104
else:
11031105
# CPython
1104-
match = _sys_version_parser.match(sys_version)
1106+
match = sys_version_parser.match(sys_version)
11051107
if match is None:
11061108
raise ValueError(
11071109
'failed to parse CPython sys.version: %s' %
@@ -1290,13 +1292,6 @@ def platform(aliased=False, terse=False):
12901292
### freedesktop.org os-release standard
12911293
# https://www.freedesktop.org/software/systemd/man/os-release.html
12921294

1293-
# NAME=value with optional quotes (' or "). The regular expression is less
1294-
# strict than shell lexer, but that's ok.
1295-
_os_release_line = re.compile(
1296-
"^(?P<name>[a-zA-Z0-9_]+)=(?P<quote>[\"\']?)(?P<value>.*)(?P=quote)$"
1297-
)
1298-
# unescape five special characters mentioned in the standard
1299-
_os_release_unescape = re.compile(r"\\([\\\$\"\'`])")
13001295
# /etc takes precedence over /usr/lib
13011296
_os_release_candidates = ("/etc/os-release", "/usr/lib/os-release")
13021297
_os_release_cache = None
@@ -1311,10 +1306,18 @@ def _parse_os_release(lines):
13111306
"PRETTY_NAME": "Linux",
13121307
}
13131308

1309+
# NAME=value with optional quotes (' or "). The regular expression is less
1310+
# strict than shell lexer, but that's ok.
1311+
os_release_line = re.compile(
1312+
"^(?P<name>[a-zA-Z0-9_]+)=(?P<quote>[\"\']?)(?P<value>.*)(?P=quote)$"
1313+
)
1314+
# unescape five special characters mentioned in the standard
1315+
os_release_unescape = re.compile(r"\\([\\\$\"\'`])")
1316+
13141317
for line in lines:
1315-
mo = _os_release_line.match(line)
1318+
mo = os_release_line.match(line)
13161319
if mo is not None:
1317-
info[mo.group('name')] = _os_release_unescape.sub(
1320+
info[mo.group('name')] = os_release_unescape.sub(
13181321
r"\1", mo.group('value')
13191322
)
13201323

Lib/test/test_calendar.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -493,11 +493,11 @@ def test_format(self):
493493
class CalendarTestCase(unittest.TestCase):
494494

495495
def test_deprecation_warning(self):
496-
with warnings.catch_warnings(record=True) as w:
496+
with self.assertWarnsRegex(
497+
DeprecationWarning,
498+
"The 'January' attribute is deprecated, use 'JANUARY' instead"
499+
):
497500
calendar.January
498-
self.assertEqual(len(w), 1)
499-
self.assertEqual(w[0].category, DeprecationWarning)
500-
self.assertIn("The 'January' attribute is deprecated, use 'JANUARY' instead", str(w[0].message))
501501

502502
def test_isleap(self):
503503
# Make sure that the return is right for a few years, and

Lib/test/test_dataclasses.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3671,7 +3671,7 @@ def test_text_annotations(self):
36713671
ByMakeDataClass = make_dataclass('ByMakeDataClass', [('x', int)])
36723672
ManualModuleMakeDataClass = make_dataclass('ManualModuleMakeDataClass',
36733673
[('x', int)],
3674-
module='test.test_dataclasses')
3674+
module=__name__)
36753675
WrongNameMakeDataclass = make_dataclass('Wrong', [('x', int)])
36763676
WrongModuleMakeDataclass = make_dataclass('WrongModuleMakeDataclass',
36773677
[('x', int)],
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Complex function calls are now faster and consume no C stack
2+
space.
3+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve import time of :mod:`platform` module.

Objects/call.c

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,6 @@
88
#include "pycore_tuple.h" // _PyTuple_ITEMS()
99

1010

11-
static PyObject *const *
12-
_PyStack_UnpackDict(PyThreadState *tstate,
13-
PyObject *const *args, Py_ssize_t nargs,
14-
PyObject *kwargs, PyObject **p_kwnames);
15-
16-
static void
17-
_PyStack_UnpackDict_Free(PyObject *const *stack, Py_ssize_t nargs,
18-
PyObject *kwnames);
19-
20-
2111
static PyObject *
2212
null_error(PyThreadState *tstate)
2313
{
@@ -965,7 +955,7 @@ _PyStack_AsDict(PyObject *const *values, PyObject *kwnames)
965955
The newly allocated argument vector supports PY_VECTORCALL_ARGUMENTS_OFFSET.
966956
967957
When done, you must call _PyStack_UnpackDict_Free(stack, nargs, kwnames) */
968-
static PyObject *const *
958+
PyObject *const *
969959
_PyStack_UnpackDict(PyThreadState *tstate,
970960
PyObject *const *args, Py_ssize_t nargs,
971961
PyObject *kwargs, PyObject **p_kwnames)
@@ -1034,14 +1024,20 @@ _PyStack_UnpackDict(PyThreadState *tstate,
10341024
return stack;
10351025
}
10361026

1037-
static void
1027+
void
10381028
_PyStack_UnpackDict_Free(PyObject *const *stack, Py_ssize_t nargs,
10391029
PyObject *kwnames)
10401030
{
10411031
Py_ssize_t n = PyTuple_GET_SIZE(kwnames) + nargs;
10421032
for (Py_ssize_t i = 0; i < n; i++) {
10431033
Py_DECREF(stack[i]);
10441034
}
1035+
_PyStack_UnpackDict_FreeNoDecRef(stack, kwnames);
1036+
}
1037+
1038+
void
1039+
_PyStack_UnpackDict_FreeNoDecRef(PyObject *const *stack, PyObject *kwnames)
1040+
{
10451041
PyMem_Free((PyObject **)stack - 1);
10461042
Py_DECREF(kwnames);
10471043
}

Python/bytecodes.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3103,6 +3103,25 @@ dummy_func(
31033103
}
31043104
}
31053105
else {
3106+
if (Py_TYPE(func) == &PyFunction_Type &&
3107+
tstate->interp->eval_frame == NULL &&
3108+
((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) {
3109+
assert(PyTuple_CheckExact(callargs));
3110+
Py_ssize_t nargs = PyTuple_GET_SIZE(callargs);
3111+
int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags;
3112+
PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func));
3113+
3114+
_PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex(tstate,
3115+
(PyFunctionObject *)func, locals,
3116+
nargs, callargs, kwargs);
3117+
// Need to manually shrink the stack since we exit with DISPATCH_INLINED.
3118+
STACK_SHRINK(oparg + 3);
3119+
if (new_frame == NULL) {
3120+
goto error;
3121+
}
3122+
frame->return_offset = 0;
3123+
DISPATCH_INLINED(new_frame);
3124+
}
31063125
result = PyObject_Call(func, callargs, kwargs);
31073126
}
31083127
DECREF_INPUTS();

Python/ceval.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ static _PyInterpreterFrame *
212212
_PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func,
213213
PyObject *locals, PyObject* const* args,
214214
size_t argcount, PyObject *kwnames);
215+
static _PyInterpreterFrame *
216+
_PyEvalFramePushAndInit_Ex(PyThreadState *tstate, PyFunctionObject *func,
217+
PyObject *locals, Py_ssize_t nargs, PyObject *callargs, PyObject *kwargs);
215218
static void
216219
_PyEvalFrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame);
217220

@@ -1501,6 +1504,49 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func,
15011504
return NULL;
15021505
}
15031506

1507+
/* Same as _PyEvalFramePushAndInit but takes an args tuple and kwargs dict.
1508+
Steals references to func, callargs and kwargs.
1509+
*/
1510+
static _PyInterpreterFrame *
1511+
_PyEvalFramePushAndInit_Ex(PyThreadState *tstate, PyFunctionObject *func,
1512+
PyObject *locals, Py_ssize_t nargs, PyObject *callargs, PyObject *kwargs)
1513+
{
1514+
bool has_dict = (kwargs != NULL && PyDict_GET_SIZE(kwargs) > 0);
1515+
PyObject *kwnames = NULL;
1516+
PyObject *const *newargs;
1517+
if (has_dict) {
1518+
newargs = _PyStack_UnpackDict(tstate, _PyTuple_ITEMS(callargs), nargs, kwargs, &kwnames);
1519+
if (newargs == NULL) {
1520+
Py_DECREF(func);
1521+
goto error;
1522+
}
1523+
}
1524+
else {
1525+
newargs = &PyTuple_GET_ITEM(callargs, 0);
1526+
/* We need to incref all our args since the new frame steals the references. */
1527+
for (Py_ssize_t i = 0; i < nargs; ++i) {
1528+
Py_INCREF(PyTuple_GET_ITEM(callargs, i));
1529+
}
1530+
}
1531+
_PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit(
1532+
tstate, (PyFunctionObject *)func, locals,
1533+
newargs, nargs, kwnames
1534+
);
1535+
if (has_dict) {
1536+
_PyStack_UnpackDict_FreeNoDecRef(newargs, kwnames);
1537+
}
1538+
/* No need to decref func here because the reference has been stolen by
1539+
_PyEvalFramePushAndInit.
1540+
*/
1541+
Py_DECREF(callargs);
1542+
Py_XDECREF(kwargs);
1543+
return new_frame;
1544+
error:
1545+
Py_DECREF(callargs);
1546+
Py_XDECREF(kwargs);
1547+
return NULL;
1548+
}
1549+
15041550
PyObject *
15051551
_PyEval_Vector(PyThreadState *tstate, PyFunctionObject *func,
15061552
PyObject *locals,

0 commit comments

Comments
 (0)
0