mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
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:
1 parent
48e4e7c661
commit
bbb6c66a29
9 files changed
+5649
-396
No files matched your search
Vendored
+1
-1
@@ -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
|
||||
Vendored
+242
-177
@@ -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()
|
||||
Vendored
+5139
File diff suppressed because it is too large.
Load diff
Vendored
+26
-43
@@ -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()
|
||||
Vendored
+24
@@ -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
|
||||
Vendored
+2
-10
@@ -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
|
||||
|
||||
|
||||
|
||||
Vendored
+136
-96
@@ -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
|
||||
|
||||
Vendored
+11
-22
@@ -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()
|
||||
|
||||
Vendored
+68
-47
@@ -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
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user