Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Python 3.12 and drop EOL 3.6 and 3.7 #514

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 15 additions & 19 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v1
uses: actions/setup-python@v4
with:
python-version: 3.11
- name: Install flake8
Expand All @@ -32,20 +32,15 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
# TODO: add "3.12-dev" to the list
python_version: [3.7, 3.8, 3.9, "3.10", "3.11", "pypy-3.9"]
python_version: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy-3.9"]
exclude:
# Do not test all minor versions on all platforms, especially if they
# are not the oldest/newest supported versions
- os: windows-latest
python_version: 3.7
- os: windows-latest
python_version: 3.8
# as of 4/02/2020, psutil won't build under PyPy + Windows
- os: windows-latest
python_version: "pypy-3.9"
- os: macos-latest
python_version: 3.7
- os: macos-latest
python_version: 3.8
- os: macos-latest
Expand All @@ -56,11 +51,12 @@ jobs:
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python_version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python_version }}
allow-prereleases: true
- name: Install project and dependencies
shell: bash
run: |
Expand All @@ -85,7 +81,7 @@ jobs:
coverage combine --append
coverage xml -i
- name: Publish coverage results
uses: codecov/codecov-action@v1
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.xml
Expand All @@ -101,9 +97,9 @@ jobs:
matrix:
python_version: ["3.10"]
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v1
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python_version }}
- name: Install project and dependencies
Expand Down Expand Up @@ -133,9 +129,9 @@ jobs:
matrix:
python_version: ["3.10"]
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v1
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python_version }}
- name: Install project and dependencies
Expand All @@ -161,9 +157,9 @@ jobs:
matrix:
python_version: ["3.10"]
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v1
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python_version }}
- name: Install downstream project and dependencies
Expand All @@ -186,9 +182,9 @@ jobs:
matrix:
python_version: ["3.10"]
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v1
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python_version }}
- name: Install project and dependencies
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,14 @@ Running the tests

or alternatively for a specific environment:

tox -e py37
tox -e py312


- With `py.test` to only run the tests for your current version of
- With `pytest` to only run the tests for your current version of
Python:

pip install -r dev-requirements.txt
PYTHONPATH='.:tests' py.test
PYTHONPATH='.:tests' pytest

History
-------
Expand Down
2 changes: 1 addition & 1 deletion ci/install_coverage_subprocess_pth.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import os.path as op
from sysconfig import get_path

FILE_CONTENT = u"""\
FILE_CONTENT = """\
import coverage; coverage.process_startup()
"""

Expand Down
88 changes: 7 additions & 81 deletions cloudpickle/cloudpickle.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,26 +55,18 @@

from .compat import pickle
from collections import OrderedDict
from types import CellType
from typing import ClassVar, Generic, Union, Tuple, Callable
from pickle import _getattribute
from importlib._bootstrap import _find_spec

try: # pragma: no branch
import typing_extensions as _typing_extensions
from typing_extensions import Literal, Final
from typing_extensions import Literal
from typing import Final
except ImportError:
_typing_extensions = Literal = Final = None

if sys.version_info >= (3, 8):
from types import CellType
else:
def f():
a = 1

def g():
return a
return g
CellType = type(f().__closure__[0])


# cloudpickle is meant for inter process communication: we expect all
Expand Down Expand Up @@ -201,20 +193,7 @@ def _whichmodule(obj, name):
- Errors arising during module introspection are ignored, as those errors
are considered unwanted side effects.
"""
if sys.version_info[:2] < (3, 7) and isinstance(obj, typing.TypeVar): # pragma: no branch # noqa
# Workaround bug in old Python versions: prior to Python 3.7,
# T.__module__ would always be set to "typing" even when the TypeVar T
# would be defined in a different module.
if name is not None and getattr(typing, name, None) is obj:
# Built-in TypeVar defined in typing such as AnyStr
return 'typing'
else:
# User defined or third-party TypeVar: __module__ attribute is
# irrelevant, thus trigger a exhaustive search for obj in all
# modules.
module_name = None
else:
module_name = getattr(obj, '__module__', None)
module_name = getattr(obj, '__module__', None)

if module_name is not None:
return module_name
Expand Down Expand Up @@ -322,7 +301,7 @@ def _extract_code_globals(co):
out_names = _extract_code_globals_cache.get(co)
if out_names is None:
# We use a dict with None values instead of a set to get a
# deterministic order (assuming Python 3.6+) and avoid introducing
# deterministic order and avoid introducing
# non-deterministic pickle bytes as a results.
out_names = {name: None for name in _walk_global_ops(co)}

Expand Down Expand Up @@ -399,7 +378,7 @@ def cell_set(cell, value):
``f = types.FunctionType(code, globals, name, argdefs, closure)``,
closure will not be able to contain the yet-to-be-created f.

In Python3.7, cell_contents is writeable, so setting the contents of a cell
cell_contents is writeable, so setting the contents of a cell
can be done simply using
>>> cell.cell_contents = value

