From 6533f5508e59bde120984f2f48ebf10af9bc7c79 Mon Sep 17 00:00:00 2001 From: "R. Alex Matevish" Date: Wed, 13 Feb 2019 22:44:21 -0800 Subject: [PATCH 1/4] Remove code and tests related to deprecated 'convert' kwarg --- src/attr/__init__.pyi | 4 -- src/attr/_make.py | 43 +-------------- tests/test_make.py | 118 ++---------------------------------------- 3 files changed, 4 insertions(+), 161 deletions(-) diff --git a/src/attr/__init__.pyi b/src/attr/__init__.pyi index ea788ea3c..8de9056e0 100644 --- a/src/attr/__init__.pyi +++ b/src/attr/__init__.pyi @@ -93,7 +93,6 @@ def attrib( cmp: bool = ..., hash: Optional[bool] = ..., init: bool = ..., - convert: None = ..., metadata: Optional[Mapping[Any, Any]] = ..., type: None = ..., converter: None = ..., @@ -110,7 +109,6 @@ def attrib( cmp: bool = ..., hash: Optional[bool] = ..., init: bool = ..., - convert: Optional[_ConverterType[_T]] = ..., metadata: Optional[Mapping[Any, Any]] = ..., type: Optional[Type[_T]] = ..., converter: Optional[_ConverterType[_T]] = ..., @@ -127,7 +125,6 @@ def attrib( cmp: bool = ..., hash: Optional[bool] = ..., init: bool = ..., - convert: Optional[_ConverterType[_T]] = ..., metadata: Optional[Mapping[Any, Any]] = ..., type: Optional[Type[_T]] = ..., converter: Optional[_ConverterType[_T]] = ..., @@ -144,7 +141,6 @@ def attrib( cmp: bool = ..., hash: Optional[bool] = ..., init: bool = ..., - convert: Optional[_ConverterType[_T]] = ..., metadata: Optional[Mapping[Any, Any]] = ..., type: object = ..., converter: Optional[_ConverterType[_T]] = ..., diff --git a/src/attr/_make.py b/src/attr/_make.py index 1373536af..6b4f7ee51 100644 --- a/src/attr/_make.py +++ b/src/attr/_make.py @@ -74,7 +74,6 @@ def attrib( cmp=True, hash=None, init=True, - convert=None, metadata=None, type=None, converter=None, @@ -172,26 +171,13 @@ def attrib( .. versionadded:: 18.1.0 ``factory=f`` is syntactic sugar for ``default=attr.Factory(f)``. .. versionadded:: 18.2.0 *kw_only* + .. versionchanged:: 19.0.1 *convert* removed """ if hash is not None and hash is not True and hash is not False: raise TypeError( "Invalid value for hash. Must be True, False, or None." ) - if convert is not None: - if converter is not None: - raise RuntimeError( - "Can't pass both `convert` and `converter`. " - "Please use `converter` only." - ) - warnings.warn( - "The `convert` argument is deprecated in favor of `converter`. " - "It will be removed after 2019/01.", - DeprecationWarning, - stacklevel=2, - ) - converter = convert - if factory is not None: if default is not NOTHING: raise ValueError( @@ -1682,7 +1668,6 @@ def __init__( cmp, hash, init, - convert=None, metadata=None, type=None, converter=None, @@ -1693,20 +1678,6 @@ def __init__( # Despite the big red warning, people *do* instantiate `Attribute` # themselves. - if convert is not None: - if converter is not None: - raise RuntimeError( - "Can't pass both `convert` and `converter`. " - "Please use `converter` only." - ) - warnings.warn( - "The `convert` argument is deprecated in favor of `converter`." - " It will be removed after 2019/01.", - DeprecationWarning, - stacklevel=2, - ) - converter = convert - bound_setattr("name", name) bound_setattr("default", default) bound_setattr("validator", validator) @@ -1729,16 +1700,6 @@ def __init__( def __setattr__(self, name, value): raise FrozenInstanceError() - @property - def convert(self): - warnings.warn( - "The `convert` attribute is deprecated in favor of `converter`. " - "It will be removed after 2019/01.", - DeprecationWarning, - stacklevel=2, - ) - return self.converter - @classmethod def from_counting_attr(cls, name, ca, type=None): # type holds the annotated value. deal with conflicts: @@ -1757,7 +1718,6 @@ def from_counting_attr(cls, name, ca, type=None): "validator", "default", "type", - "convert", ) # exclude methods and deprecated alias } return cls( @@ -1820,7 +1780,6 @@ def _setattrs(self, name_values_pairs): init=True, ) for name in Attribute.__slots__ - if name != "convert" # XXX: remove once `convert` is gone ] Attribute = _add_hash( diff --git a/tests/test_make.py b/tests/test_make.py index d02c12420..61cf965b2 100644 --- a/tests/test_make.py +++ b/tests/test_make.py @@ -143,76 +143,6 @@ def f(self): assert Factory(f, True) == a._default -class TestAttribute(object): - """ - Tests for `attr.Attribute`. - """ - - def test_deprecated_convert_argument(self): - """ - Using *convert* raises a DeprecationWarning and sets the converter - field. - """ - - def conv(v): - return v - - with pytest.warns(DeprecationWarning) as wi: - a = Attribute( - "a", True, True, True, True, True, True, convert=conv - ) - w = wi.pop() - - assert conv == a.converter - assert ( - "The `convert` argument is deprecated in favor of `converter`. " - "It will be removed after 2019/01.", - ) == w.message.args - assert __file__ == w.filename - - def test_deprecated_convert_attribute(self): - """ - If Attribute.convert is accessed, a DeprecationWarning is raised. - """ - - def conv(v): - return v - - a = simple_attr("a", converter=conv) - with pytest.warns(DeprecationWarning) as wi: - convert = a.convert - w = wi.pop() - - assert conv is convert is a.converter - assert ( - "The `convert` attribute is deprecated in favor of `converter`. " - "It will be removed after 2019/01.", - ) == w.message.args - assert __file__ == w.filename - - def test_convert_converter(self): - """ - A TypeError is raised if both *convert* and *converter* are passed. - """ - with pytest.raises(RuntimeError) as ei: - Attribute( - "a", - True, - True, - True, - True, - True, - True, - convert=lambda v: v, - converter=lambda v: v, - ) - - assert ( - "Can't pass both `convert` and `converter`. " - "Please use `converter` only.", - ) == ei.value.args - - def make_tc(): class TransformC(object): z = attr.ib() @@ -1072,7 +1002,7 @@ def test_convert(self): @given(integers(), booleans()) def test_convert_property(self, val, init): """ - Property tests for attributes with convert. + Property tests for attributes using converter. """ C = make_class( "C", @@ -1089,9 +1019,9 @@ def test_convert_property(self, val, init): assert c.y == 2 @given(integers(), booleans()) - def test_convert_factory_property(self, val, init): + def test_converter_factory_property(self, val, init): """ - Property tests for attributes with convert, and a factory default. + Property tests for attributes with converter, and a factory default. """ C = make_class( "C", @@ -1164,48 +1094,6 @@ def test_frozen(self): ) C("1") - def test_deprecated_convert(self): - """ - Using *convert* raises a DeprecationWarning and sets the converter - field. - """ - - def conv(v): - return v - - with pytest.warns(DeprecationWarning) as wi: - - @attr.s - class C(object): - x = attr.ib(convert=conv) - - convert = fields(C).x.convert - - assert 2 == len(wi.list) - w = wi.pop() - - assert conv == fields(C).x.converter == convert - assert ( - "The `convert` argument is deprecated in favor of `converter`. " - "It will be removed after 2019/01.", - ) == w.message.args - assert __file__ == w.filename - - def test_convert_converter(self): - """ - A TypeError is raised if both *convert* and *converter* are passed. - """ - with pytest.raises(RuntimeError) as ei: - - @attr.s - class C(object): - x = attr.ib(convert=lambda v: v, converter=lambda v: v) - - assert ( - "Can't pass both `convert` and `converter`. " - "Please use `converter` only.", - ) == ei.value.args - class TestValidate(object): """ From b1653133b04207e4546d943d21d87b2a83bd7e71 Mon Sep 17 00:00:00 2001 From: "R. Alex Matevish" Date: Wed, 13 Feb 2019 22:59:24 -0800 Subject: [PATCH 2/4] Updated documentation, added changelog.d entry --- changelog.d/504.breaking.rst | 2 ++ docs/examples.rst | 2 +- docs/extending.rst | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 changelog.d/504.breaking.rst diff --git a/changelog.d/504.breaking.rst b/changelog.d/504.breaking.rst new file mode 100644 index 000000000..d68c4a7d4 --- /dev/null +++ b/changelog.d/504.breaking.rst @@ -0,0 +1,2 @@ +Removed deprecated ``Attribute`` attribute ``convert`` per scheduled removal on 2019/1. +This planned deprecation is tracked in issue `#307 `_. diff --git a/docs/examples.rst b/docs/examples.rst index 03a002cc0..b54637eb4 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -210,7 +210,7 @@ If you don't set ``kw_only=True``, then there's is no valid attribute ordering a ... b = attr.ib() Traceback (most recent call last): ... - ValueError: No mandatory attributes allowed after an attribute with a default value or factory. Attribute in question: Attribute(name='b', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, convert=None, metadata=mappingproxy({}), type=None, kw_only=False) + ValueError: No mandatory attributes allowed after an attribute with a default value or factory. Attribute in question: Attribute(name='b', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, converter=None, metadata=mappingproxy({}), type=None, kw_only=False) .. _asdict: diff --git a/docs/extending.rst b/docs/extending.rst index 7b185e8ff..30d85ed72 100644 --- a/docs/extending.rst +++ b/docs/extending.rst @@ -101,10 +101,10 @@ Here are some tips for effective use of metadata: >>> MY_TYPE_METADATA = '__my_type_metadata' >>> - >>> def typed(cls, default=attr.NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, convert=None, metadata={}): + >>> def typed(cls, default=attr.NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, converter=None, metadata={}): ... metadata = dict() if not metadata else metadata ... metadata[MY_TYPE_METADATA] = cls - ... return attr.ib(default, validator, repr, cmp, hash, init, convert, metadata) + ... return attr.ib(default, validator, repr, cmp, hash, init, converter, metadata) >>> >>> @attr.s ... class C(object): From f416bcacee729b5fa878e8c65a8eb90fa7ff48fd Mon Sep 17 00:00:00 2001 From: "R. Alex Matevish" Date: Mon, 4 Mar 2019 20:48:18 -0800 Subject: [PATCH 3/4] Fix attrib signature in docstring --- docs/extending.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/extending.rst b/docs/extending.rst index 30d85ed72..fe6883cf2 100644 --- a/docs/extending.rst +++ b/docs/extending.rst @@ -101,10 +101,10 @@ Here are some tips for effective use of metadata: >>> MY_TYPE_METADATA = '__my_type_metadata' >>> - >>> def typed(cls, default=attr.NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, converter=None, metadata={}): + >>> def typed(cls, default=attr.NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata={}, type=None, converter=None): ... metadata = dict() if not metadata else metadata ... metadata[MY_TYPE_METADATA] = cls - ... return attr.ib(default, validator, repr, cmp, hash, init, converter, metadata) + ... return attr.ib(default, validator, repr, cmp, hash, init, metadata, type, converter) >>> >>> @attr.s ... class C(object): From 56bab8d781366765531a693f40183e5ebb8c15d5 Mon Sep 17 00:00:00 2001 From: "R. Alex Matevish" Date: Mon, 4 Mar 2019 20:51:55 -0800 Subject: [PATCH 4/4] Update docstring version number for removal of 'convert' kw argument in attrib --- src/attr/_make.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/attr/_make.py b/src/attr/_make.py index 7bd933228..24b524e11 100644 --- a/src/attr/_make.py +++ b/src/attr/_make.py @@ -171,7 +171,7 @@ def attrib( .. versionadded:: 18.1.0 ``factory=f`` is syntactic sugar for ``default=attr.Factory(f)``. .. versionadded:: 18.2.0 *kw_only* - .. versionchanged:: 19.0.1 *convert* removed + .. versionchanged:: 19.2.0 *convert* keyword argument removed """ if hash is not None and hash is not True and hash is not False: raise TypeError(