Skip to content

Commit

Permalink
Remove various obsolete _compat helpers (#941)
Browse files Browse the repository at this point in the history
* remove obsolete _compat.iteritems()

* remove obsolete _compat.TYPE

* remove obsolete _compat.isclass()

* remove obsolete _compat.new_class()

* remove obsolete _compat.metadata_proxy
  • Loading branch information
wbolster authored Mar 21, 2022
1 parent a22e98c commit e18b5f3
Show file tree
Hide file tree
Showing 9 changed files with 44 additions and 84 deletions.
6 changes: 4 additions & 2 deletions src/attr/_cmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@


import functools
import types

from ._compat import new_class
from ._make import _make_ne


Expand Down Expand Up @@ -79,7 +79,9 @@ def cmp_using(
num_order_functions += 1
body["__ge__"] = _make_operator("ge", ge)

type_ = new_class(class_name, (object,), {}, lambda ns: ns.update(body))
type_ = types.new_class(
class_name, (object,), {}, lambda ns: ns.update(body)
)

# Add same type requirement.
if require_same_type:
Expand Down
18 changes: 0 additions & 18 deletions src/attr/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,6 @@ def just_warn(*args, **kw):
)


def isclass(klass):
return isinstance(klass, type)


TYPE = "class"


def iteritems(d):
return d.items()


new_class = types.new_class


def metadata_proxy(d):
return types.MappingProxyType(dict(d))


class _AnnotationExtractor:
"""
Extract type annotations from a callable, returning None whenever there
Expand Down
9 changes: 4 additions & 5 deletions src/attr/_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import copy

from ._compat import iteritems
from ._make import NOTHING, _obj_setattr, fields
from .exceptions import AttrsAttributeNotFoundError

Expand Down Expand Up @@ -106,7 +105,7 @@ def asdict(
value_serializer=value_serializer,
),
)
for kk, vv in iteritems(v)
for kk, vv in v.items()
)
else:
rv[a.name] = v
Expand Down Expand Up @@ -178,7 +177,7 @@ def _asdict_anything(
value_serializer=value_serializer,
),
)
for kk, vv in iteritems(val)
for kk, vv in val.items()
)
else:
rv = val
Expand Down Expand Up @@ -277,7 +276,7 @@ def astuple(
if has(vv.__class__)
else vv,
)
for kk, vv in iteritems(v)
for kk, vv in v.items()
)
)
else:
Expand Down Expand Up @@ -328,7 +327,7 @@ def assoc(inst, **changes):
)
new = copy.copy(inst)
attrs = fields(inst.__class__)
for k, v in iteritems(changes):
for k, v in changes.items():
a = getattr(attrs, k, NOTHING)
if a is NOTHING:
raise AttrsAttributeNotFoundError(
Expand Down
23 changes: 10 additions & 13 deletions src/attr/_make.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import copy
import linecache
import sys
import types
import typing

from operator import itemgetter
Expand All @@ -15,10 +16,6 @@
PY310,
PYPY,
_AnnotationExtractor,
isclass,
iteritems,
metadata_proxy,
new_class,
ordered_dict,
set_closure_cell,
)
Expand Down Expand Up @@ -48,7 +45,7 @@
# (when slots=True)
_hash_cache_field = "_attrs_cached_hash"

_empty_metadata_singleton = metadata_proxy({})
_empty_metadata_singleton = types.MappingProxyType({})

# Unique object for unequivocal getattr() defaults.
_sentinel = object()
Expand Down Expand Up @@ -504,7 +501,7 @@ def _transform_attrs(
anns = _get_annotations(cls)

if these is not None:
ca_list = [(name, ca) for name, ca in iteritems(these)]
ca_list = [(name, ca) for name, ca in these.items()]

if not isinstance(these, ordered_dict):
ca_list.sort(key=_counter_getter)
Expand Down Expand Up @@ -795,7 +792,7 @@ def _create_slots_class(self):
"""
cd = {
k: v
for k, v in iteritems(self._cls_dict)
for k, v in self._cls_dict.items()
if k not in tuple(self._attr_names) + ("__dict__", "__weakref__")
}

Expand Down Expand Up @@ -850,7 +847,7 @@ def _create_slots_class(self):
# we collect them here and update the class dict
reused_slots = {
slot: slot_descriptor
for slot, slot_descriptor in iteritems(existing_slots)
for slot, slot_descriptor in existing_slots.items()
if slot in slot_names
}
slot_names = [name for name in slot_names if name not in reused_slots]
Expand Down Expand Up @@ -1988,7 +1985,7 @@ def fields(cls):
.. versionchanged:: 16.2.0 Returned tuple allows accessing the fields
by name.
"""
if not isclass(cls):
if not isinstance(cls, type):
raise TypeError("Passed object must be a class.")
attrs = getattr(cls, "__attrs_attrs__", None)
if attrs is None:
Expand Down Expand Up @@ -2016,7 +2013,7 @@ def fields_dict(cls):
.. versionadded:: 18.1.0
"""
if not isclass(cls):
if not isinstance(cls, type):
raise TypeError("Passed object must be a class.")
attrs = getattr(cls, "__attrs_attrs__", None)
if attrs is None:
Expand Down Expand Up @@ -2542,7 +2539,7 @@ def __init__(
bound_setattr(
"metadata",
(
metadata_proxy(metadata)
types.MappingProxyType(dict(metadata)) # Shallow copy
if metadata
else _empty_metadata_singleton
),
Expand Down Expand Up @@ -2628,7 +2625,7 @@ def _setattrs(self, name_values_pairs):
else:
bound_setattr(
name,
metadata_proxy(value)
types.MappingProxyType(dict(value))
if value
else _empty_metadata_singleton,
)
Expand Down Expand Up @@ -2904,7 +2901,7 @@ def make_class(name, attrs, bases=(object,), **attributes_arguments):
if user_init is not None:
body["__init__"] = user_init

type_ = new_class(name, bases, {}, lambda ns: ns.update(body))
type_ = types.new_class(name, bases, {}, lambda ns: ns.update(body))

# For pickling to work, the __module__ variable needs to be set to the
# frame where the class is created. Bypass this step in environments where
Expand Down
4 changes: 1 addition & 3 deletions src/attr/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
Commonly useful filters for `attr.asdict`.
"""


from ._compat import isclass
from ._make import Attribute


Expand All @@ -14,7 +12,7 @@ def _split_what(what):
Returns a tuple of `frozenset`s of classes and attributes.
"""
return (
frozenset(cls for cls in what if isclass(cls)),
frozenset(cls for cls in what if isinstance(cls, type)),
frozenset(cls for cls in what if isinstance(cls, Attribute)),
)

Expand Down
8 changes: 4 additions & 4 deletions tests/test_compat.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# SPDX-License-Identifier: MIT

import pytest
import types

from attr._compat import metadata_proxy
import pytest


@pytest.fixture(name="mp")
def _mp():
return metadata_proxy({"x": 42, "y": "foo"})
return types.MappingProxyType({"x": 42, "y": "foo"})


class TestMetadataProxy:
"""
Ensure properties of metadata_proxy independently of hypothesis strategies.
Ensure properties of metadata proxy independently of hypothesis strategies.
"""

def test_repr(self, mp):
Expand Down
4 changes: 2 additions & 2 deletions tests/test_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import attr

from attr import asdict, assoc, astuple, evolve, fields, has
from attr._compat import TYPE, Mapping, Sequence, ordered_dict
from attr._compat import Mapping, Sequence, ordered_dict
from attr.exceptions import AttrsAttributeNotFoundError
from attr.validators import instance_of

Expand Down Expand Up @@ -599,7 +599,7 @@ class C:
evolve(C(a=1), a="some string")
m = e.value.args[0]

assert m.startswith("'a' must be <{type} 'int'>".format(type=TYPE))
assert m.startswith("'a' must be <class 'int'>")

def test_private(self):
"""
Expand Down
5 changes: 2 additions & 3 deletions tests/test_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import attr

from attr._compat import PY36, TYPE
from attr._compat import PY36
from attr._make import NOTHING, Attribute
from attr.exceptions import FrozenInstanceError

Expand Down Expand Up @@ -161,8 +161,7 @@ def test_validator(self, cls):

# Using C1 explicitly, since slotted classes don't support this.
assert (
"'x' must be <{type} 'int'> (got '1' that is a <{type} "
"'str'>).".format(type=TYPE),
"'x' must be <class 'int'> (got '1' that is a <class 'str'>).",
attr.fields(C1).x,
int,
"1",
Expand Down
51 changes: 17 additions & 34 deletions tests/test_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

from attr import _config, fields, has
from attr import validators as validator_module
from attr._compat import TYPE
from attr.validators import (
and_,
deep_iterable,
Expand Down Expand Up @@ -143,8 +142,7 @@ def test_fail(self):
with pytest.raises(TypeError) as e:
v(None, a, "42")
assert (
"'test' must be <{type} 'int'> (got '42' that is a <{type} "
"'str'>).".format(type=TYPE),
"'test' must be <class 'int'> (got '42' that is a <class 'str'>).",
a,
int,
"42",
Expand All @@ -155,9 +153,7 @@ def test_repr(self):
Returned validator has a useful `__repr__`.
"""
v = instance_of(int)
assert (
"<instance_of validator for type <{type} 'int'>>".format(type=TYPE)
) == repr(v)
assert ("<instance_of validator for type <class 'int'>>") == repr(v)


class TestMatchesRe:
Expand Down Expand Up @@ -422,8 +418,7 @@ def test_fail(self, validator):
with pytest.raises(TypeError) as e:
v(None, a, "42")
assert (
"'test' must be <{type} 'int'> (got '42' that is a <{type} "
"'str'>).".format(type=TYPE),
"'test' must be <class 'int'> (got '42' that is a <class 'str'>).",
a,
int,
"42",
Expand All @@ -438,13 +433,13 @@ def test_repr(self, validator):
if isinstance(validator, list):
repr_s = (
"<optional validator for _AndValidator(_validators=[{func}, "
"<instance_of validator for type <{type} 'int'>>]) or None>"
).format(func=repr(always_pass), type=TYPE)
"<instance_of validator for type <class 'int'>>]) or None>"
).format(func=repr(always_pass))
else:
repr_s = (
"<optional validator for <instance_of validator for type "
"<{type} 'int'>> or None>"
).format(type=TYPE)
"<class 'int'>> or None>"
)

assert repr_s == repr(v)

Expand Down Expand Up @@ -608,9 +603,7 @@ def test_repr_member_only(self):
when only member validator is set.
"""
member_validator = instance_of(int)
member_repr = "<instance_of validator for type <{type} 'int'>>".format(
type=TYPE
)
member_repr = "<instance_of validator for type <class 'int'>>"
v = deep_iterable(member_validator)
expected_repr = (
"<deep_iterable validator for iterables of {member_repr}>"
Expand All @@ -626,8 +619,8 @@ def test_repr_member_only_sequence(self):
member_validator = [always_pass, instance_of(int)]
member_repr = (
"_AndValidator(_validators=({func}, "
"<instance_of validator for type <{type} 'int'>>))"
).format(func=repr(always_pass), type=TYPE)
"<instance_of validator for type <class 'int'>>))"
).format(func=repr(always_pass))
v = deep_iterable(member_validator)
expected_repr = (
"<deep_iterable validator for iterables of {member_repr}>"
Expand All @@ -640,13 +633,9 @@ def test_repr_member_and_iterable(self):
and iterable validators are set.
"""
member_validator = instance_of(int)
member_repr = "<instance_of validator for type <{type} 'int'>>".format(
type=TYPE
)
member_repr = "<instance_of validator for type <class 'int'>>"
iterable_validator = instance_of(list)
iterable_repr = (
"<instance_of validator for type <{type} 'list'>>"
).format(type=TYPE)
iterable_repr = "<instance_of validator for type <class 'list'>>"
v = deep_iterable(member_validator, iterable_validator)
expected_repr = (
"<deep_iterable validator for"
Expand All @@ -663,12 +652,10 @@ def test_repr_sequence_member_and_iterable(self):
member_validator = [always_pass, instance_of(int)]
member_repr = (
"_AndValidator(_validators=({func}, "
"<instance_of validator for type <{type} 'int'>>))"
).format(func=repr(always_pass), type=TYPE)
"<instance_of validator for type <class 'int'>>))"
).format(func=repr(always_pass))
iterable_validator = instance_of(list)
iterable_repr = (
"<instance_of validator for type <{type} 'list'>>"
).format(type=TYPE)
iterable_repr = "<instance_of validator for type <class 'list'>>"
v = deep_iterable(member_validator, iterable_validator)
expected_repr = (
"<deep_iterable validator for"
Expand Down Expand Up @@ -767,13 +754,9 @@ def test_repr(self):
Returned validator has a useful `__repr__`.
"""
key_validator = instance_of(str)
key_repr = "<instance_of validator for type <{type} 'str'>>".format(
type=TYPE
)
key_repr = "<instance_of validator for type <class 'str'>>"
value_validator = instance_of(int)
value_repr = "<instance_of validator for type <{type} 'int'>>".format(
type=TYPE
)
value_repr = "<instance_of validator for type <class 'int'>>"
v = deep_mapping(key_validator, value_validator)
expected_repr = (
"<deep_mapping validator for objects mapping "
Expand Down

0 comments on commit e18b5f3

Please sign in to comment.