Skip to content

Commit

Permalink
Merge branch 'main' into make-class-annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
hynek authored Jul 13, 2024
2 parents d17b9f6 + 0b07265 commit a8882ea
Show file tree
Hide file tree
Showing 17 changed files with 120 additions and 78 deletions.
2 changes: 1 addition & 1 deletion .github/CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
identity and expression, level of experience, education, socioeconomic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.

Expand Down
35 changes: 31 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,24 @@ jobs:
- name: Prepare tox
run: |
V=${{ matrix.python-version }}
DO_MYPY=1
if [[ "$V" = pypy-* ]]; then
DO_MYPY=0
V=pypy3
IS_PYPY=1
else
if [[ "$V" == "3.7" || "$V" == "3.8" ]]; then
DO_MYPY=0
echo "skipping $V"
else
echo "not skipping $V"
fi
V=py$(echo $V | tr -d .)
IS_PYPY=0
fi
echo IS_PYPY=$IS_PYPY >>$GITHUB_ENV
echo DO_MYPY=$DO_MYPY >>$GITHUB_ENV
echo TOX_PYTHON=$V >>$GITHUB_ENV
python -Im pip install tox
Expand All @@ -68,7 +76,7 @@ jobs:

- run: python -Im tox run -e ${{ env.TOX_PYTHON }}-tests
- run: python -Im tox run -e ${{ env.TOX_PYTHON }}-mypy
if: env.IS_PYPY == '0' && matrix.python-version != '3.7'
if: env.DO_MYPY == '1'

- name: Upload coverage data
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -115,6 +123,25 @@ jobs:
path: htmlcov
if: ${{ failure() }}


codspeed:
name: Run CodSpeed benchmarks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version-file: .python-version-default
cache: pip
- run: python -Im pip install tox-uv

- name: Run CodSpeed benchmarks
uses: CodSpeedHQ/action@v2
with:
token: ${{ secrets.CODSPEED_TOKEN }}
run: tox run -e codspeed


docs:
name: Build docs & run doctests
runs-on: ubuntu-latest
Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ repos:
- id: black

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.3
rev: v0.5.0
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
Expand All @@ -21,7 +21,7 @@ repos:
args: [tests]

- repo: https://github.com/codespell-project/codespell
rev: v2.2.6
rev: v2.3.0
hooks:
- id: codespell
args: [--exclude-file=tests/test_mypy.yml]
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ See https://github.com/python-attrs/attrs/blob/main/.github/CONTRIBUTING.md#chan

<!-- towncrier release notes start -->


## [23.2.0](https://github.com/python-attrs/attrs/tree/23.2.0) - 2023-12-31

