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.11 #969

Merged
merged 3 commits into from Jun 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Expand Up @@ -23,7 +23,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "pypy-3.7", "pypy-3.8"]
python-version: ["3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11.0-beta - 3.11", "pypy-3.7", "pypy-3.8"]

steps:
- uses: actions/checkout@v3
Expand Down
1 change: 1 addition & 0 deletions setup.py
Expand Up @@ -39,6 +39,7 @@
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Software Development :: Libraries :: Python Modules",
Expand Down
28 changes: 13 additions & 15 deletions src/attr/_compat.py
Expand Up @@ -111,12 +111,10 @@ def force_x_to_be_a_cell(): # pragma: no cover
# Convert this code object to a code object that sets the
# function's first _freevar_ (not cellvar) to the argument.
if sys.version_info >= (3, 8):
# CPython 3.8+ has an incompatible CodeType signature
# (added a posonlyargcount argument) but also added
# CodeType.replace() to do this without counting parameters.
set_first_freevar_code = co.replace(
co_cellvars=co.co_freevars, co_freevars=co.co_cellvars
)

def set_closure_cell(cell, value):
cell.cell_contents = value

else:
args = [co.co_argcount]
args.append(co.co_kwonlyargcount)
Expand All @@ -140,15 +138,15 @@ def force_x_to_be_a_cell(): # pragma: no cover
)
set_first_freevar_code = types.CodeType(*args)

def set_closure_cell(cell, value):
# Create a function using the set_first_freevar_code,
# whose first closure cell is `cell`. Calling it will
# change the value of that cell.
setter = types.FunctionType(
set_first_freevar_code, {}, "setter", (), (cell,)
)
# And call it to set the cell.
setter(value)
def set_closure_cell(cell, value):
# Create a function using the set_first_freevar_code,
# whose first closure cell is `cell`. Calling it will
# change the value of that cell.
setter = types.FunctionType(
set_first_freevar_code, {}, "setter", (), (cell,)
)
# And call it to set the cell.
setter(value)

# Make sure it works on this interpreter:
def make_func_with_cell():
Expand Down
13 changes: 9 additions & 4 deletions tests/test_annotations.py
Expand Up @@ -94,6 +94,10 @@ class C:
assert 1 == len(attr.fields(C))
assert_init_annotations(C, x=typing.List[int])

@pytest.mark.skipif(
sys.version_info[:2] < (3, 11),
reason="Incompatible behavior on older Pythons",
)
@pytest.mark.parametrize("slots", [True, False])
def test_auto_attribs(self, slots):
"""
Expand Down Expand Up @@ -149,7 +153,7 @@ class C:
x=typing.List[int],
y=int,
z=int,
foo=typing.Optional[typing.Any],
foo=typing.Any,
)

@pytest.mark.parametrize("slots", [True, False])
Expand Down Expand Up @@ -384,8 +388,9 @@ def noop():

assert attr.converters.optional(noop).__annotations__ == {}

@pytest.mark.xfail(
sys.version_info[:2] == (3, 6), reason="Does not work on 3.6."
@pytest.mark.skipif(
sys.version_info[:2] < (3, 11),
reason="Incompatible behavior on older Pythons",
)
@pytest.mark.parametrize("slots", [True, False])
def test_annotations_strings(self, slots):
Expand Down Expand Up @@ -417,7 +422,7 @@ class C:
x=typing.List[int],
y=int,
z=int,
foo=typing.Optional[typing.Any],
foo=typing.Any,
)

@pytest.mark.parametrize("slots", [True, False])
Expand Down
8 changes: 6 additions & 2 deletions tests/test_make.py
Expand Up @@ -2275,7 +2275,9 @@ class C:
def __getstate__(self):
return ("hi",)

assert None is getattr(C(), "__setstate__", None)
assert getattr(object, "__setstate__", None) is getattr(
C, "__setstate__", None
)

@attr.s(slots=slots, auto_detect=True)
class C:
Expand All @@ -2291,7 +2293,9 @@ def __setstate__(self, state):
i.__setstate__(())

assert True is i.called
assert None is getattr(C(), "__getstate__", None)
assert getattr(object, "__getstate__", None) is getattr(
C, "__getstate__", None
)

@pytest.mark.skipif(PY310, reason="Pre-3.10 only.")
def test_match_args_pre_310(self):
Expand Down
20 changes: 12 additions & 8 deletions tests/test_slots.py
Expand Up @@ -660,10 +660,12 @@ def test_no_getstate_setstate_for_dict_classes(self):
As long as getstate_setstate is None, nothing is done to dict
classes.
"""
i = C1(1, 2)

assert None is getattr(i, "__getstate__", None)
assert None is getattr(i, "__setstate__", None)
assert getattr(object, "__getstate__", None) is getattr(
C1, "__getstate__", None
)
assert getattr(object, "__setstate__", None) is getattr(
C1, "__setstate__", None
)

def test_no_getstate_setstate_if_option_false(self):
"""
Expand All @@ -674,10 +676,12 @@ def test_no_getstate_setstate_if_option_false(self):
class C:
x = attr.ib()

i = C(42)

assert None is getattr(i, "__getstate__", None)
assert None is getattr(i, "__setstate__", None)
assert getattr(object, "__getstate__", None) is getattr(
C, "__getstate__", None
)
assert getattr(object, "__setstate__", None) is getattr(
C, "__setstate__", None
)

@pytest.mark.parametrize("cls", [C2(1), C2Slots(1)])
def test_getstate_set_state_force_true(self, cls):
Expand Down
3 changes: 2 additions & 1 deletion tox.ini
Expand Up @@ -16,11 +16,12 @@ python =
3.8: py38, changelog
3.9: py39, pyright
3.10: py310, manifest, typing, docs
3.11: py311
pypy-3: pypy3


[tox]
envlist = typing,pre-commit,py35,py36,py37,py38,py39,py310,pypy3,pyright,manifest,docs,pypi-description,changelog,coverage-report
envlist = typing,pre-commit,py35,py36,py37,py38,py39,py310,py311,pypy3,pyright,manifest,docs,pypi-description,changelog,coverage-report
isolated_build = True


Expand Down