Skip to content

Commit

Permalink
Optionally use pickle5 (Redux) (#370)
Browse files Browse the repository at this point in the history
Co-authored-by: Pierre Glaser <pierreglaser@msn.com>
  • Loading branch information
jakirkham and pierreglaser authored Jul 1, 2020
1 parent 938553f commit 508cdf1
Show file tree
Hide file tree
Showing 9 changed files with 53 additions and 16 deletions.
1 change: 1 addition & 0 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ jobs:
git clone https://github.com/ray-project/ray.git ../ray
cp -R ../ray/python/ray/tests $PROJECT_DIR/tests
cp cloudpickle/cloudpickle.py $PROJECT_DIR/cloudpickle/cloudpickle.py
cp cloudpickle/compat.py $PROJECT_DIR/cloudpickle/compat.py
cp cloudpickle/cloudpickle_fast.py $PROJECT_DIR/cloudpickle/cloudpickle_fast.py
- name: Test the downstream project
run: |
Expand Down
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
importable modules.
([issue #360](https://github.com/cloudpipe/cloudpickle/issues/354))

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


1.4.1
=====
Expand Down
2 changes: 1 addition & 1 deletion cloudpickle/cloudpickle.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
import builtins
import dis
import opcode
import pickle
import platform
import sys
import types
Expand All @@ -55,6 +54,7 @@
import typing
import warnings

from .compat import pickle
from typing import Generic, Union, Tuple, Callable
from pickle import _getattribute
from importlib._bootstrap import _find_spec
Expand Down
24 changes: 19 additions & 5 deletions cloudpickle/cloudpickle_fast.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import io
import itertools
import logging
import pickle
import sys
import struct
import types
Expand All @@ -25,6 +24,7 @@
from enum import Enum
from collections import ChainMap

from .compat import pickle, Pickler
from .cloudpickle import (
_extract_code_globals, _BUILTIN_TYPE_NAMES, DEFAULT_PROTOCOL,
_find_imported_submodules, _get_cell_contents, _is_importable,
Expand All @@ -37,8 +37,8 @@

)

if sys.version_info >= (3, 8) and not PYPY:
from _pickle import Pickler

if pickle.HIGHEST_PROTOCOL >= 5 and not PYPY:
# Shorthands similar to pickle.dump/pickle.dumps

def dump(obj, file, protocol=None, buffer_callback=None):
Expand Down Expand Up @@ -73,8 +73,6 @@ def dumps(obj, protocol=None, buffer_callback=None):
return file.getvalue()

else:
from pickle import _Pickler as Pickler

# Shorthands similar to pickle.dump/pickle.dumps
def dump(obj, file, protocol=None):
"""Serialize obj as bytes streamed into file
Expand Down Expand Up @@ -551,6 +549,17 @@ def dump(self, obj):
raise

if pickle.HIGHEST_PROTOCOL >= 5:
# `CloudPickler.dispatch` is only left for backward compatibility - note
# that when using protocol 5, `CloudPickler.dispatch` is not an
# extension of `Pickler.dispatch` dictionary, because CloudPickler
# subclasses the C-implemented Pickler, which does not expose a
# `dispatch` attribute. Earlier versions of the protocol 5 CloudPickler
# used `CloudPickler.dispatch` as a class-level attribute storing all
# reducers implemented by cloudpickle, but the attribute name was not a
# great choice given the meaning of `Cloudpickler.dispatch` when
# `CloudPickler` extends the pure-python pickler.
dispatch = dispatch_table

# Implementation of the reducer_override callback, in order to
# efficiently serialize dynamic functions and classes by subclassing
# the C-implemented Pickler.
Expand Down Expand Up @@ -604,6 +613,11 @@ def reducer_override(self, obj):
reducers, such as Exceptions. See
https://github.com/cloudpipe/cloudpickle/issues/248
"""
if sys.version_info[:2] < (3, 7) and _is_parametrized_type_hint(obj): # noqa # pragma: no branch
return (
_create_parametrized_type_hint,
parametrized_type_hint_getinitargs(obj)
)
t = type(obj)
try:
is_anyclass = issubclass(t, type)
Expand Down
13 changes: 13 additions & 0 deletions cloudpickle/compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import sys


if sys.version_info < (3, 8):
try:
import pickle5 as pickle # noqa: F401
from pickle5 import Pickler # noqa: F401
except ImportError:
import pickle # noqa: F401
from pickle import _Pickler as Pickler # noqa: F401
else:
import pickle # noqa: F401
from _pickle import Pickler # noqa: F401
4 changes: 3 additions & 1 deletion dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ flake8
pytest
pytest-cov
psutil
# To test on older Python versions
pickle5 >=0.0.11 ; python_version <= '3.7' and python_implementation == 'CPython'
# To be able to test tornado coroutines
tornado
# To be able to test numpy specific things
# but do not build numpy from source on Python nightly
numpy; python_version <= '3.8'
numpy >=1.18.5; python_version <= '3.8'
# Code coverage uploader for Travis:
codecov
coverage
Expand Down
2 changes: 1 addition & 1 deletion tests/cloudpickle_file_test.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from __future__ import unicode_literals

import os
import pickle
import shutil
import sys
import tempfile
Expand All @@ -10,6 +9,7 @@
import pytest

import cloudpickle
from cloudpickle.compat import pickle


class CloudPickleFileTests(unittest.TestCase):
Expand Down
16 changes: 9 additions & 7 deletions tests/cloudpickle_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import logging
import math
from operator import itemgetter, attrgetter
import pickle
import platform
import random
import shutil
Expand Down Expand Up @@ -43,6 +42,7 @@
tornado = None

import cloudpickle
from cloudpickle.compat import pickle
from cloudpickle.cloudpickle import _is_importable
from cloudpickle.cloudpickle import _make_empty_cell, cell_set
from cloudpickle.cloudpickle import _extract_class_dict, _whichmodule
Expand Down Expand Up @@ -521,7 +521,7 @@ def test_module_locals_behavior(self):
pickled_func_path = os.path.join(self.tmpdir, 'local_func_g.pkl')

child_process_script = '''
import pickle
from cloudpickle.compat import pickle
import gc
with open("{pickled_func_path}", 'rb') as f:
func = pickle.load(f)
Expand Down Expand Up @@ -606,7 +606,7 @@ def test_load_dynamic_module_in_grandchild_process(self):
child_process_module_file = os.path.join(
self.tmpdir, 'dynamic_module_from_child_process.pkl')
child_process_script = '''
import pickle
from cloudpickle.compat import pickle
import textwrap
import cloudpickle
Expand All @@ -626,7 +626,7 @@ def test_load_dynamic_module_in_grandchild_process(self):

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

def test_module_importability(self):
import pickle # decouple this test from global imports
from cloudpickle.compat import pickle
import os.path
import distutils
import distutils.ccompiler
Expand Down Expand Up @@ -1008,7 +1008,8 @@ def example():

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

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

command = ("import pickle, base64; "
command = ("import base64; "
"from cloudpickle.compat import pickle; "
"pickle.loads(base64.b32decode('" +
base64.b32encode(s).decode('ascii') +
"'))()")
Expand Down
3 changes: 2 additions & 1 deletion tests/testutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
import tempfile
import base64
from subprocess import Popen, check_output, PIPE, STDOUT, CalledProcessError
from pickle import loads
from cloudpickle.compat import pickle
from contextlib import contextmanager
from concurrent.futures import ProcessPoolExecutor

import psutil
from cloudpickle import dumps
from subprocess import TimeoutExpired

loads = pickle.loads
TIMEOUT = 60
TEST_GLOBALS = "a test value"

Expand Down

0 comments on commit 508cdf1

Please sign in to comment.