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
122 changes: 119 additions & 3 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,128 @@ updates:
# Cooldowns protect against supply chain attacks by avoiding the
# highest-risk window immediately after new releases.
default-days: 14

- package-ecosystem: "pip"
directory: "/Tools/"
schedule:
interval: "quarterly"
labels:
- "skip issue"
- "skip news"
labels: ["skip issue", "skip news"]
groups:
pip:
patterns: ["*"]
cooldown:
default-days: 14

# Release branches: Dependabot only reads this file from the default
# branch, so each branch that should get its own actions bumps needs an
# entry here with `target-branch`. Add one when a new release branch is
# created, and remove when the branch reaches end-of-life.
- package-ecosystem: "github-actions"
target-branch: "3.15"
directory: "/"
schedule:
interval: "quarterly"
labels: ["skip issue", "skip news"]
groups:
actions:
patterns: ["*"]
cooldown:
default-days: 14

- package-ecosystem: "github-actions"
target-branch: "3.14"
directory: "/"
schedule:
interval: "quarterly"
labels: ["skip issue", "skip news"]
groups:
actions:
patterns: ["*"]
cooldown:
default-days: 14

- package-ecosystem: "github-actions"
target-branch: "3.13"
directory: "/"
schedule:
interval: "quarterly"
labels: ["skip issue", "skip news"]
groups:
actions:
patterns: ["*"]
cooldown:
default-days: 14

- package-ecosystem: "github-actions"
target-branch: "3.12"
directory: "/"
schedule:
interval: "quarterly"
labels: ["skip issue", "skip news"]
groups:
actions:
patterns: ["*"]
cooldown:
default-days: 14

- package-ecosystem: "github-actions"
target-branch: "3.11"
directory: "/"
schedule:
interval: "quarterly"
labels: ["skip issue", "skip news"]
groups:
actions:
patterns: ["*"]
cooldown:
default-days: 14

- package-ecosystem: "github-actions"
target-branch: "3.10"
directory: "/"
schedule:
interval: "quarterly"
labels: ["skip issue", "skip news"]
groups:
actions:
patterns: ["*"]
cooldown:
default-days: 14

# Only bump bugfix branches for pip. Remove
# the entry when branch goes security-only.
- package-ecosystem: "pip"
target-branch: "3.15"
directory: "/Tools/"
schedule:
interval: "quarterly"
labels: ["skip issue", "skip news"]
groups:
pip:
patterns: ["*"]
cooldown:
default-days: 14

- package-ecosystem: "pip"
target-branch: "3.14"
directory: "/Tools/"
schedule:
interval: "quarterly"
labels: ["skip issue", "skip news"]
groups:
pip:
patterns: ["*"]
cooldown:
default-days: 14

- package-ecosystem: "pip"
target-branch: "3.13"
directory: "/Tools/"
schedule:
interval: "quarterly"
labels: ["skip issue", "skip news"]
groups:
pip:
patterns: ["*"]
cooldown:
default-days: 14
16 changes: 16 additions & 0 deletions Include/internal/pycore_bytesobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,22 @@ extern void _PyBytes_CheckOverflow(
const char *type_name);
#endif


// Return the cached hash value, or -1 if not cached yet.
static inline Py_hash_t
_PyBytes_GET_CACHED_HASH(PyBytesObject *self)
{
_Py_COMP_DIAG_PUSH
_Py_COMP_DIAG_IGNORE_DEPR_DECLS
#ifdef Py_GIL_DISABLED
return _Py_atomic_load_ssize_relaxed(&self->ob_shash);
#else
return self->ob_shash;
#endif
_Py_COMP_DIAG_POP
}


/* --- PyBytesWriter ------------------------------------------------------ */

struct PyBytesWriter {
Expand Down
26 changes: 7 additions & 19 deletions Lib/test/dtracedata/call_stack.stp
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,21 @@ function basename:string(path:string)
return last_token;
}

probe @PYTHON_SYSTEMTAP_PROBE@("function__entry")
{
funcname = user_string($arg2);

if (funcname == "start") {
tracing = 1;
}
}