### Changes
Expand All @@ -42,6 +43,7 @@ See https://github.com/python-attrs/attrs/blob/main/.github/CONTRIBUTING.md#chan
It is, for example, now possible to attach methods.
[#1203](https://github.com/python-attrs/attrs/issues/1203)


## [23.1.0](https://github.com/python-attrs/attrs/tree/23.1.0) - 2023-04-16

### Backwards-incompatible Changes
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Especially those generously supporting us at the *The Organization* tier and hig
<p align="center">
<a href="https://www.variomedia.de/"><img src="https://www.attrs.org/en/latest/_static/sponsors/Variomedia.svg" width="200" height="60" /></a>
<a href="https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=referral&utm_campaign=enterprise&utm_term=repo"><img src="https://www.attrs.org/en/latest/_static/sponsors/Tidelift.svg" width="200" height="60" /></a>
<a href="https://klaviyo.com/"><img src="https://www.attrs.org/en/latest/_static/sponsors/Klaviyo.svg" width="200" height="60"/></a>
<a href="https://filepreviews.io/"><img src="https://www.attrs.org/en/latest/_static/sponsors/FilePreviews.svg" width="200" height="60"/></a>
</p>

Expand Down
1 change: 1 addition & 0 deletions changelog.d/towncrier_template.md.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ No significant changes.


{% endif %}

{% endfor %}
1 change: 1 addition & 0 deletions docs/_static/sponsors/Klaviyo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 8 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ dynamic = ["version", "readme"]

[project.optional-dependencies]
tests-mypy = [
'pytest-mypy-plugins; platform_python_implementation == "CPython" and python_version >= "3.8" and python_version < "3.13"',
'pytest-mypy-plugins; platform_python_implementation == "CPython" and python_version >= "3.9" and python_version < "3.13"',
# Since the mypy error messages keep changing, we have to keep updating this
# pin.
'mypy>=1.6,<1.10; platform_python_implementation == "CPython" and python_version >= "3.8"',
'mypy>=1.10.1; platform_python_implementation == "CPython" and python_version >= "3.9"',
]
tests = [
# For regression test to ensure cloudpickle compat doesn't break.
Expand All @@ -43,6 +43,7 @@ tests = [
"pympler",
# 4.3.0 dropped last use of `convert`
"pytest>=4.3.0",
"pytest-codspeed",
"pytest-xdist[psutil]",
"attrs[tests-mypy]",
]
Expand Down Expand Up @@ -271,3 +272,8 @@ showcontent = true
pretty = true
disallow_untyped_defs = true
check_untyped_defs = true


[tool.pyright]
# We need epexrimental features for converters.
enableExperimentalFeatures = true
16 changes: 5 additions & 11 deletions src/attr/_make.py
Original file line number Diff line number Diff line change
Expand Up @@ -931,19 +931,13 @@ def _create_slots_class(self):
# To know to update them.
additional_closure_functions_to_update = []
if cached_properties:
# Add cached properties to names for slotting.
names += tuple(cached_properties.keys())

for name in cached_properties:
# Clear out function from class to avoid clashing.
del cd[name]

additional_closure_functions_to_update.extend(
cached_properties.values()
)

class_annotations = _get_annotations(self._cls)
for name, func in cached_properties.items():
# Add cached properties to names for slotting.
names += (name,)
# Clear out function from class to avoid clashing.
del cd[name]
additional_closure_functions_to_update.append(func)
annotation = inspect.signature(func).return_annotation
if annotation is not inspect.Parameter.empty:
class_annotations[name] = annotation
Expand Down
1 change: 0 additions & 1 deletion tests/dataclass_transform_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ class Define:

@attr.define()
class DefineConverter:
# mypy plugin adapts the "int" method signature, pyright does not
with_converter: int = attr.field(converter=int)


Expand Down
23 changes: 13 additions & 10 deletions tests/test_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,15 @@ class C:

attr.resolve_types(C)

assert int == attr.fields(C).a.type
assert int is attr.fields(C).a.type

assert attr.Factory(list) == attr.fields(C).x.default
assert typing.List[int] == attr.fields(C).x.type
assert typing.List[int] is attr.fields(C).x.type

assert int == attr.fields(C).y.type
assert int is attr.fields(C).y.type
assert 2 == attr.fields(C).y.default

assert int == attr.fields(C).z.type
assert int is attr.fields(C).z.type

assert typing.Any == attr.fields(C).foo.type

Expand Down Expand Up @@ -534,15 +534,15 @@ class C:

attr.resolve_types(C, globals)

assert attr.fields(C).x.type == Annotated[float, "test"]
assert Annotated[float, "test"] is attr.fields(C).x.type

@attr.define
class D:
x: 'Annotated[float, "test"]'

attr.resolve_types(D, globals, include_extras=False)

assert attr.fields(D).x.type == float
assert float is attr.fields(D).x.type

def test_resolve_types_auto_attrib(self, slots):
"""
Expand Down Expand Up @@ -658,8 +658,8 @@ class B(A):
attr.resolve_types(A)
attr.resolve_types(B)

assert int == attr.fields(A).n.type
assert int == attr.fields(B).n.type
assert int is attr.fields(A).n.type
assert int is attr.fields(B).n.type

def test_resolve_twice(self):
"""
Expand All @@ -672,9 +672,12 @@ class A:
n: "int"

attr.resolve_types(A)
assert int == attr.fields(A).n.type

assert int is attr.fields(A).n.type

attr.resolve_types(A)
assert int == attr.fields(A).n.type

assert int is attr.fields(A).n.type


@pytest.mark.parametrize(
Expand Down
6 changes: 6 additions & 0 deletions tests/test_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ class TestFunctional:
Functional tests.
"""

@pytest.mark.benchmark()
@pytest.mark.parametrize("cls", [C2, C2Slots])
def test_fields(self, cls):
"""
Expand Down Expand Up @@ -145,6 +146,7 @@ def test_fields(self, cls):
),
) == attr.fields(cls)

@pytest.mark.benchmark()
@pytest.mark.parametrize("cls", [C1, C1Slots])
def test_asdict(self, cls):
"""
Expand Down Expand Up @@ -180,6 +182,7 @@ class C3:

assert "C3(_x=1)" == repr(C3(x=1))

@pytest.mark.benchmark()
@given(booleans(), booleans())
def test_programmatic(self, slots, frozen):
"""
Expand Down Expand Up @@ -337,6 +340,7 @@ def test_metaclass_preserved(self, cls):
"""
assert Meta == type(cls)

@pytest.mark.benchmark()
def test_default_decorator(self):
"""
Default decorator sets the default and the respective method gets
Expand Down Expand Up @@ -540,6 +544,7 @@ class C:
assert "itemgetter" == attr.fields(C).itemgetter.name
assert "x" == attr.fields(C).x.name

@pytest.mark.benchmark()
def test_auto_exc(self, slots, frozen):
"""
Classes with auto_exc=True have a Exception-style __str__, compare and
Expand Down Expand Up @@ -734,6 +739,7 @@ class D(C):
assert "_setattr('y', y)" in src
assert object.__setattr__ != D.__setattr__

@pytest.mark.benchmark()
def test_unsafe_hash(self, slots):
"""
attr.s(unsafe_hash=True) makes a class hashable.
Expand Down
2 changes: 1 addition & 1 deletion tests/test_make.py
Original file line number Diff line number Diff line change
Expand Up @@ -1099,7 +1099,7 @@ class D:

cls = make_class("C", {})

assert cls.__mro__[-1] == object
assert cls.__mro__[-1] is object

cls = make_class("C", {}, bases=(D,))

Expand Down
8 changes: 4 additions & 4 deletions tests/test_mypy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@
x: Optional[T]
@classmethod
def clsmeth(cls) -> None:
reveal_type(cls) # N: Revealed type is "Type[main.A[T`1]]"
reveal_type(cls) # N: Revealed type is "type[main.A[T`1]]"
- case: testAttrsForwardReference
main: |
Expand Down Expand Up @@ -645,7 +645,7 @@
b: str = attr.ib()
@classmethod
def new(cls) -> A:
reveal_type(cls) # N: Revealed type is "Type[main.A]"
reveal_type(cls) # N: Revealed type is "type[main.A]"
return cls(6, 'hello')
@classmethod
def bad(cls) -> A:
Expand Down Expand Up @@ -680,7 +680,7 @@
@classmethod
def foo(cls, x: Union[int, str]) -> Union[int, str]:
reveal_type(cls) # N: Revealed type is "Type[main.A]"
reveal_type(cls) # N: Revealed type is "type[main.A]"
reveal_type(cls.other()) # N: Revealed type is "builtins.str"
return x
Expand Down Expand Up @@ -1411,4 +1411,4 @@
reveal_type(A) # N: Revealed type is "def () -> main.A"
if has(A):
reveal_type(A) # N: Revealed type is "Type[attr.AttrsInstance]"
reveal_type(A) # N: Revealed type is "type[attr.AttrsInstance]"
2 changes: 1 addition & 1 deletion tests/test_next_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def test_field_type(self):

A = attrs.make_class("A", classFields)

assert int == attrs.fields(A).testint.type
assert int is attrs.fields(A).testint.type

def test_no_slots(self):
"""
Expand Down
Loading

0 comments on commit a8882ea

Please sign in to comment.