Skip to content

Commit 508cdf1

Browse files
Optionally use pickle5 (Redux) (#370)
Co-authored-by: Pierre Glaser <[email protected]>
1 parent 938553f commit 508cdf1

9 files changed

+53
-16
lines changed

Diff for: .github/workflows/testing.yml

+1
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ jobs:
228228
git clone https://github.com/ray-project/ray.git ../ray
229229
cp -R ../ray/python/ray/tests $PROJECT_DIR/tests
230230
cp cloudpickle/cloudpickle.py $PROJECT_DIR/cloudpickle/cloudpickle.py
231+
cp cloudpickle/compat.py $PROJECT_DIR/cloudpickle/compat.py
231232
cp cloudpickle/cloudpickle_fast.py $PROJECT_DIR/cloudpickle/cloudpickle_fast.py
232233
- name: Test the downstream project
233234
run: |

Diff for: CHANGES.md

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
importable modules.
66
([issue #360](https://github.com/cloudpipe/cloudpickle/issues/354))
77

8+
- Add optional dependency on `pickle5` to get improved performance on
9+
Python 3.6 and 3.7.
10+
([PR #370](https://github.com/cloudpipe/cloudpickle/pull/370))
11+
812

913
1.4.1
1014
=====

Diff for: cloudpickle/cloudpickle.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
import builtins
4646
import dis
4747
import opcode
48-
import pickle
4948
import platform
5049
import sys
5150
import types
@@ -55,6 +54,7 @@
5554
import typing
5655
import warnings
5756

57+
from .compat import pickle
5858
from typing import Generic, Union, Tuple, Callable
5959
from pickle import _getattribute
6060
from importlib._bootstrap import _find_spec

Diff for: cloudpickle/cloudpickle_fast.py

+19-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import io
1616
import itertools
1717
import logging
18-
import pickle
1918
import sys
2019
import struct
2120
import types
@@ -25,6 +24,7 @@
2524
from enum import Enum
2625
from collections import ChainMap
2726

27+
from .compat import pickle, Pickler
2828
from .cloudpickle import (
2929
_extract_code_globals, _BUILTIN_TYPE_NAMES, DEFAULT_PROTOCOL,
3030
_find_imported_submodules, _get_cell_contents, _is_importable,
@@ -37,8 +37,8 @@
3737

3838
)
3939

40-
if sys.version_info >= (3, 8) and not PYPY:
41-
from _pickle import Pickler
40+
41+
if pickle.HIGHEST_PROTOCOL >= 5 and not PYPY:
4242
# Shorthands similar to pickle.dump/pickle.dumps
4343

4444
def dump(obj, file, protocol=None, buffer_callback=None):
@@ -73,8 +73,6 @@ def dumps(obj, protocol=None, buffer_callback=None):
7373
return file.getvalue()
7474

7575
else:
76-
from pickle import _Pickler as Pickler
77-
7876
# Shorthands similar to pickle.dump/pickle.dumps
7977
def dump(obj, file, protocol=None):
8078
"""Serialize obj as bytes streamed into file
@@ -551,6 +549,17 @@ def dump(self, obj):
551549
raise
552550

553551
if pickle.HIGHEST_PROTOCOL >= 5:
552+
# `CloudPickler.dispatch` is only left for backward compatibility - note
553+
# that when using protocol 5, `CloudPickler.dispatch` is not an
554+
# extension of `Pickler.dispatch` dictionary, because CloudPickler
555+
# subclasses the C-implemented Pickler, which does not expose a
556+
# `dispatch` attribute. Earlier versions of the protocol 5 CloudPickler
557+
# used `CloudPickler.dispatch` as a class-level attribute storing all
558+
# reducers implemented by cloudpickle, but the attribute name was not a
559+
# great choice given the meaning of `Cloudpickler.dispatch` when
560+
# `CloudPickler` extends the pure-python pickler.
561+
dispatch = dispatch_table
562+
554563
# Implementation of the reducer_override callback, in order to
555564
# efficiently serialize dynamic functions and classes by subclassing
556565
# the C-implemented Pickler.
@@ -604,6 +613,11 @@ def reducer_override(self, obj):
604613
reducers, such as Exceptions. See
605614
https://github.com/cloudpipe/cloudpickle/issues/248
606615
"""
616+
if sys.version_info[:2] < (3, 7) and _is_parametrized_type_hint(obj): # noqa # pragma: no branch
617+
return (
618+
_create_parametrized_type_hint,
619+
parametrized_type_hint_getinitargs(obj)
620+
)
607621
t = type(obj)
608622
try:
609623
is_anyclass = issubclass(t, type)

Diff for: cloudpickle/compat.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import sys
2+
3+
4+
if sys.version_info < (3, 8):
5+
try:
6+
import pickle5 as pickle # noqa: F401
7+
from pickle5 import Pickler # noqa: F401
8+
except ImportError:
9+
import pickle # noqa: F401
10+
from pickle import _Pickler as Pickler # noqa: F401
11+
else:
12+
import pickle # noqa: F401
13+
from _pickle import Pickler # noqa: F401

Diff for: dev-requirements.txt

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ flake8
33
pytest
44
pytest-cov
55
psutil
6+
# To test on older Python versions
7+
pickle5 >=0.0.11 ; python_version <= '3.7' and python_implementation == 'CPython'
68
# To be able to test tornado coroutines
79
tornado
810
# To be able to test numpy specific things
911
# but do not build numpy from source on Python nightly
10-
numpy; python_version <= '3.8'
12+
numpy >=1.18.5; python_version <= '3.8'
1113
# Code coverage uploader for Travis:
1214
codecov
1315
coverage

Diff for: tests/cloudpickle_file_test.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import unicode_literals
22

33
import os
4-
import pickle
54
import shutil
65
import sys
76
import tempfile
@@ -10,6 +9,7 @@
109
import pytest
1110

1211
import cloudpickle
12+
from cloudpickle.compat import pickle
1313

1414

1515
class CloudPickleFileTests(unittest.TestCase):

Diff for: tests/cloudpickle_test.py

+9-7
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import logging
1010
import math
1111
from operator import itemgetter, attrgetter
12-
import pickle
1312
import platform
1413
import random
1514
import shutil
@@ -43,6 +42,7 @@
4342
tornado = None
4443

4544
import cloudpickle
45+
from cloudpickle.compat import pickle
4646
from cloudpickle.cloudpickle import _is_importable
4747
from cloudpickle.cloudpickle import _make_empty_cell, cell_set
4848
from cloudpickle.cloudpickle import _extract_class_dict, _whichmodule
@@ -521,7 +521,7 @@ def test_module_locals_behavior(self):
521521
pickled_func_path = os.path.join(self.tmpdir, 'local_func_g.pkl')
522522

523523
child_process_script = '''
524-
import pickle
524+
from cloudpickle.compat import pickle
525525
import gc
526526
with open("{pickled_func_path}", 'rb') as f:
527527
func = pickle.load(f)
@@ -606,7 +606,7 @@ def test_load_dynamic_module_in_grandchild_process(self):
606606
child_process_module_file = os.path.join(
607607
self.tmpdir, 'dynamic_module_from_child_process.pkl')
608608
child_process_script = '''
609-
import pickle
609+
from cloudpickle.compat import pickle
610610
import textwrap
611611
612612
import cloudpickle
@@ -626,7 +626,7 @@ def test_load_dynamic_module_in_grandchild_process(self):
626626

627627
# The script ran by the process created by the child process
628628
child_of_child_process_script = """ '''
629-
import pickle
629+
from cloudpickle.compat import pickle
630630
with open('{child_process_module_file}','rb') as fid:
631631
mod = pickle.load(fid)
632632
''' """
@@ -681,7 +681,7 @@ def my_small_function(x, y):
681681
assert b'math' not in b
682682

683683
def test_module_importability(self):
684-
import pickle # decouple this test from global imports
684+
from cloudpickle.compat import pickle
685685
import os.path
686686
import distutils
687687
import distutils.ccompiler
@@ -1008,7 +1008,8 @@ def example():
10081008

10091009
# choose "subprocess" rather than "multiprocessing" because the latter
10101010
# library uses fork to preserve the parent environment.
1011-
command = ("import pickle, base64; "
1011+
command = ("import base64; "
1012+
"from cloudpickle.compat import pickle; "
10121013
"pickle.loads(base64.b32decode('" +
10131014
base64.b32encode(s).decode('ascii') +
10141015
"'))()")
@@ -1029,7 +1030,8 @@ def example():
10291030

10301031
s = cloudpickle.dumps(example, protocol=self.protocol)
10311032

1032-
command = ("import pickle, base64; "
1033+
command = ("import base64; "
1034+
"from cloudpickle.compat import pickle; "
10331035
"pickle.loads(base64.b32decode('" +
10341036
base64.b32encode(s).decode('ascii') +
10351037
"'))()")

Diff for: tests/testutils.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
import tempfile
55
import base64
66
from subprocess import Popen, check_output, PIPE, STDOUT, CalledProcessError
7-
from pickle import loads
7+
from cloudpickle.compat import pickle
88
from contextlib import contextmanager
99
from concurrent.futures import ProcessPoolExecutor
1010

1111
import psutil
1212
from cloudpickle import dumps
1313
from subprocess import TimeoutExpired
1414

15+
loads = pickle.loads
1516
TIMEOUT = 60
1617
TEST_GLOBALS = "a test value"
1718

0 commit comments

Comments
 (0)