probe @PYTHON_SYSTEMTAP_PROBE@("function__entry"),
@PYTHON_SYSTEMTAP_PROBE@("function__return")
{
filename = user_string($arg1);
funcname = user_string($arg2);
lineno = $arg3;

if (tracing) {
if (funcname == "start") {
if ($$name == "function__entry") {
tracing = 1;
} else {
tracing = 0;
}
} else if (tracing) {
printf("%d\t%s:%s:%s:%d\n", gettimeofday_us(), $$name,
basename(filename), funcname, lineno);
}
}

probe @PYTHON_SYSTEMTAP_PROBE@("function__return")
{
funcname = user_string($arg2);

if (funcname == "start") {
tracing = 0;
}
}
58 changes: 55 additions & 3 deletions Lib/test/test_bytes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
"""

import array
import codecs
import contextlib
import copy
import functools
import operator
import os
import pickle
import re
import sys
import copy
import functools
import pickle
import tempfile
import textwrap
import threading
Expand Down Expand Up @@ -1146,6 +1147,34 @@ def test_translate(self):
c = b.translate(None, delete=b'e')
self.assertEqual(c, b'hllo')

# short inputs starting with NUL bytes
table = bytes.maketrans(b'\x00', b'Z')
for data in b'\x00', b'\x00' * 8, b'\x00' * 8 + b'a' * 247:
c = self.type2test(data).translate(table)
self.assertEqual(c, data.replace(b'\x00', b'Z'))

@support.cpython_only
def test_translate_unchanged(self):
if self.type2test != bytes:
self.skipTest("test specific bytes.translate()")

# bytes.translate() returns the input string unchanged
# if no byte is modified
size = 1024
b = b'hell' + b'o' * size
rosetta = bytearray(range(256))
rosetta[ord('#')] = ord('?')
self.assertIs(b.translate(rosetta), b)

# bytes.translate() always create a new object
# if the input string is a bytes subclass
class bytes_subclass(bytes):
pass
b = bytes_subclass(b)
result = b.translate(rosetta)
self.assertIsNot(result, b)
self.assertEqual(result, b)

def test_sq_item(self):
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
obj = self.type2test((42,))
Expand Down Expand Up @@ -1706,6 +1735,29 @@ def test_take_bytes_optimization(self):
bytes_header_size = sys.getsizeof(b'')
self.assertEqual(ba.__alloc__(), 499 + bytes_header_size)

def test_take_bytes_hash(self):
# gh-158219: bytearray constructor must not use a bytes object
# if its hash value is already cached.

def encode(string, errors='strict'):
encoded = string.encode('utf-8')
hash(encoded) # a codec may hash its own output
return encoded, len(string)

def hashing_codec(name):
if name != 'test_take_bytes_hash':
return None
return codecs.CodecInfo(encode, None, name=name)

codecs.register(hashing_codec)
self.addCleanup(codecs.unregister, hashing_codec)

ba = bytearray('hello', 'test_take_bytes_hash')
ba[0] = ord('H')
taken = ba.take_bytes()
self.assertEqual(taken, b'Hello')
self.assertEqual(hash(taken), hash(b'Hello'))

def test_take_bytes_reentrant_resize(self):
# gh-153570: n.__index__() can resize the bytearray, so take_bytes()
# must re-read the size afterwards. It cached the size before the
Expand Down
20 changes: 20 additions & 0 deletions Lib/test/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,26 @@ def test_no_ending_newline(self):
def test_empty(self):
compile("", "<test>", "exec")

@support.requires_subprocess()
@support.nomemtest
def test_assemble_init_allocation_failure(self):
# gh-158241: Check error handling on MemoryError in Python/assemble.c
code = textwrap.dedent("""
from test import support

failures = 0
for n in range(1, 100):
with support.inject_memory_error_cm(n, n + 1):
try:
compile("x", "<test>", "exec")
except MemoryError:
failures += 1

if failures == 0:
raise AssertionError
""")
script_helper.assert_python_ok('-c', code)

def test_other_newlines(self):
compile("\r\n", "<test>", "exec")
compile("\r", "<test>", "exec")
Expand Down
35 changes: 35 additions & 0 deletions Lib/test/test_unittest/testmock/testpatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import os
import sys
from collections import OrderedDict
from types import LazyImportType, ModuleType

import unittest
import test
Expand Down Expand Up @@ -2101,5 +2102,39 @@ def test(_):
test()


class PatchLazyImportTest(unittest.TestCase):

def lazy_module(self):
# `lazy from` binds eagerly when the module it imports from is already
# imported, so publish the target only once the statement has run.
self.enterContext(uncache('lazy_patch_user', 'lazy_patch_target'))
user = ModuleType('lazy_patch_user')
exec('lazy from lazy_patch_target import function', user.__dict__)
sys.modules['lazy_patch_user'] = user
target = ModuleType('lazy_patch_target')
exec('def function(arg): pass', target.__dict__)
sys.modules['lazy_patch_target'] = target

self.assertIsInstance(user.__dict__['function'], LazyImportType)
return user

def test_autospec(self):
module = self.lazy_module()
with patch.object(module, 'function', autospec=True) as mock_function:
mock_function('arg')
with self.assertRaises(TypeError):
mock_function('arg', 'extra')

def test_spec(self):
module = self.lazy_module()
with patch.object(module, 'function', spec=True) as mock_function:
mock_function('arg')

def test_spec_set(self):
module = self.lazy_module()
with patch.object(module, 'function', spec_set=True) as mock_function:
mock_function('arg')


if __name__ == '__main__':
unittest.main()
6 changes: 5 additions & 1 deletion Lib/unittest/mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import threading
from annotationlib import Format
from dataclasses import fields, is_dataclass
from types import CodeType, ModuleType, MethodType
from types import CodeType, LazyImportType, ModuleType, MethodType
from unittest.util import safe_repr
from functools import wraps, partial
from threading import RLock
Expand Down Expand Up @@ -1507,6 +1507,10 @@ def __enter__(self):

original, local = self.get_original()

if (isinstance(original, LazyImportType)
and (spec is True or spec_set is True or autospec is True)):
original = original.resolve()

if new is DEFAULT and autospec is None:
inherit = False
if spec is True:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix a crash and possible code object corruption following a
:exc:`MemoryError` while compiling Python code.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix :class:`bytearray` constructor: do not use a bytes object if its hash
value is already computed. Patch by Cody Maloney and Victor Stinner.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix :func:`unittest.mock.patch` with ``autospec``, ``spec``, or ``spec_set``
when used with lazy-imported objects.
Loading
Loading