Update pickle.py from 3.14.2 (#6982)

* Update `_compat_pickle.py` from 3.14.2

* Update `pickle.py` from 3.14.2

* Update pickletools and tests

* Update all other pickle related files

* Make `test_extcall` to use modified doctest checker
This commit is contained in:
Shahar Naveh authored and GitHub committed 2026-02-05 19:27:10 +09:00
1 parent 48e4e7c661
commit bbb6c66a29
9 files changed
+5649 -396

No files matched your search

+1 -1
View File
@@ -22,7 +22,6 @@ IMPORT_MAPPING = {
'tkMessageBox': 'tkinter.messagebox',
'ScrolledText': 'tkinter.scrolledtext',
'Tkconstants': 'tkinter.constants',
'Tix': 'tkinter.tix',
'ttk': 'tkinter.ttk',
'Tkinter': 'tkinter',
'markupbase': '_markupbase',
@@ -257,3 +256,4 @@ PYTHON3_IMPORTERROR_EXCEPTIONS = (
for excname in PYTHON3_IMPORTERROR_EXCEPTIONS:
REVERSE_NAME_MAPPING[('builtins', excname)] = ('exceptions', 'ImportError')
del excname
+242 -177
View File
@@ -26,12 +26,11 @@ Misc variables:
from types import FunctionType
from copyreg import dispatch_table
from copyreg import _extension_registry, _inverted_registry, _extension_cache
from itertools import islice
from itertools import batched
from functools import partial
import sys
from sys import maxsize
from struct import pack, unpack
import re
import io
import codecs
import _compat_pickle
@@ -51,7 +50,7 @@ except ImportError:
bytes_types = (bytes, bytearray)
# These are purely informational; no code uses these.
format_version = "4.0" # File format version we write
format_version = "5.0" # File format version we write
compatible_formats = ["1.0", # Original protocol 0
"1.1", # Protocol 0 with INST added
"1.2", # Original protocol 1
@@ -68,7 +67,7 @@ HIGHEST_PROTOCOL = 5
# The protocol we write by default. May be less than HIGHEST_PROTOCOL.
# Only bump this if the oldest still supported version of Python already
# includes it.
DEFAULT_PROTOCOL = 4
DEFAULT_PROTOCOL = 5
class PickleError(Exception):
"""A common base class for the other pickling exceptions."""
@@ -188,7 +187,7 @@ BYTEARRAY8 = b'\x96' # push bytearray
NEXT_BUFFER = b'\x97' # push next out-of-band buffer
READONLY_BUFFER = b'\x98' # make top of stack readonly
__all__.extend([x for x in dir() if re.match("[A-Z][A-Z0-9_]+$", x)])
__all__.extend(x for x in dir() if x.isupper() and not x.startswith('_'))
class _Framer:
@@ -313,38 +312,46 @@ class _Unframer:
# Tools used for pickling.
def _getattribute(obj, name):
top = obj
for subpath in name.split('.'):
if subpath == '<locals>':
raise AttributeError("Can't get local attribute {!r} on {!r}"
.format(name, top))
try:
parent = obj
obj = getattr(obj, subpath)
except AttributeError:
raise AttributeError("Can't get attribute {!r} on {!r}"
.format(name, top)) from None
return obj, parent
def _getattribute(obj, dotted_path):
for subpath in dotted_path:
obj = getattr(obj, subpath)
return obj
def whichmodule(obj, name):
"""Find the module an object belong to."""
dotted_path = name.split('.')
module_name = getattr(obj, '__module__', None)
if module_name is not None:
return module_name
# Protect the iteration by using a list copy of sys.modules against dynamic
# modules that trigger imports of other modules upon calls to getattr.
for module_name, module in sys.modules.copy().items():
if (module_name == '__main__'
or module_name == '__mp_main__' # bpo-42406
or module is None):
continue
try:
if _getattribute(module, name)[0] is obj:
return module_name
except AttributeError:
pass
return '__main__'
if '<locals>' in dotted_path:
raise PicklingError(f"Can't pickle local object {obj!r}")
if module_name is None:
# Protect the iteration by using a list copy of sys.modules against dynamic
# modules that trigger imports of other modules upon calls to getattr.
for module_name, module in sys.modules.copy().items():
if (module_name == '__main__'
or module_name == '__mp_main__' # bpo-42406
or module is None):
continue
try:
if _getattribute(module, dotted_path) is obj:
return module_name
except AttributeError:
pass
module_name = '__main__'
try:
__import__(module_name, level=0)
module = sys.modules[module_name]
except (ImportError, ValueError, KeyError) as exc:
raise PicklingError(f"Can't pickle {obj!r}: {exc!s}")
try:
if _getattribute(module, dotted_path) is obj:
return module_name
except AttributeError:
raise PicklingError(f"Can't pickle {obj!r}: "
f"it's not found as {module_name}.{name}")
raise PicklingError(
f"Can't pickle {obj!r}: it's not the same object as {module_name}.{name}")
def encode_long(x):
r"""Encode a long to a two's complement little-endian binary string.
@@ -396,6 +403,13 @@ def decode_long(data):
"""
return int.from_bytes(data, byteorder='little', signed=True)
def _T(obj):
cls = type(obj)
module = cls.__module__
if module in (None, 'builtins', '__main__'):
return cls.__qualname__
return f'{module}.{cls.__qualname__}'
_NoValue = object()
@@ -409,7 +423,7 @@ class _Pickler:
The optional *protocol* argument tells the pickler to use the
given protocol; supported protocols are 0, 1, 2, 3, 4 and 5.
The default protocol is 4. It was introduced in Python 3.4, and
The default protocol is 5. It was introduced in Python 3.8, and
is incompatible with previous versions.
Specifying a negative protocol version selects the highest
@@ -579,26 +593,29 @@ class _Pickler:
if reduce is not _NoValue:
rv = reduce()
else:
raise PicklingError("Can't pickle %r object: %r" %
(t.__name__, obj))
raise PicklingError(f"Can't pickle {_T(t)} object")
# Check for string returned by reduce(), meaning "save as global"
if isinstance(rv, str):
self.save_global(obj, rv)
return
# Assert that reduce() returned a tuple
if not isinstance(rv, tuple):
raise PicklingError("%s must return string or tuple" % reduce)
try:
# Assert that reduce() returned a tuple
if not isinstance(rv, tuple):
raise PicklingError(f'__reduce__ must return a string or tuple, not {_T(rv)}')
# Assert that it returned an appropriately sized tuple
l = len(rv)
if not (2 <= l <= 6):
raise PicklingError("Tuple returned by %s must have "
"two to six elements" % reduce)
# Assert that it returned an appropriately sized tuple
l = len(rv)
if not (2 <= l <= 6):
raise PicklingError("tuple returned by __reduce__ "
"must contain 2 through 6 elements")
# Save the reduce() output and finally memoize the object
self.save_reduce(obj=obj, *rv)
# Save the reduce() output and finally memoize the object
self.save_reduce(obj=obj, *rv)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} object')
raise
def persistent_id(self, obj):
# This exists so a subclass can override it
@@ -620,10 +637,12 @@ class _Pickler:
dictitems=None, state_setter=None, *, obj=None):
# This API is called by some subclasses
if not isinstance(args, tuple):
raise PicklingError("args from save_reduce() must be a tuple")
if not callable(func):
raise PicklingError("func from save_reduce() must be callable")
raise PicklingError(f"first item of the tuple returned by __reduce__ "
f"must be callable, not {_T(func)}")
if not isinstance(args, tuple):
raise PicklingError(f"second item of the tuple returned by __reduce__ "
f"must be a tuple, not {_T(args)}")
save = self.save
write = self.write
@@ -632,19 +651,30 @@ class _Pickler:
if self.proto >= 2 and func_name == "__newobj_ex__":
cls, args, kwargs = args
if not hasattr(cls, "__new__"):
raise PicklingError("args[0] from {} args has no __new__"
.format(func_name))
raise PicklingError("first argument to __newobj_ex__() has no __new__")
if obj is not None and cls is not obj.__class__:
raise PicklingError("args[0] from {} args has the wrong class"
.format(func_name))
raise PicklingError(f"first argument to __newobj_ex__() "
f"must be {obj.__class__!r}, not {cls!r}")
if self.proto >= 4:
save(cls)
save(args)
save(kwargs)
try:
save(cls)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} class')
raise
try:
save(args)
save(kwargs)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} __new__ arguments')
raise
write(NEWOBJ_EX)
else:
func = partial(cls.__new__, cls, *args, **kwargs)
save(func)
try:
save(func)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} reconstructor')
raise
save(())
write(REDUCE)
elif self.proto >= 2 and func_name == "__newobj__":
@@ -676,18 +706,33 @@ class _Pickler:
# Python 2.2).
cls = args[0]
if not hasattr(cls, "__new__"):
raise PicklingError(
"args[0] from __newobj__ args has no __new__")
raise PicklingError("first argument to __newobj__() has no __new__")
if obj is not None and cls is not obj.__class__:
raise PicklingError(
"args[0] from __newobj__ args has the wrong class")
raise PicklingError(f"first argument to __newobj__() "
f"must be {obj.__class__!r}, not {cls!r}")
args = args[1:]
save(cls)
save(args)
try:
save(cls)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} class')
raise
try:
save(args)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} __new__ arguments')
raise
write(NEWOBJ)
else:
save(func)
save(args)
try:
save(func)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} reconstructor')
raise
try:
save(args)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} reconstructor arguments')
raise
write(REDUCE)
if obj is not None:
@@ -705,23 +750,35 @@ class _Pickler:
# items and dict items (as (key, value) tuples), or None.
if listitems is not None:
self._batch_appends(listitems)
self._batch_appends(listitems, obj)
if dictitems is not None:
self._batch_setitems(dictitems)
self._batch_setitems(dictitems, obj)
if state is not None:
if state_setter is None:
save(state)
try:
save(state)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} state')
raise
write(BUILD)
else:
# If a state_setter is specified, call it instead of load_build
# to update obj's with its previous state.
# First, push state_setter and its tuple of expected arguments
# (obj, state) onto the stack.
save(state_setter)
try:
save(state_setter)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} state setter')
raise
save(obj) # simple BINGET opcode as obj is already memoized.
save(state)
try:
save(state)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} state')
raise
write(TUPLE2)
# Trigger a state_setter(obj, state) function call.
write(REDUCE)
@@ -901,8 +958,12 @@ class _Pickler:
save = self.save
memo = self.memo
if n <= 3 and self.proto >= 2:
for element in obj:
save(element)
for i, element in enumerate(obj):
try:
save(element)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} item {i}')
raise
# Subtle. Same as in the big comment below.
if id(obj) in memo:
get = self.get(memo[id(obj)][0])
@@ -916,8 +977,12 @@ class _Pickler:
# has more than 3 elements.
write = self.write
write(MARK)
for element in obj:
save(element)
for i, element in enumerate(obj):
try:
save(element)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} item {i}')
raise
if id(obj) in memo:
# Subtle. d was not in memo when we entered save_tuple(), so
@@ -947,38 +1012,47 @@ class _Pickler:
self.write(MARK + LIST)
self.memoize(obj)
self._batch_appends(obj)
self._batch_appends(obj, obj)
dispatch[list] = save_list
_BATCHSIZE = 1000
def _batch_appends(self, items):
def _batch_appends(self, items, obj):
# Helper to batch up APPENDS sequences
save = self.save
write = self.write
if not self.bin:
for x in items:
save(x)
for i, x in enumerate(items):
try:
save(x)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} item {i}')
raise
write(APPEND)
return
it = iter(items)
while True:
tmp = list(islice(it, self._BATCHSIZE))
n = len(tmp)
if n > 1:
start = 0
for batch in batched(items, self._BATCHSIZE):
batch_len = len(batch)
if batch_len != 1:
write(MARK)
for x in tmp:
save(x)
for i, x in enumerate(batch, start):
try:
save(x)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} item {i}')
raise
write(APPENDS)
elif n:
save(tmp[0])
else:
try:
save(batch[0])
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} item {start}')
raise
write(APPEND)
# else tmp is empty, and we're done
if n < self._BATCHSIZE:
return
start += batch_len
def save_dict(self, obj):
if self.bin:
@@ -987,11 +1061,11 @@ class _Pickler:
self.write(MARK + DICT)
self.memoize(obj)
self._batch_setitems(obj.items())
self._batch_setitems(obj.items(), obj)
dispatch[dict] = save_dict
def _batch_setitems(self, items):
def _batch_setitems(self, items, obj):
# Helper to batch up SETITEMS sequences; proto >= 1 only
save = self.save
write = self.write
@@ -999,28 +1073,34 @@ class _Pickler:
if not self.bin:
for k, v in items:
save(k)
save(v)
try:
save(v)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} item {k!r}')
raise
write(SETITEM)
return
it = iter(items)
while True:
tmp = list(islice(it, self._BATCHSIZE))
n = len(tmp)
if n > 1:
for batch in batched(items, self._BATCHSIZE):
if len(batch) != 1:
write(MARK)
for k, v in tmp:
for k, v in batch:
save(k)
save(v)
try:
save(v)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} item {k!r}')
raise
write(SETITEMS)
elif n:
k, v = tmp[0]
else:
k, v = batch[0]
save(k)
save(v)
try:
save(v)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} item {k!r}')
raise
write(SETITEM)
# else tmp is empty, and we're done
if n < self._BATCHSIZE:
return
def save_set(self, obj):
save = self.save
@@ -1033,17 +1113,15 @@ class _Pickler:
write(EMPTY_SET)
self.memoize(obj)
it = iter(obj)
while True:
batch = list(islice(it, self._BATCHSIZE))
n = len(batch)
if n > 0:
write(MARK)
for batch in batched(obj, self._BATCHSIZE):
write(MARK)
try:
for item in batch:
save(item)
write(ADDITEMS)
if n < self._BATCHSIZE:
return
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} element')
raise
write(ADDITEMS)
dispatch[set] = save_set
def save_frozenset(self, obj):
@@ -1055,8 +1133,12 @@ class _Pickler:
return
write(MARK)
for item in obj:
save(item)
try:
for item in obj:
save(item)
except BaseException as exc:
exc.add_note(f'when serializing {_T(obj)} element')
raise
if id(obj) in self.memo:
# If the object is already in the memo, this means it is
@@ -1075,24 +1157,10 @@ class _Pickler:
if name is None:
name = getattr(obj, '__qualname__', None)
if name is None:
name = obj.__name__
if name is None:
name = obj.__name__
module_name = whichmodule(obj, name)
try:
__import__(module_name, level=0)
module = sys.modules[module_name]
obj2, parent = _getattribute(module, name)
except (ImportError, KeyError, AttributeError):
raise PicklingError(
"Can't pickle %r: it's not found as %s.%s" %
(obj, module_name, name)) from None
else:
if obj2 is not obj:
raise PicklingError(
"Can't pickle %r: it's not the same object as %s.%s" %
(obj, module_name, name))
if self.proto >= 2:
code = _extension_registry.get((module_name, name), _NoValue)
if code is not _NoValue:
@@ -1109,10 +1177,7 @@ class _Pickler:
else:
write(EXT4 + pack("<i", code))
return
lastname = name.rpartition('.')[2]
if parent is module:
name = lastname
# Non-ASCII identifiers are supported only with protocols >= 3.
if self.proto >= 4:
self.save(module_name)
self.save(name)
@@ -1144,8 +1209,7 @@ class _Pickler:
def _save_toplevel_by_name(self, module_name, name):
if self.proto >= 3:
# Non-ASCII identifiers are supported only with protocols >= 3.
self.write(GLOBAL + bytes(module_name, "utf-8") + b'\n' +
bytes(name, "utf-8") + b'\n')
encoding = "utf-8"
else:
if self.fix_imports:
r_name_mapping = _compat_pickle.REVERSE_NAME_MAPPING
@@ -1154,13 +1218,19 @@ class _Pickler:
module_name, name = r_name_mapping[(module_name, name)]
elif module_name in r_import_mapping:
module_name = r_import_mapping[module_name]
try:
self.write(GLOBAL + bytes(module_name, "ascii") + b'\n' +
bytes(name, "ascii") + b'\n')
except UnicodeEncodeError:
raise PicklingError(
"can't pickle global identifier '%s.%s' using "
"pickle protocol %i" % (module_name, name, self.proto)) from None
encoding = "ascii"
try:
self.write(GLOBAL + bytes(module_name, encoding) + b'\n')
except UnicodeEncodeError:
raise PicklingError(
f"can't pickle module identifier {module_name!r} using "
f"pickle protocol {self.proto}")
try:
self.write(bytes(name, encoding) + b'\n')
except UnicodeEncodeError:
raise PicklingError(
f"can't pickle global identifier {name!r} using "
f"pickle protocol {self.proto}")
def save_type(self, obj):
if obj is type(None):
@@ -1316,7 +1386,7 @@ class _Unpickler:
elif data == TRUE[1:]:
val = True
else:
val = int(data, 0)
val = int(data)
self.append(val)
dispatch[INT[0]] = load_int
@@ -1336,7 +1406,7 @@ class _Unpickler:
val = self.readline()[:-1]
if val and val[-1] == b'L'[0]:
val = val[:-1]
self.append(int(val, 0))
self.append(int(val))
dispatch[LONG[0]] = load_long
def load_long1(self):
@@ -1620,8 +1690,13 @@ class _Unpickler:
elif module in _compat_pickle.IMPORT_MAPPING:
module = _compat_pickle.IMPORT_MAPPING[module]
__import__(module, level=0)
if self.proto >= 4:
return _getattribute(sys.modules[module], name)[0]
if self.proto >= 4 and '.' in name:
dotted_path = name.split('.')
try:
return _getattribute(sys.modules[module], dotted_path)
except AttributeError:
raise AttributeError(
f"Can't resolve path {name!r} on module {module!r}")
else:
return getattr(sys.modules[module], name)
@@ -1831,36 +1906,26 @@ except ImportError:
Pickler, Unpickler = _Pickler, _Unpickler
dump, dumps, load, loads = _dump, _dumps, _load, _loads
# Doctest
def _test():
import doctest
return doctest.testmod()
if __name__ == "__main__":
def _main(args=None):
import argparse
import pprint
parser = argparse.ArgumentParser(
description='display contents of the pickle files')
description='display contents of the pickle files',
color=True,
)
parser.add_argument(
'pickle_file',
nargs='*', help='the pickle file')
parser.add_argument(
'-t', '--test', action='store_true',
help='run self-test suite')
parser.add_argument(
'-v', action='store_true',
help='run verbosely; only affects self-test run')
args = parser.parse_args()
if args.test:
_test()
else:
if not args.pickle_file:
parser.print_help()
nargs='+', help='the pickle file')
args = parser.parse_args(args)
for fn in args.pickle_file:
if fn == '-':
obj = load(sys.stdin.buffer)
else:
import pprint
for fn in args.pickle_file:
if fn == '-':
obj = load(sys.stdin.buffer)
else:
with open(fn, 'rb') as f:
obj = load(f)
pprint.pprint(obj)
with open(fn, 'rb') as f:
obj = load(f)
pprint.pprint(obj)
if __name__ == "__main__":
_main()
+5139
View File
File diff suppressed because it is too large. Load diff
+26 -43
View File
@@ -335,7 +335,7 @@ def read_stringnl(f, decode=True, stripquotes=True, *, encoding='latin-1'):
ValueError: no newline found when trying to read stringnl
Embedded escapes are undone in the result.
>>> read_stringnl(io.BytesIO(br"'a\n\\b\x00c\td'" + b"\n'e'"))
>>> read_stringnl(io.BytesIO(br"'a\n\\b\x00c\td'" + b"\n'e'")) # TODO: RUSTPYTHON # doctest: +EXPECTED_FAILURE
'a\n\\b\x00c\td'
"""
@@ -348,7 +348,7 @@ def read_stringnl(f, decode=True, stripquotes=True, *, encoding='latin-1'):
for q in (b'"', b"'"):
if data.startswith(q):
if not data.endswith(q):
raise ValueError("strinq quote %r not found at both "
raise ValueError("string quote %r not found at both "
"ends of %r" % (q, data))
data = data[1:-1]
break
@@ -2429,8 +2429,6 @@ def dis(pickle, out=None, memo=None, indentlevel=4, annotate=0):
+ A memo entry isn't referenced before it's defined.
+ The markobject isn't stored in the memo.
+ A memo entry isn't redefined.
"""
# Most of the hair here is for sanity checks, but most of it is needed
@@ -2484,7 +2482,7 @@ def dis(pickle, out=None, memo=None, indentlevel=4, annotate=0):
assert opcode.name == "POP"
numtopop = 0
else:
errormsg = markmsg = "no MARK exists on stack"
errormsg = "no MARK exists on stack"
# Check for correct memo usage.
if opcode.name in ("PUT", "BINPUT", "LONG_BINPUT", "MEMOIZE"):
@@ -2494,9 +2492,7 @@ def dis(pickle, out=None, memo=None, indentlevel=4, annotate=0):
else:
assert arg is not None
memo_idx = arg
if memo_idx in memo:
errormsg = "memo key %r already defined" % arg
elif not stack:
if not stack:
errormsg = "stack is empty -- can't store into memo"
elif stack[-1] is markobject:
errormsg = "can't store markobject in the memo"
@@ -2842,17 +2838,16 @@ __test__ = {'disassembler_test': _dis_test,
'disassembler_memo_test': _memo_test,
}
def _test():
import doctest
return doctest.testmod()
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(
description='disassemble one or more pickle files')
description='disassemble one or more pickle files',
color=True,
)
parser.add_argument(
'pickle_file',
nargs='*', help='the pickle file')
nargs='+', help='the pickle file')
parser.add_argument(
'-o', '--output',
help='the file where the output should be written')
@@ -2869,36 +2864,24 @@ if __name__ == "__main__":
'-p', '--preamble', default="==> {name} <==",
help='if more than one pickle file is specified, print this before'
' each disassembly')
parser.add_argument(
'-t', '--test', action='store_true',
help='run self-test suite')
parser.add_argument(
'-v', action='store_true',
help='run verbosely; only affects self-test run')
args = parser.parse_args()
if args.test:
_test()
annotate = 30 if args.annotate else 0
memo = {} if args.memo else None
if args.output is None:
output = sys.stdout
else:
if not args.pickle_file:
parser.print_help()
else:
annotate = 30 if args.annotate else 0
memo = {} if args.memo else None
if args.output is None:
output = sys.stdout
output = open(args.output, 'w')
try:
for arg in args.pickle_file:
if len(args.pickle_file) > 1:
name = '<stdin>' if arg == '-' else arg
preamble = args.preamble.format(name=name)
output.write(preamble + '\n')
if arg == '-':
dis(sys.stdin.buffer, output, memo, args.indentlevel, annotate)
else:
output = open(args.output, 'w')
try:
for arg in args.pickle_file:
if len(args.pickle_file) > 1:
name = '<stdin>' if arg == '-' else arg
preamble = args.preamble.format(name=name)
output.write(preamble + '\n')
if arg == '-':
dis(sys.stdin.buffer, output, memo, args.indentlevel, annotate)
else:
with open(arg, 'rb') as f:
dis(f, output, memo, args.indentlevel, annotate)
finally:
if output is not sys.stdout:
output.close()
with open(arg, 'rb') as f:
dis(f, output, memo, args.indentlevel, annotate)
finally:
if output is not sys.stdout:
output.close()
+24
View File
@@ -0,0 +1,24 @@
"""
RustPython specific helpers.
"""
import doctest
# copied from https://github.com/RustPython/RustPython/pull/6919
EXPECTED_FAILURE = doctest.register_optionflag("EXPECTED_FAILURE")
class DocTestChecker(doctest.OutputChecker):
"""
Custom output checker that lets us add: `+EXPECTED_FAILURE` for doctest tests.
We want to be able to mark failing doctest test the same way we do with normal
unit test, without this class we would have to skip the doctest for the CI to pass.
"""
def check_output(self, want, got, optionflags):
res = super().check_output(want, got, optionflags)
if optionflags & EXPECTED_FAILURE:
res = not res
return res
+2 -10
View File
@@ -545,17 +545,9 @@ Same with keyword only args:
import doctest
import unittest
EXPECTED_FAILURE = doctest.register_optionflag('EXPECTED_FAILURE') # TODO: RUSTPYTHON
class CustomOutputChecker(doctest.OutputChecker): # TODO: RUSTPYTHON
def check_output(self, want, got, optionflags): # TODO: RUSTPYTHON
if optionflags & EXPECTED_FAILURE: # TODO: RUSTPYTHON
if want == got: # TODO: RUSTPYTHON
return False # TODO: RUSTPYTHON
return True # TODO: RUSTPYTHON
return super().check_output(want, got, optionflags) # TODO: RUSTPYTHON
def load_tests(loader, tests, pattern):
tests.addTest(doctest.DocTestSuite(checker=CustomOutputChecker())) # TODO: RUSTPYTHON
from test.support.rustpython import DocTestChecker # TODO: RUSTPYTHON
tests.addTest(doctest.DocTestSuite(checker=DocTestChecker())) # XXX: RUSTPYTHON
return tests
+136 -96
View File
@@ -1,18 +1,22 @@
from _compat_pickle import (IMPORT_MAPPING, REVERSE_IMPORT_MAPPING,
NAME_MAPPING, REVERSE_NAME_MAPPING)
import builtins
import pickle
import io
import collections
import contextlib
import io
import pickle
import struct
import sys
import tempfile
import warnings
import weakref
from textwrap import dedent
import doctest
import unittest
from test import support
from test.support import import_helper
from test.support import cpython_only, import_helper, os_helper
from test.support.import_helper import ensure_lazy_imports
from test.pickletester import AbstractHookTests
from test.pickletester import AbstractUnpickleTests
@@ -33,6 +37,12 @@ except ImportError:
has_c_implementation = False
class LazyImportTest(unittest.TestCase):
@cpython_only
def test_lazy_import(self):
ensure_lazy_imports("pickle", {"re"})
class PyPickleTests(AbstractPickleModuleTests, unittest.TestCase):
dump = staticmethod(pickle._dump)
dumps = staticmethod(pickle._dumps)
@@ -41,14 +51,12 @@ class PyPickleTests(AbstractPickleModuleTests, unittest.TestCase):
Pickler = pickle._Pickler
Unpickler = pickle._Unpickler
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_dump_load_oob_buffers(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_dump_load_oob_buffers(self):
return super().test_dump_load_oob_buffers()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_dumps_loads_oob_buffers(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_dumps_loads_oob_buffers(self):
return super().test_dumps_loads_oob_buffers()
@@ -65,19 +73,16 @@ class PyUnpicklerTests(AbstractUnpickleTests, unittest.TestCase):
u = self.unpickler(f, **kwds)
return u.load()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_badly_escaped_string(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_badly_escaped_string(self):
return super().test_badly_escaped_string()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_correctly_quoted_string(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_correctly_quoted_string(self):
return super().test_correctly_quoted_string()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_load_python2_str_as_bytes(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_load_python2_str_as_bytes(self):
return super().test_load_python2_str_as_bytes()
@@ -92,22 +97,18 @@ class PyPicklingErrorTests(AbstractPicklingErrorTests, unittest.TestCase):
f.seek(0)
return bytes(f.read())
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_picklebuffer_error(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_picklebuffer_error()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_buffer_callback_error(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_buffer_callback_error(self):
return super().test_buffer_callback_error()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_non_continuous_buffer(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_non_continuous_buffer(self):
return super().test_non_continuous_buffer()
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_picklebuffer_error(self):
return super().test_picklebuffer_error()
class PyPicklerTests(AbstractPickleTests, unittest.TestCase):
@@ -126,41 +127,35 @@ class PyPicklerTests(AbstractPickleTests, unittest.TestCase):
u = self.unpickler(f, **kwds)
return u.load()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_c_methods(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_c_methods()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_buffers_error(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_buffers_error(self):
return super().test_buffers_error()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bytearray_memoization(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_bytearray_memoization(self):
return super().test_bytearray_memoization()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bytes_memoization(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_bytes_memoization(self):
return super().test_bytes_memoization()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_in_band_buffers(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_c_methods(self):
return super().test_c_methods()
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_in_band_buffers(self):
return super().test_in_band_buffers()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_oob_buffers(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_oob_buffers(self):
return super().test_oob_buffers()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_oob_buffers_writable_to_readonly(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_oob_buffers_writable_to_readonly(self):
return super().test_oob_buffers_writable_to_readonly()
class InMemoryPickleTests(AbstractPickleTests, AbstractUnpickleTests,
BigmemPickleTests, unittest.TestCase):
@@ -179,56 +174,47 @@ class InMemoryPickleTests(AbstractPickleTests, AbstractUnpickleTests,
test_find_class = None
test_custom_find_class = None
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_c_methods(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_c_methods()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_badly_escaped_string(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_badly_escaped_string(self):
return super().test_badly_escaped_string()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_correctly_quoted_string(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_correctly_quoted_string()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_load_python2_str_as_bytes(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_load_python2_str_as_bytes()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_oob_buffers_writable_to_readonly(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_oob_buffers_writable_to_readonly()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_buffers_error(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_buffers_error(self):
return super().test_buffers_error()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bytearray_memoization(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_bytearray_memoization(self):
return super().test_bytearray_memoization()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bytes_memoization(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_bytes_memoization(self):
return super().test_bytes_memoization()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_in_band_buffers(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_c_methods(self):
return super().test_c_methods()
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_correctly_quoted_string(self):
return super().test_correctly_quoted_string()
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_in_band_buffers(self):
return super().test_in_band_buffers()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_oob_buffers(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_load_python2_str_as_bytes(self):
return super().test_load_python2_str_as_bytes()
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_oob_buffers(self):
return super().test_oob_buffers()
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_oob_buffers_writable_to_readonly(self):
return super().test_oob_buffers_writable_to_readonly()
class PersistentPicklerUnpicklerMixin(object):
def dumps(self, arg, proto=None):
@@ -451,6 +437,7 @@ class PyIdPersPicklerTests(AbstractIdentityPersistentPicklerTests,
del unpickler.persistent_load
self.assertEqual(unpickler.persistent_load, old_persistent_load)
class PyPicklerUnpicklerObjectTests(AbstractPicklerUnpicklerObjectTests, unittest.TestCase):
pickler_class = pickle._Pickler
@@ -724,10 +711,10 @@ class CompatPickleTests(unittest.TestCase):
with self.subTest(((module3, name3), (module2, name2))):
if (module2, name2) == ('exceptions', 'OSError'):
attr = getattribute(module3, name3)
self.assertTrue(issubclass(attr, OSError))
self.assertIsSubclass(attr, OSError)
elif (module2, name2) == ('exceptions', 'ImportError'):
attr = getattribute(module3, name3)
self.assertTrue(issubclass(attr, ImportError))
self.assertIsSubclass(attr, ImportError)
else:
module, name = mapping(module2, name2)
if module3[:1] != '_':
@@ -772,8 +759,7 @@ class CompatPickleTests(unittest.TestCase):
module, name = mapping(module, name)
self.assertEqual((module, name), (module3, name3))
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_exceptions(self):
self.assertEqual(mapping('exceptions', 'StandardError'),
('builtins', 'Exception'))
@@ -823,6 +809,60 @@ class CompatPickleTests(unittest.TestCase):
self.assertEqual(mapping('multiprocessing', name),
('multiprocessing.context', name))
class CommandLineTest(unittest.TestCase):
def setUp(self):
self.filename = tempfile.mktemp()
self.addCleanup(os_helper.unlink, self.filename)
@staticmethod
def text_normalize(string):
"""Dedent *string* and strip it from its surrounding whitespaces.
This method is used by the other utility functions so that any
string to write or to match against can be freely indented.
"""
return dedent(string).strip()
def set_pickle_data(self, data):
with open(self.filename, 'wb') as f:
pickle.dump(data, f)
def invoke_pickle(self, *flags):
output = io.StringIO()
with contextlib.redirect_stdout(output):
pickle._main(args=[*flags, self.filename])
return self.text_normalize(output.getvalue())
def test_invocation(self):
# test 'python -m pickle pickle_file'
data = {
'a': [1, 2.0, 3+4j],
'b': ('character string', b'byte string'),
'c': 'string'
}
expect = '''
{'a': [1, 2.0, (3+4j)],
'b': ('character string', b'byte string'),
'c': 'string'}
'''
self.set_pickle_data(data)
with self.subTest(data=data):
res = self.invoke_pickle()
expect = self.text_normalize(expect)
self.assertListEqual(res.splitlines(), expect.splitlines())
@support.force_not_colorized
def test_unknown_flag(self):
stderr = io.StringIO()
with self.assertRaises(SystemExit):
# check that the parser help is shown
with contextlib.redirect_stderr(stderr):
_ = self.invoke_pickle('--unknown')
self.assertStartsWith(stderr.getvalue(), 'usage: ')
def load_tests(loader, tests, pattern):
tests.addTest(doctest.DocTestSuite(pickle))
return tests
+11 -22
View File
@@ -34,8 +34,7 @@ class PickleBufferTest(unittest.TestCase):
self.assertEqual(m.format, expected.format)
self.assertEqual(m.tobytes(), expected.tobytes())
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_constructor_failure(self):
with self.assertRaises(TypeError):
PickleBuffer()
@@ -47,8 +46,7 @@ class PickleBufferTest(unittest.TestCase):
with self.assertRaises(ValueError):
PickleBuffer(m)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_basics(self):
pb = PickleBuffer(b"foo")
self.assertEqual(b"foo", bytes(pb))
@@ -62,8 +60,7 @@ class PickleBufferTest(unittest.TestCase):
m[0] = 48
self.assertEqual(b"0oo", bytes(pb))
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_release(self):
pb = PickleBuffer(b"foo")
pb.release()
@@ -74,8 +71,7 @@ class PickleBufferTest(unittest.TestCase):
# Idempotency
pb.release()
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_cycle(self):
b = B(b"foo")
pb = PickleBuffer(b)
@@ -85,8 +81,7 @@ class PickleBufferTest(unittest.TestCase):
gc.collect()
self.assertIsNone(wpb())
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_ndarray_2d(self):
# C-contiguous
ndarray = import_helper.import_module("_testbuffer").ndarray
@@ -110,23 +105,20 @@ class PickleBufferTest(unittest.TestCase):
# Tests for PickleBuffer.raw()
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def check_raw(self, obj, equiv):
pb = PickleBuffer(obj)
with pb.raw() as m:
self.assertIsInstance(m, memoryview)
self.check_memoryview(m, equiv)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_raw(self):
for obj in (b"foo", bytearray(b"foo")):
with self.subTest(obj=obj):
self.check_raw(obj, obj)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_raw_ndarray(self):
# 1-D, contiguous
ndarray = import_helper.import_module("_testbuffer").ndarray
@@ -148,15 +140,13 @@ class PickleBufferTest(unittest.TestCase):
equiv = b'\xc8\x01\x00\x00'
self.check_raw(arr, equiv)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def check_raw_non_contiguous(self, obj):
pb = PickleBuffer(obj)
with self.assertRaisesRegex(BufferError, "non-contiguous"):
pb.raw()
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_raw_non_contiguous(self):
# 1-D
ndarray = import_helper.import_module("_testbuffer").ndarray
@@ -166,8 +156,7 @@ class PickleBufferTest(unittest.TestCase):
arr = ndarray(list(range(12)), shape=(4, 3), format='<i')[::2]
self.check_raw_non_contiguous(arr)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_raw_released(self):
pb = PickleBuffer(b"foo")
pb.release()
+68 -47
View File
@@ -62,41 +62,34 @@ class OptimizedPickleTests(AbstractPickleTests, unittest.TestCase):
self.assertIs(unpickled2[1], unpickled2[2])
self.assertNotIn(pickle.BINPUT, pickled2)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_buffers_error(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_buffers_error(self):
return super().test_buffers_error()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bytearray_memoization(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_bytearray_memoization(self):
return super().test_bytearray_memoization()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bytes_memoization(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_bytes_memoization(self):
return super().test_bytes_memoization()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_in_band_buffers(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_c_methods(self):
return super().test_c_methods()
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_in_band_buffers(self):
return super().test_in_band_buffers()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_oob_buffers(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_oob_buffers(self):
return super().test_oob_buffers()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_oob_buffers_writable_to_readonly(self): # TODO(RUSTPYTHON): Remove this test when it passes
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_oob_buffers_writable_to_readonly(self):
return super().test_oob_buffers_writable_to_readonly()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_c_methods(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_c_methods()
class SimpleReader:
def __init__(self, data):
@@ -241,7 +234,7 @@ highest protocol among opcodes = 0
def test_no_mark(self):
self.check_dis_error(b'Nt.', '''\
0: N NONE
1: t TUPLE no MARK exists on stack
1: t TUPLE
''', 'no MARK exists on stack')
def test_put(self):
@@ -256,26 +249,16 @@ highest protocol among opcodes = 4
''')
def test_put_redefined(self):
self.check_dis_error(b'Np1\np1\n.', '''\
self.check_dis(b'Np1\np1\nq\x01r\x01\x00\x00\x00\x94.', '''\
0: N NONE
1: p PUT 1
4: p PUT 1
''', 'memo key 1 already defined')
self.check_dis_error(b'Np1\nq\x01.', '''\
0: N NONE
1: p PUT 1
4: q BINPUT 1
''', 'memo key 1 already defined')
self.check_dis_error(b'Np1\nr\x01\x00\x00\x00.', '''\
0: N NONE
1: p PUT 1
4: r LONG_BINPUT 1
''', 'memo key 1 already defined')
self.check_dis_error(b'Np1\n\x94.', '''\
0: N NONE
1: p PUT 1
4: \\x94 MEMOIZE (as 1)
''', 'memo key None already defined')
7: q BINPUT 1
9: r LONG_BINPUT 1
14: \\x94 MEMOIZE (as 1)
15: . STOP
highest protocol among opcodes = 4
''')
def test_put_empty_stack(self):
self.check_dis_error(b'p0\n', '''\
@@ -429,13 +412,13 @@ highest protocol among opcodes = 0
self.check_dis_error(b'Sabc"\n.', '',
"no string quotes around b'abc\"'")
self.check_dis_error(b"S'abc\n.", '',
'''strinq quote b"'" not found at both ends of b"'abc"''')
'''string quote b"'" not found at both ends of b"'abc"''')
self.check_dis_error(b'S"abc\n.', '',
r"""strinq quote b'"' not found at both ends of b'"abc'""")
r"""string quote b'"' not found at both ends of b'"abc'""")
self.check_dis_error(b"S'abc\"\n.", '',
r"""strinq quote b"'" not found at both ends of b'\\'abc"'""")
r"""string quote b"'" not found at both ends of b'\\'abc"'""")
self.check_dis_error(b"S\"abc'\n.", '',
r"""strinq quote b'"' not found at both ends of b'"abc\\''""")
r"""string quote b'"' not found at both ends of b'"abc\\''""")
def test_binstring(self):
self.check_dis(b"T\x03\x00\x00\x00abc.", '''\
@@ -488,6 +471,44 @@ highest protocol among opcodes = 0
highest protocol among opcodes = 0
''')
def test_constants(self):
self.check_dis(b"(NI00\nI01\n\x89\x88t.", '''\
0: ( MARK
1: N NONE
2: I INT False
6: I INT True
10: \\x89 NEWFALSE
11: \\x88 NEWTRUE
12: t TUPLE (MARK at 0)
13: . STOP
highest protocol among opcodes = 2
''')
def test_integers(self):
self.check_dis(b"(I0\nI1\nI10\nI011\nL12\nL13L\nL014\nL015L\nt.", '''\
0: ( MARK
1: I INT 0
4: I INT 1
7: I INT 10
11: I INT 11
16: L LONG 12
20: L LONG 13
25: L LONG 14
30: L LONG 15
36: t TUPLE (MARK at 0)
37: . STOP
highest protocol among opcodes = 0
''')
def test_nondecimal_integers(self):
self.check_dis_error(b'I0b10\n.', '', 'invalid literal for int')
self.check_dis_error(b'I0o10\n.', '', 'invalid literal for int')
self.check_dis_error(b'I0x10\n.', '', 'invalid literal for int')
self.check_dis_error(b'L0b10L\n.', '', 'invalid literal for int')
self.check_dis_error(b'L0o10L\n.', '', 'invalid literal for int')
self.check_dis_error(b'L0x10L\n.', '', 'invalid literal for int')
class MiscTestCase(unittest.TestCase):
def test__all__(self):
not_exported = {
@@ -522,8 +543,8 @@ class MiscTestCase(unittest.TestCase):
def load_tests(loader, tests, pattern):
# TODO: RUSTPYTHON
# tests.addTest(doctest.DocTestSuite(pickletools))
from test.support.rustpython import DocTestChecker # TODO: RUSTPYTHON; Remove this
tests.addTest(doctest.DocTestSuite(pickletools, checker=DocTestChecker())) # XXX: RUSTPYTHON
return tests