Skip to content

Commit f09c2c6

Browse files
authored
Merge pull request #277 from derek73/fix/254-str-none-scrub
Fix str() scrubbing literal "None" from names when empty_attribute_default is None
2 parents 770ed0a + c8b245a commit f09c2c6

3 files changed

Lines changed: 23 additions & 2 deletions

File tree

docs/release_log.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Release Log
33
* 1.3.1 - Unreleased
44

55
- Fix invisible Unicode bidirectional control characters (LRM/RLM/ALM, the embedding/override marks, and the isolates U+2066–U+2069) surviving parsing and sticking to ``first``/``last``/etc., so a copy-pasted right-to-left name silently failed equality and dedup. They are now stripped in preprocessing like emoji; disable via ``CONSTANTS.regexes.bidi = False`` (closes #266)
6+
- Fix ``str()`` corrupting name text containing the substring ``"None"`` when ``empty_attribute_default`` is ``None`` (e.g. ``"Nonez Smith"`` rendered as ``"z Smith"``): empty attributes are now substituted as ``''`` before the format string is applied, instead of scrubbing the interpolated ``"None"`` from the output afterward (closes #254)
67

78
* 1.3.0 - July 5, 2026
89

nameparser/parser.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,14 @@ def __setitem__(self, key: str, value: str | list[str] | None) -> None:
241241
def __str__(self) -> str:
242242
if self.string_format is not None:
243243
# string_format = "{title} {first} {middle} {last} {suffix} ({nickname})"
244-
_s = self.string_format.format(**self.as_dict()) # noqa: UP032
244+
# Empty attributes must render as '' (not empty_attribute_default,
245+
# which may be None) so str.format does not interpolate the
246+
# literal "None" into the output, which cannot be scrubbed
247+
# afterward without corrupting name text containing the same
248+
# substring (#254).
249+
_s = self.string_format.format(**{k: v or '' for k, v in self.as_dict().items()})
245250
# remove trailing punctuation from missing nicknames
246-
_s = _s.replace(str(self.C.empty_attribute_default), '').replace(" ()", "").replace(" ''", "").replace(' ""', "")
251+
_s = _s.replace(" ()", "").replace(" ''", "").replace(' ""', "")
247252
_s = self.C.regexes.space_before_comma.sub(',', _s)
248253
return self.collapse_whitespace(_s).strip(', ')
249254
return " ".join(self)

tests/test_output_format.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,21 @@ def test_formating_of_nicknames_in_middle(self) -> None:
9999
hn.nickname = ''
100100
self.assertEqual(str(hn), "Rev John A. Kenneth Doe III")
101101

102+
def test_name_containing_none_substring_with_none_empty_attribute_default(self) -> None:
103+
# Regression for #254: with empty_attribute_default = None, __str__
104+
# scrubbed the literal string 'None' from the formatted output,
105+
# corrupting real name text containing that substring.
106+
hn = HumanName("Nonez Smith", None)
107+
hn.C.empty_attribute_default = None # type: ignore[assignment] # see test_constants.test_empty_attribute_default
108+
self.assertEqual(str(hn), "Nonez Smith")
109+
110+
def test_name_none_as_literal_name_with_none_empty_attribute_default(self) -> None:
111+
# Companion to the #254 regression: a name piece that is exactly
112+
# 'None' must survive formatting in None-mode.
113+
hn = HumanName("None Smith", None)
114+
hn.C.empty_attribute_default = None # type: ignore[assignment] # see test_constants.test_empty_attribute_default
115+
self.assertEqual(str(hn), "None Smith")
116+
102117
def test_empty_field_drops_surrounding_whitespace(self) -> None:
103118
# issue #139: adjacent whitespace/punctuation should be dropped when a field is empty
104119
hn = HumanName("John Smith")

0 commit comments

Comments
 (0)