aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/test/support
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/support')
-rw-r--r--Lib/test/support/__init__.py13
-rw-r--r--Lib/test/support/hashlib_helper.py221
-rw-r--r--Lib/test/support/import_helper.py2
-rw-r--r--Lib/test/support/interpreters/channels.py89
-rw-r--r--Lib/test/support/interpreters/queues.py145
-rw-r--r--Lib/test/support/strace_helper.py7
-rw-r--r--Lib/test/support/warnings_helper.py3
7 files changed, 342 insertions, 138 deletions
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index c74c3a31909..351d832a26d 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -696,9 +696,11 @@ def sortdict(dict):
return "{%s}" % withcommas
-def run_code(code: str) -> dict[str, object]:
+def run_code(code: str, extra_names: dict[str, object] | None = None) -> dict[str, object]:
"""Run a piece of code after dedenting it, and return its global namespace."""
ns = {}
+ if extra_names:
+ ns.update(extra_names)
exec(textwrap.dedent(code), ns)
return ns
@@ -1099,7 +1101,6 @@ class _MemoryWatchdog:
self.started = False
def start(self):
- import warnings
try:
f = open(self.procfile, 'r')
except OSError as e:
@@ -2726,7 +2727,7 @@ def iter_builtin_types():
# Fall back to making a best-effort guess.
if hasattr(object, '__flags__'):
# Look for any type object with the Py_TPFLAGS_STATIC_BUILTIN flag set.
- import datetime
+ import datetime # noqa: F401
seen = set()
for cls, subs in walk_class_hierarchy(object):
if cls in seen:
@@ -2927,12 +2928,6 @@ def make_clean_env() -> dict[str, str]:
return clean_env
-def initialized_with_pyrepl():
- """Detect whether PyREPL was used during Python initialization."""
- # If the main module has a __file__ attribute it's a Python module, which means PyREPL.
- return hasattr(sys.modules["__main__"], "__file__")
-
-
WINDOWS_STATUS = {
0xC0000005: "STATUS_ACCESS_VIOLATION",
0xC00000FD: "STATUS_STACK_OVERFLOW",
diff --git a/Lib/test/support/hashlib_helper.py b/Lib/test/support/hashlib_helper.py
index 5043f08dd93..7032257b068 100644
--- a/Lib/test/support/hashlib_helper.py
+++ b/Lib/test/support/hashlib_helper.py
@@ -23,6 +23,22 @@ def requires_builtin_hmac():
return unittest.skipIf(_hmac is None, "requires _hmac")
+def _missing_hash(digestname, implementation=None, *, exc=None):
+ parts = ["missing", implementation, f"hash algorithm: {digestname!r}"]
+ msg = " ".join(filter(None, parts))
+ raise unittest.SkipTest(msg) from exc
+
+
+def _openssl_availabillity(digestname, *, usedforsecurity):
+ try:
+ _hashlib.new(digestname, usedforsecurity=usedforsecurity)
+ except AttributeError:
+ assert _hashlib is None
+ _missing_hash(digestname, "OpenSSL")
+ except ValueError as exc:
+ _missing_hash(digestname, "OpenSSL", exc=exc)
+
+
def _decorate_func_or_class(func_or_class, decorator_func):
if not isinstance(func_or_class, type):
return decorator_func(func_or_class)
@@ -71,8 +87,7 @@ def requires_hashdigest(digestname, openssl=None, usedforsecurity=True):
try:
test_availability()
except ValueError as exc:
- msg = f"missing hash algorithm: {digestname!r}"
- raise unittest.SkipTest(msg) from exc
+ _missing_hash(digestname, exc=exc)
return func(*args, **kwargs)
return wrapper
@@ -87,14 +102,44 @@ def requires_openssl_hashdigest(digestname, *, usedforsecurity=True):
The hashing algorithm may be missing or blocked by a strict crypto policy.
"""
def decorator_func(func):
- @requires_hashlib()
+ @requires_hashlib() # avoid checking at each call
@functools.wraps(func)
def wrapper(*args, **kwargs):
+ _openssl_availabillity(digestname, usedforsecurity=usedforsecurity)
+ return func(*args, **kwargs)
+ return wrapper
+
+ def decorator(func_or_class):
+ return _decorate_func_or_class(func_or_class, decorator_func)
+ return decorator
+
+
+def find_openssl_hashdigest_constructor(digestname, *, usedforsecurity=True):
+ """Find the OpenSSL hash function constructor by its name."""
+ assert isinstance(digestname, str), digestname
+ _openssl_availabillity(digestname, usedforsecurity=usedforsecurity)
+ # This returns a function of the form _hashlib.openssl_<name> and
+ # not a lambda function as it is rejected by _hashlib.hmac_new().
+ return getattr(_hashlib, f"openssl_{digestname}")
+
+
+def requires_builtin_hashdigest(
+ module_name, digestname, *, usedforsecurity=True
+):
+ """Decorator raising SkipTest if a HACL* hashing algorithm is missing.
+
+ - The *module_name* is the C extension module name based on HACL*.
+ - The *digestname* is one of its member, e.g., 'md5'.
+ """
+ def decorator_func(func):
+ @functools.wraps(func)
+ def wrapper(*args, **kwargs):
+ module = import_module(module_name)
try:
- _hashlib.new(digestname, usedforsecurity=usedforsecurity)
- except ValueError:
- msg = f"missing OpenSSL hash algorithm: {digestname!r}"
- raise unittest.SkipTest(msg)
+ getattr(module, digestname)
+ except AttributeError:
+ fullname = f'{module_name}.{digestname}'
+ _missing_hash(fullname, implementation="HACL")
return func(*args, **kwargs)
return wrapper
@@ -103,6 +148,168 @@ def requires_openssl_hashdigest(digestname, *, usedforsecurity=True):
return decorator
+def find_builtin_hashdigest_constructor(
+ module_name, digestname, *, usedforsecurity=True
+):
+ """Find the HACL* hash function constructor.
+
+ - The *module_name* is the C extension module name based on HACL*.
+ - The *digestname* is one of its member, e.g., 'md5'.
+ """
+ module = import_module(module_name)
+ try:
+ constructor = getattr(module, digestname)
+ constructor(b'', usedforsecurity=usedforsecurity)
+ except (AttributeError, TypeError, ValueError):
+ _missing_hash(f'{module_name}.{digestname}', implementation="HACL")
+ return constructor
+
+
+class HashFunctionsTrait:
+ """Mixin trait class containing hash functions.
+
+ This class is assumed to have all unitest.TestCase methods but should
+ not directly inherit from it to prevent the test suite being run on it.
+
+ Subclasses should implement the hash functions by returning an object
+ that can be recognized as a valid digestmod parameter for both hashlib
+ and HMAC. In particular, it cannot be a lambda function as it will not
+ be recognized by hashlib (it will still be accepted by the pure Python
+ implementation of HMAC).
+ """
+
+ ALGORITHMS = [
+ 'md5', 'sha1',
+ 'sha224', 'sha256', 'sha384', 'sha512',
+ 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512',
+ ]
+
+ # Default 'usedforsecurity' to use when looking up a hash function.
+ usedforsecurity = True
+
+ def _find_constructor(self, name):
+ # By default, a missing algorithm skips the test that uses it.
+ self.assertIn(name, self.ALGORITHMS)
+ self.skipTest(f"missing hash function: {name}")
+
+ @property
+ def md5(self):
+ return self._find_constructor("md5")
+
+ @property
+ def sha1(self):
+ return self._find_constructor("sha1")
+
+ @property
+ def sha224(self):
+ return self._find_constructor("sha224")
+
+ @property
+ def sha256(self):
+ return self._find_constructor("sha256")
+
+ @property
+ def sha384(self):
+ return self._find_constructor("sha384")
+
+ @property
+ def sha512(self):
+ return self._find_constructor("sha512")
+
+ @property
+ def sha3_224(self):
+ return self._find_constructor("sha3_224")
+
+ @property
+ def sha3_256(self):
+ return self._find_constructor("sha3_256")
+
+ @property
+ def sha3_384(self):
+ return self._find_constructor("sha3_384")
+
+ @property
+ def sha3_512(self):
+ return self._find_constructor("sha3_512")
+
+
+class NamedHashFunctionsTrait(HashFunctionsTrait):
+ """Trait containing named hash functions.
+
+ Hash functions are available if and only if they are available in hashlib.
+ """
+
+ def _find_constructor(self, name):
+ self.assertIn(name, self.ALGORITHMS)
+ return name
+
+
+class OpenSSLHashFunctionsTrait(HashFunctionsTrait):
+ """Trait containing OpenSSL hash functions.
+
+ Hash functions are available if and only if they are available in _hashlib.
+ """
+
+ def _find_constructor(self, name):
+ self.assertIn(name, self.ALGORITHMS)
+ return find_openssl_hashdigest_constructor(
+ name, usedforsecurity=self.usedforsecurity
+ )
+
+
+class BuiltinHashFunctionsTrait(HashFunctionsTrait):
+ """Trait containing HACL* hash functions.
+
+ Hash functions are available if and only if they are available in C.
+ In particular, HACL* HMAC-MD5 may be available even though HACL* md5
+ is not since the former is unconditionally built.
+ """
+
+ def _find_constructor_in(self, module, name):
+ self.assertIn(name, self.ALGORITHMS)
+ return find_builtin_hashdigest_constructor(module, name)
+
+ @property
+ def md5(self):
+ return self._find_constructor_in("_md5", "md5")
+
+ @property
+ def sha1(self):
+ return self._find_constructor_in("_sha1", "sha1")
+
+ @property
+ def sha224(self):
+ return self._find_constructor_in("_sha2", "sha224")
+
+ @property
+ def sha256(self):
+ return self._find_constructor_in("_sha2", "sha256")
+
+ @property
+ def sha384(self):
+ return self._find_constructor_in("_sha2", "sha384")
+
+ @property
+ def sha512(self):
+ return self._find_constructor_in("_sha2", "sha512")
+
+ @property
+ def sha3_224(self):
+ return self._find_constructor_in("_sha3", "sha3_224")
+
+ @property
+ def sha3_256(self):
+ return self._find_constructor_in("_sha3","sha3_256")
+
+ @property
+ def sha3_384(self):
+ return self._find_constructor_in("_sha3","sha3_384")
+
+ @property
+ def sha3_512(self):
+ return self._find_constructor_in("_sha3","sha3_512")
+
+
def find_gil_minsize(modules_names, default=2048):
"""Get the largest GIL_MINSIZE value for the given cryptographic modules.
diff --git a/Lib/test/support/import_helper.py b/Lib/test/support/import_helper.py
index edb734d294f..0af63501f93 100644
--- a/Lib/test/support/import_helper.py
+++ b/Lib/test/support/import_helper.py
@@ -438,5 +438,5 @@ def ensure_module_imported(name, *, clearnone=True):
if sys.modules.get(name) is not None:
mod = sys.modules[name]
else:
- mod, _, _ = _force_import(name, False, True, clearnone)
+ mod, _, _ = _ensure_module(name, False, True, clearnone)
return mod
diff --git a/Lib/test/support/interpreters/channels.py b/Lib/test/support/interpreters/channels.py
index d2bd93d77f7..1724759b75a 100644
--- a/Lib/test/support/interpreters/channels.py
+++ b/Lib/test/support/interpreters/channels.py
@@ -6,8 +6,8 @@ from . import _crossinterp
# aliases:
from _interpchannels import (
- ChannelError, ChannelNotFoundError, ChannelClosedError,
- ChannelEmptyError, ChannelNotEmptyError,
+ ChannelError, ChannelNotFoundError, ChannelClosedError, # noqa: F401
+ ChannelEmptyError, ChannelNotEmptyError, # noqa: F401
)
from ._crossinterp import (
UNBOUND_ERROR, UNBOUND_REMOVE,
@@ -55,15 +55,23 @@ def create(*, unbounditems=UNBOUND):
"""
unbound = _serialize_unbound(unbounditems)
unboundop, = unbound
- cid = _channels.create(unboundop)
- recv, send = RecvChannel(cid), SendChannel(cid, _unbound=unbound)
+ cid = _channels.create(unboundop, -1)
+ recv, send = RecvChannel(cid), SendChannel(cid)
+ send._set_unbound(unboundop, unbounditems)
return recv, send
def list_all():
"""Return a list of (recv, send) for all open channels."""
- return [(RecvChannel(cid), SendChannel(cid, _unbound=unbound))
- for cid, unbound in _channels.list_all()]
+ channels = []
+ for cid, unboundop, _ in _channels.list_all():
+ chan = _, send = RecvChannel(cid), SendChannel(cid)
+ if not hasattr(send, '_unboundop'):
+ send._set_unbound(unboundop)
+ else:
+ assert send._unbound[0] == unboundop
+ channels.append(chan)
+ return channels
class _ChannelEnd:
@@ -175,16 +183,33 @@ class SendChannel(_ChannelEnd):
_end = 'send'
- def __new__(cls, cid, *, _unbound=None):
- if _unbound is None:
- try:
- op = _channels.get_channel_defaults(cid)
- _unbound = (op,)
- except ChannelNotFoundError:
- _unbound = _serialize_unbound(UNBOUND)
- self = super().__new__(cls, cid)
- self._unbound = _unbound
- return self
+# def __new__(cls, cid, *, _unbound=None):
+# if _unbound is None:
+# try:
+# op = _channels.get_channel_defaults(cid)
+# _unbound = (op,)
+# except ChannelNotFoundError:
+# _unbound = _serialize_unbound(UNBOUND)
+# self = super().__new__(cls, cid)
+# self._unbound = _unbound
+# return self
+
+ def _set_unbound(self, op, items=None):
+ assert not hasattr(self, '_unbound')
+ if items is None:
+ items = _resolve_unbound(op)
+ unbound = (op, items)
+ self._unbound = unbound
+ return unbound
+
+ @property
+ def unbounditems(self):
+ try:
+ _, items = self._unbound
+ except AttributeError:
+ op, _ = _channels.get_queue_defaults(self._id)
+ _, items = self._set_unbound(op)
+ return items
@property
def is_closed(self):
@@ -192,61 +217,61 @@ class SendChannel(_ChannelEnd):
return info.closed or info.closing
def send(self, obj, timeout=None, *,
- unbound=None,
+ unbounditems=None,
):
"""Send the object (i.e. its data) to the channel's receiving end.
This blocks until the object is received.
"""
- if unbound is None:
- unboundop, = self._unbound
+ if unbounditems is None:
+ unboundop = -1
else:
- unboundop, = _serialize_unbound(unbound)
+ unboundop, = _serialize_unbound(unbounditems)
_channels.send(self._id, obj, unboundop, timeout=timeout, blocking=True)
def send_nowait(self, obj, *,
- unbound=None,
+ unbounditems=None,
):
"""Send the object to the channel's receiving end.
If the object is immediately received then return True
(else False). Otherwise this is the same as send().
"""
- if unbound is None:
- unboundop, = self._unbound
+ if unbounditems is None:
+ unboundop = -1
else:
- unboundop, = _serialize_unbound(unbound)
+ unboundop, = _serialize_unbound(unbounditems)
# XXX Note that at the moment channel_send() only ever returns
# None. This should be fixed when channel_send_wait() is added.
# See bpo-32604 and gh-19829.
return _channels.send(self._id, obj, unboundop, blocking=False)
def send_buffer(self, obj, timeout=None, *,
- unbound=None,
+ unbounditems=None,
):
"""Send the object's buffer to the channel's receiving end.
This blocks until the object is received.
"""
- if unbound is None:
- unboundop, = self._unbound
+ if unbounditems is None:
+ unboundop = -1
else:
- unboundop, = _serialize_unbound(unbound)
+ unboundop, = _serialize_unbound(unbounditems)
_channels.send_buffer(self._id, obj, unboundop,
timeout=timeout, blocking=True)
def send_buffer_nowait(self, obj, *,
- unbound=None,
+ unbounditems=None,
):
"""Send the object's buffer to the channel's receiving end.
If the object is immediately received then return True
(else False). Otherwise this is the same as send().
"""
- if unbound is None:
- unboundop, = self._unbound
+ if unbounditems is None:
+ unboundop = -1
else:
- unboundop, = _serialize_unbound(unbound)
+ unboundop, = _serialize_unbound(unbounditems)
return _channels.send_buffer(self._id, obj, unboundop, blocking=False)
def close(self):
diff --git a/Lib/test/support/interpreters/queues.py b/Lib/test/support/interpreters/queues.py
index deb8e8613af..99987f2f692 100644
--- a/Lib/test/support/interpreters/queues.py
+++ b/Lib/test/support/interpreters/queues.py
@@ -1,6 +1,5 @@
"""Cross-interpreter Queues High Level Module."""
-import pickle
import queue
import time
import weakref
@@ -63,29 +62,34 @@ def _resolve_unbound(flag):
return resolved
-def create(maxsize=0, *, syncobj=False, unbounditems=UNBOUND):
+def create(maxsize=0, *, unbounditems=UNBOUND):
"""Return a new cross-interpreter queue.
The queue may be used to pass data safely between interpreters.
- "syncobj" sets the default for Queue.put()
- and Queue.put_nowait().
-
- "unbounditems" likewise sets the default. See Queue.put() for
+ "unbounditems" sets the default for Queue.put(); see that method for
supported values. The default value is UNBOUND, which replaces
the unbound item.
"""
- fmt = _SHARED_ONLY if syncobj else _PICKLED
unbound = _serialize_unbound(unbounditems)
unboundop, = unbound
- qid = _queues.create(maxsize, fmt, unboundop)
- return Queue(qid, _fmt=fmt, _unbound=unbound)
+ qid = _queues.create(maxsize, unboundop, -1)
+ self = Queue(qid)
+ self._set_unbound(unboundop, unbounditems)
+ return self
def list_all():
"""Return a list of all open queues."""
- return [Queue(qid, _fmt=fmt, _unbound=(unboundop,))
- for qid, fmt, unboundop in _queues.list_all()]
+ queues = []
+ for qid, unboundop, _ in _queues.list_all():
+ self = Queue(qid)
+ if not hasattr(self, '_unbound'):
+ self._set_unbound(unboundop)
+ else:
+ assert self._unbound[0] == unboundop
+ queues.append(self)
+ return queues
_known_queues = weakref.WeakValueDictionary()
@@ -93,28 +97,17 @@ _known_queues = weakref.WeakValueDictionary()
class Queue:
"""A cross-interpreter queue."""
- def __new__(cls, id, /, *, _fmt=None, _unbound=None):
+ def __new__(cls, id, /):
# There is only one instance for any given ID.
if isinstance(id, int):
id = int(id)
else:
raise TypeError(f'id must be an int, got {id!r}')
- if _fmt is None:
- if _unbound is None:
- _fmt, op = _queues.get_queue_defaults(id)
- _unbound = (op,)
- else:
- _fmt, _ = _queues.get_queue_defaults(id)
- elif _unbound is None:
- _, op = _queues.get_queue_defaults(id)
- _unbound = (op,)
try:
self = _known_queues[id]
except KeyError:
self = super().__new__(cls)
self._id = id
- self._fmt = _fmt
- self._unbound = _unbound
_known_queues[id] = self
_queues.bind(id)
return self
@@ -143,11 +136,28 @@ class Queue:
def __getstate__(self):
return None
+ def _set_unbound(self, op, items=None):
+ assert not hasattr(self, '_unbound')
+ if items is None:
+ items = _resolve_unbound(op)
+ unbound = (op, items)
+ self._unbound = unbound
+ return unbound
+
@property
def id(self):
return self._id
@property
+ def unbounditems(self):
+ try:
+ _, items = self._unbound
+ except AttributeError:
+ op, _ = _queues.get_queue_defaults(self._id)
+ _, items = self._set_unbound(op)
+ return items
+
+ @property
def maxsize(self):
try:
return self._maxsize
@@ -165,77 +175,56 @@ class Queue:
return _queues.get_count(self._id)
def put(self, obj, timeout=None, *,
- syncobj=None,
- unbound=None,
+ unbounditems=None,
_delay=10 / 1000, # 10 milliseconds
):
"""Add the object to the queue.
This blocks while the queue is full.
- If "syncobj" is None (the default) then it uses the
- queue's default, set with create_queue().
-
- If "syncobj" is false then all objects are supported,
- at the expense of worse performance.
-
- If "syncobj" is true then the object must be "shareable".
- Examples of "shareable" objects include the builtin singletons,
- str, and memoryview. One benefit is that such objects are
- passed through the queue efficiently.
-
- The key difference, though, is conceptual: the corresponding
- object returned from Queue.get() will be strictly equivalent
- to the given obj. In other words, the two objects will be
- effectively indistinguishable from each other, even if the
- object is mutable. The received object may actually be the
- same object, or a copy (immutable values only), or a proxy.
- Regardless, the received object should be treated as though
- the original has been shared directly, whether or not it
- actually is. That's a slightly different and stronger promise
- than just (initial) equality, which is all "syncobj=False"
- can promise.
-
- "unbound" controls the behavior of Queue.get() for the given
+ For most objects, the object received through Queue.get() will
+ be a new one, equivalent to the original and not sharing any
+ actual underlying data. The notable exceptions include
+ cross-interpreter types (like Queue) and memoryview, where the
+ underlying data is actually shared. Furthermore, some types
+ can be sent through a queue more efficiently than others. This
+ group includes various immutable types like int, str, bytes, and
+ tuple (if the items are likewise efficiently shareable). See interpreters.is_shareable().
+
+ "unbounditems" controls the behavior of Queue.get() for the given
object if the current interpreter (calling put()) is later
destroyed.
- If "unbound" is None (the default) then it uses the
+ If "unbounditems" is None (the default) then it uses the
queue's default, set with create_queue(),
which is usually UNBOUND.
- If "unbound" is UNBOUND_ERROR then get() will raise an
+ If "unbounditems" is UNBOUND_ERROR then get() will raise an
ItemInterpreterDestroyed exception if the original interpreter
has been destroyed. This does not otherwise affect the queue;
the next call to put() will work like normal, returning the next
item in the queue.
- If "unbound" is UNBOUND_REMOVE then the item will be removed
+ If "unbounditems" is UNBOUND_REMOVE then the item will be removed
from the queue as soon as the original interpreter is destroyed.
Be aware that this will introduce an imbalance between put()
and get() calls.
- If "unbound" is UNBOUND then it is returned by get() in place
+ If "unbounditems" is UNBOUND then it is returned by get() in place
of the unbound item.
"""
- if syncobj is None:
- fmt = self._fmt
- else:
- fmt = _SHARED_ONLY if syncobj else _PICKLED
- if unbound is None:
- unboundop, = self._unbound
+ if unbounditems is None:
+ unboundop = -1
else:
- unboundop, = _serialize_unbound(unbound)
+ unboundop, = _serialize_unbound(unbounditems)
if timeout is not None:
timeout = int(timeout)
if timeout < 0:
raise ValueError(f'timeout value must be non-negative')
end = time.time() + timeout
- if fmt is _PICKLED:
- obj = pickle.dumps(obj)
while True:
try:
- _queues.put(self._id, obj, fmt, unboundop)
+ _queues.put(self._id, obj, unboundop)
except QueueFull as exc:
if timeout is not None and time.time() >= end:
raise # re-raise
@@ -243,18 +232,12 @@ class Queue:
else:
break
- def put_nowait(self, obj, *, syncobj=None, unbound=None):
- if syncobj is None:
- fmt = self._fmt
+ def put_nowait(self, obj, *, unbounditems=None):
+ if unbounditems is None:
+ unboundop = -1
else:
- fmt = _SHARED_ONLY if syncobj else _PICKLED
- if unbound is None:
- unboundop, = self._unbound
- else:
- unboundop, = _serialize_unbound(unbound)
- if fmt is _PICKLED:
- obj = pickle.dumps(obj)
- _queues.put(self._id, obj, fmt, unboundop)
+ unboundop, = _serialize_unbound(unbounditems)
+ _queues.put(self._id, obj, unboundop)
def get(self, timeout=None, *,
_delay=10 / 1000, # 10 milliseconds
@@ -265,7 +248,7 @@ class Queue:
If the next item's original interpreter has been destroyed
then the "next object" is determined by the value of the
- "unbound" argument to put().
+ "unbounditems" argument to put().
"""
if timeout is not None:
timeout = int(timeout)
@@ -274,7 +257,7 @@ class Queue:
end = time.time() + timeout
while True:
try:
- obj, fmt, unboundop = _queues.get(self._id)
+ obj, unboundop = _queues.get(self._id)
except QueueEmpty as exc:
if timeout is not None and time.time() >= end:
raise # re-raise
@@ -284,10 +267,6 @@ class Queue:
if unboundop is not None:
assert obj is None, repr(obj)
return _resolve_unbound(unboundop)
- if fmt == _PICKLED:
- obj = pickle.loads(obj)
- else:
- assert fmt == _SHARED_ONLY
return obj
def get_nowait(self):
@@ -297,16 +276,12 @@ class Queue:
is the same as get().
"""
try:
- obj, fmt, unboundop = _queues.get(self._id)
+ obj, unboundop = _queues.get(self._id)
except QueueEmpty as exc:
raise # re-raise
if unboundop is not None:
assert obj is None, repr(obj)
return _resolve_unbound(unboundop)
- if fmt == _PICKLED:
- obj = pickle.loads(obj)
- else:
- assert fmt == _SHARED_ONLY
return obj
diff --git a/Lib/test/support/strace_helper.py b/Lib/test/support/strace_helper.py
index 798d6c68869..cf95f7bdc7d 100644
--- a/Lib/test/support/strace_helper.py
+++ b/Lib/test/support/strace_helper.py
@@ -38,7 +38,7 @@ class StraceResult:
This assumes the program under inspection doesn't print any non-utf8
strings which would mix into the strace output."""
- decoded_events = self.event_bytes.decode('utf-8')
+ decoded_events = self.event_bytes.decode('utf-8', 'surrogateescape')
matches = [
_syscall_regex.match(event)
for event in decoded_events.splitlines()
@@ -178,7 +178,10 @@ def get_syscalls(code, strace_flags, prelude="", cleanup="",
# Moderately expensive (spawns a subprocess), so share results when possible.
@cache
def _can_strace():
- res = strace_python("import sys; sys.exit(0)", [], check=False)
+ res = strace_python("import sys; sys.exit(0)",
+ # --trace option needs strace 5.5 (gh-133741)
+ ["--trace=%process"],
+ check=False)
if res.strace_returncode == 0 and res.python_returncode == 0:
assert res.events(), "Should have parsed multiple calls"
return True
diff --git a/Lib/test/support/warnings_helper.py b/Lib/test/support/warnings_helper.py
index a6e43dff200..5f6f14afd74 100644
--- a/Lib/test/support/warnings_helper.py
+++ b/Lib/test/support/warnings_helper.py
@@ -23,8 +23,7 @@ def check_syntax_warning(testcase, statement, errtext='',
testcase.assertEqual(len(warns), 1, warns)
warn, = warns
- testcase.assertTrue(issubclass(warn.category, SyntaxWarning),
- warn.category)
+ testcase.assertIsSubclass(warn.category, SyntaxWarning)
if errtext:
testcase.assertRegex(str(warn.message), errtext)
testcase.assertEqual(warn.filename, '<testcase>')