Skip to content

Fix cast_to_bool to treat whitespace-only strings as False#2462

Open
developreiloyo wants to merge 4 commits intocopier-org:masterfrom
developreiloyo:fix-cast-to-bool-whitespace
Open

Fix cast_to_bool to treat whitespace-only strings as False#2462
developreiloyo wants to merge 4 commits intocopier-org:masterfrom
developreiloyo:fix-cast-to-bool-whitespace

Conversation

@developreiloyo
Copy link
Copy Markdown

What does this change?

cast_to_bool was treating whitespace-only strings (e.g. " ", "\n") as True due to Python’s default bool(str) behavior.

This PR adds an explicit check to treat empty and whitespace-only strings as False.

What was done?

  • Added a guard to return False for whitespace-only strings.
  • Added a unit test covering this behavior.

Why this approach?

The change is minimal and does not alter existing behavior for numbers, YAML booleans, or standard truthy values.

Copy link
Copy Markdown
Member

@sisp sisp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution around bool casting improvements, @developreiloyo! 🙇 I've left a few inline comments.

copier/_tools.py Outdated
Comment on lines +131 to +132
# Treat whitespace-only string as False
if isinstance(value,str) and not value.strip():
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Treat whitespace-only string as False
if isinstance(value,str) and not value.strip():
# Treat whitespace-only string as False
if isinstance(value, str) and not value.strip():

Comment on lines 136 to 141
with suppress(AttributeError):
lower = value.lower()
if lower in {"y", "yes", "t", "true", "on"}:
return True
elif lower in {"n", "no", "f", "false", "off", "~", "null", "none"}:
return False
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to avoid the special case handling for whitespace-only strings and extend the string handling instead?

 with suppress(AttributeError):
-    lower = value.lower()
+    lower = value.lower().strip()
     if lower in {"y", "yes", "t", "true", "on"}:
         return True
-    elif lower in {"n", "no", "f", "false", "off", "~", "null", "none"}:
+    elif lower in {"n", "no", "f", "false", "off", "~", "null", "none", ""}:
         return False

Copy link
Copy Markdown
Author

@developreiloyo developreiloyo Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @sisp! I included "" because without it, whitespace-only strings like " " fall through to bool(value), which returns True. Since .strip() normalizes them to "", treating it as a falsy keyword feels like the cleanest fix.

Copy link
Copy Markdown
Member

@sisp sisp Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this the same as

lower = value.lower().strip()
if ...:
    ...
elif lower in {..., ""}:
    return False

but without a special case? " " would be captured by the elif branch.

Copy link
Copy Markdown
Author

@developreiloyo developreiloyo Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion I realized the redundant check wasn’t needed, so I updated the implementation to use:

if isinstance(value, str) and not value.strip():
    return False

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this is not what I meant. When we're at

copier/copier/_tools.py

Lines 130 to 136 in cd45f37

# Assume it's a string
with suppress(AttributeError):
lower = value.lower()
if lower in {"y", "yes", "t", "true", "on"}:
return True
elif lower in {"n", "no", "f", "false", "off", "~", "null", "none"}:
return False

then the value is likely a string with methods like .lower() and also .strip(). So, I was thinking we only need to also strip leading and trailing whitespaces via .strip() and add the empty string to the set of values that shall cast to False. For this, I believe we don't need the special-case handling

if isinstance(value, str) and not value.strip():
    return False

but instead can extend the string handling logic just a little bit.

- Normalize string input with .strip() before keyword matching
- Include empty string ('') in false keyword list to handle normalized whitespace
- Extend existing test to cover both whitespace-only and padded valid keywords
- Add explicit check: if isinstance(value, str) and not value.strip(): return False
- Remove "" from false keyword list
- Avoid fallback to bool(value) for whitespace-only strings
Comment on lines +130 to +134

# Handle whitespace-only strings first
if isinstance(value, str) and not value.strip():
return False

Copy link
Copy Markdown
Member

@sisp sisp Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #2462 (comment):

Suggested change
# Handle whitespace-only strings first
if isinstance(value, str) and not value.strip():
return False

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants