Repository: python-attrs/attrs
Pull request #1374 (2024-11-18 05:59:51)

Use an explicit comparison to None for the converter of a field
# Summary

<!-- Please tell us what your pull request is about here. -->
Make attrs work with a converter instance that does not evaluate True. Other code spots already do an explicit comparison to None, so do that here as well. Failing to do so on a valid converter instance that is a valid callable but has a `__bool__()` that evaluates to False ends up trying to call `_fmt_converter_call` on that callable rather than on the Converter instance created from it.

# Pull Request Check List

<!--
This is just a friendly reminder about the most common mistakes.
Please make sure that you tick all boxes.
But please read our [contribution guide](https://github.com/python-attrs/attrs/blob/main/.github/CONTRIBUTING.md) at least once, it will save you unnecessary review cycles!

If an item doesn't apply to your pull request, **check it anyway** to make it apparent that there's nothing left to do.
If your pull request is a documentation fix or a trivial typo, feel free to delete the whole thing.
-->

- [x] Do **not** open pull requests from your `main` branch – **use a separate branch**!
  - There's a ton of footguns waiting if you don't heed this warning. You can still go back to your project, create a branch from your main branch, push it, and open the pull request from the new branch.
  - This is not a pre-requisite for your pull request to be accepted, but **you have been warned**.
- [x] Added **tests** for changed code.
  Our CI fails if coverage is not 100%.
- N/A New features have been added to our [Hypothesis testing strategy](https://github.com/python-attrs/attrs/blob/main/tests/strategies.py).
- N/A Changes or additions to public APIs are reflected in our type stubs (files ending in ``.pyi``).
- N/A Updated **documentation** for changed code.
- N/A Documentation in `.rst` and `.md` files is written using [semantic newlines](https://rhodesmill.org/brandon/2012/one-sentence-per-line/).
- [ ] Changes (and possible deprecations) have news fragments in [`changelog.d`](https://github.com/python-attrs/attrs/blob/main/changelog.d).
- [x] Consider granting [push permissions to the PR branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork), so maintainers can fix minor issues themselves without pestering you.

<!--
If you have *any* questions to *any* of the points above, just **submit and ask**!
This checklist is here to *help* you, not to deter you from contributing!
-->


filbranden: @hynek Would you kindly take a look whenever possible? Thanks!

Patch:
diff --git a/src/attr/_make.py b/src/attr/_make.py
index e0cd9e0..13cc994 100644
--- a/src/attr/_make.py
+++ b/src/attr/_make.py
@@ -2021,7 +2021,7 @@ def _attrs_to_init_script(
         has_factory = isinstance(a.default, Factory)
         maybe_self = "self" if has_factory and a.default.takes_self else ""
 
-        if a.converter and not isinstance(a.converter, Converter):
+        if a.converter is not None and not isinstance(a.converter, Converter):
             converter = Converter(a.converter)
         else:
             converter = a.converter


Test patch:
diff --git a/tests/test_converters.py b/tests/test_converters.py
index d4aca43..4747f13 100644
--- a/tests/test_converters.py
+++ b/tests/test_converters.py
@@ -112,6 +112,25 @@ class TestConverter:
         assert float is c.__call__.__annotations__["return"]
         assert None is c2.__call__.__annotations__.get("return")
 
+    def test_falsey_converter(self):
+        """
+        Passing a false-y instance still produces a valid converter.
+        """
+
+        class MyConv:
+            def __bool__(self):
+                return False
+
+            def __call__(self, value):
+                return value * 2
+
+        @attr.s
+        class C:
+            a = attrib(converter=MyConv())
+
+        c = C(21)
+        assert 42 == c.a
+
 
 class TestOptional:
     """