Expand Down Expand Up @@ -446,12 +425,7 @@ def cell_set(cell, value):
test and checker libraries decide to parse the whole file.
"""

if sys.version_info[:2] >= (3, 7): # pragma: no branch
cell.cell_contents = value
else:
_cell_set = types.FunctionType(
_cell_set_template_code, {}, '_cell_set', (), (cell,),)
_cell_set(value)
cell.cell_contents = value


def _make_cell_set_template_code():
Expand Down Expand Up @@ -481,9 +455,6 @@ def _cell_set_factory(value):
return _cell_set_template_code


if sys.version_info[:2] < (3, 7):
_cell_set_template_code = _make_cell_set_template_code()

# relevant opcodes
STORE_GLOBAL = opcode.opmap['STORE_GLOBAL']
DELETE_GLOBAL = opcode.opmap['DELETE_GLOBAL']
Expand Down Expand Up @@ -540,51 +511,6 @@ def _extract_class_dict(cls):
return clsdict


if sys.version_info[:2] < (3, 7): # pragma: no branch
def _is_parametrized_type_hint(obj):
# This is very cheap but might generate false positives. So try to
# narrow it down is good as possible.
type_module = getattr(type(obj), '__module__', None)
from_typing_extensions = type_module == 'typing_extensions'
from_typing = type_module == 'typing'

# general typing Constructs
is_typing = getattr(obj, '__origin__', None) is not None

# typing_extensions.Literal
is_literal = (
(getattr(obj, '__values__', None) is not None)
and from_typing_extensions
)

# typing_extensions.Final
is_final = (
(getattr(obj, '__type__', None) is not None)
and from_typing_extensions
)

# typing.ClassVar
is_classvar = (
(getattr(obj, '__type__', None) is not None) and from_typing
)

# typing.Union/Tuple for old Python 3.5
is_union = getattr(obj, '__union_params__', None) is not None
is_tuple = getattr(obj, '__tuple_params__', None) is not None
is_callable = (
getattr(obj, '__result__', None) is not None and
getattr(obj, '__args__', None) is not None
)
return any((is_typing, is_literal, is_final, is_classvar, is_union,
is_tuple, is_callable))

def _create_parametrized_type_hint(origin, args):
return origin[args]
else:
_is_parametrized_type_hint = None
_create_parametrized_type_hint = None


def parametrized_type_hint_getinitargs(obj):
# The distorted type check sematic for typing construct becomes:
# ``type(obj) is type(TypeHint)``, which means "obj is a
Expand Down
26 changes: 5 additions & 21 deletions cloudpickle/cloudpickle_fast.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
_builtin_type, _get_or_create_tracker_id, _make_skeleton_class,
_make_skeleton_enum, _extract_class_dict, dynamic_subimport, subimport,
_typevar_reduce, _get_bases, _make_cell, _make_empty_cell, CellType,
_is_parametrized_type_hint, PYPY, cell_set,
parametrized_type_hint_getinitargs, _create_parametrized_type_hint,
PYPY, cell_set,
parametrized_type_hint_getinitargs,
builtin_code_type,
_make_dict_keys, _make_dict_values, _make_dict_items, _make_function,
)
Expand Down Expand Up @@ -188,7 +188,7 @@ def _class_getstate(obj):
clsdict.pop('_abc_negative_cache_version', None)
registry = clsdict.pop('_abc_registry', None)
if registry is None:
# in Python3.7+, the abc caches and registered subclasses of a
# The abc caches and registered subclasses of a
# class are bundled into the single _abc_impl attribute
clsdict.pop('_abc_impl', None)
(registry, _, _, _) = abc._get_dump(obj)
Expand Down Expand Up @@ -355,7 +355,7 @@ def _file_reduce(obj):
obj.seek(0)
contents = obj.read()
obj.seek(curloc)
except IOError as e:
except OSError as e:
raise pickle.PicklingError(
"Cannot pickle file %s as it cannot be read" % name
) from e
Expand Down Expand Up @@ -720,11 +720,6 @@ 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 Expand Up @@ -785,18 +780,7 @@ def save_global(self, obj, name=None, pack=struct.pack):
return self.save_reduce(
_builtin_type, (_BUILTIN_TYPE_NAMES[obj],), obj=obj)

if sys.version_info[:2] < (3, 7) and _is_parametrized_type_hint(obj): # noqa # pragma: no branch
# Parametrized typing constructs in Python < 3.7 are not
# compatible with type checks and ``isinstance`` semantics. For
# this reason, it is easier to detect them using a
# duck-typing-based check (``_is_parametrized_type_hint``) than
# to populate the Pickler's dispatch with type-specific savers.
self.save_reduce(
_create_parametrized_type_hint,
parametrized_type_hint_getinitargs(obj),
obj=obj
)
elif name is not None:
if name is not None:
Pickler.save_global(self, obj, name=name)
elif not _should_pickle_by_reference(obj, name=name):
self._save_reduce_pickle5(*_dynamic_class_reduce(obj), obj=obj)
Expand Down
21 changes: 4 additions & 17 deletions cloudpickle/compat.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
import sys
import pickle # noqa: F401


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

# Use the Python pickler for old CPython versions
from pickle import _Pickler as Pickler # noqa: F401
else:
import pickle # noqa: F401

# Pickler will the C implementation in CPython and the Python
# implementation in PyPy
from pickle import Pickler # noqa: F401
# Pickler will the C implementation in CPython and the Python
# implementation in PyPy
from pickle import Pickler # noqa: F401
4 changes: 1 addition & 3 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# Dependencies for running the tests with py.test
# Dependencies for running the tests with pytest
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
Expand Down
Loading