Skip to content

Commit

Permalink
Allow optional conversion
Browse files Browse the repository at this point in the history
This just adds a function that makes an existing converter work for optional
inputs.
  • Loading branch information
adetokunbo committed Mar 29, 2017
1 parent a48124b commit a55389a
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Expand Up @@ -36,6 +36,9 @@ Deprecations:
Changes:
^^^^^^^^

- Support optional conversion
`#105 <https://github.com/python-attrs/attrs/issues/105>`_
`#173 <https://github.com/python-attrs/attrs/pull/173>`_
- Fix default hashing behavior.
Now *hash* mirrors the value of *cmp* and classes are unhashable by default.
`#136`_
Expand Down
2 changes: 2 additions & 0 deletions src/attr/__init__.py
Expand Up @@ -23,6 +23,7 @@
)
from . import exceptions
from . import filters
from . import converters
from . import validators


Expand Down Expand Up @@ -54,6 +55,7 @@
"attrib",
"attributes",
"attrs",
"converters",
"evolve",
"exceptions",
"fields",
Expand Down
23 changes: 23 additions & 0 deletions src/attr/converters.py
@@ -0,0 +1,23 @@
"""
Commonly useful converters.
"""

from __future__ import absolute_import, division, print_function


def optional(converter):
"""
A converter that allows an attribute to be optional. An optional attribute
is one which can be set to ``None``.
:param converter: the converter that is used for non-``None`` values.
.. versionadded:: 17.1.0 *convert* can be optional
"""

def optional_converter(val):
if val is None:
return None
return converter(val)

return optional_converter
37 changes: 37 additions & 0 deletions tests/test_converters.py
@@ -0,0 +1,37 @@
"""
Tests for `attr.converters`.
"""

from __future__ import absolute_import

import pytest

from attr.converters import optional


class TestOptional(object):
"""
Tests for `optional`.
"""

def test_success_with_type(self):
"""
Nothing happens if types match.
"""
c = optional(int)
assert c('42') == 42

def test_success_with_none(self):
"""
Nothing happens if None.
"""
c = optional(int)
assert c(None) is None

def test_fail(self):
"""
Propagates the underlying conversion error when conversion fails.
"""
c = optional(int)
with pytest.raises(ValueError):
c("not_an_int")

0 comments on commit a55389a

Please sign in to comment.