Skip to content

Commit 3c07f98

Browse files
committed
Make get_first nullable
1 parent 2e3bd47 commit 3c07f98

File tree

4 files changed

+27
-18
lines changed

4 files changed

+27
-18
lines changed

khard/contacts.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,19 @@ def __init__(self, vcard: vobject.base.Component,
112112
def __str__(self) -> str:
113113
return self.formatted_name
114114

115-
def get_first(self, property: str, default: str = "") -> str:
115+
@overload
116+
def get_first(self, property: Literal["n"]) -> Optional[vobject.vcard.Name]: ...
117+
@overload
118+
def get_first(self, property: Literal["adr"]) -> Optional[vobject.vcard.Address]: ...
119+
@overload
120+
def get_first(self, property: str) -> Optional[str]: ...
121+
def get_first(self, property: str) -> Union[None, str, vobject.vcard.Name,
122+
vobject.vcard.Address]:
116123
"""Get a property from the underlying vCard.
117124
118125
This method should only be called for properties with cardinality \\*1
119126
(zero or one). Otherwise only the first value will be returned. If the
120-
property is not present a default will be returned.
127+
property is not present None will be retuned.
121128
122129
The type annotation for the return value is str but this is not
123130
enforced so it is up to the caller to make sure to only call this
@@ -132,7 +139,7 @@ def get_first(self, property: str, default: str = "") -> str:
132139
try:
133140
return getattr(self.vcard, property).value
134141
except AttributeError:
135-
return default
142+
return None
136143

137144
def _get_multi_property(self, name: str) -> list:
138145
"""Get a vCard property that can exist more than once.
@@ -259,7 +266,7 @@ def _get_types_for_vcard_object(self, object: vobject.base.ContentLine,
259266
return [default_type]
260267

261268
@property
262-
def version(self) -> str:
269+
def version(self) -> Optional[str]:
263270
return self.get_first("version")
264271

265272
@version.setter
@@ -274,7 +281,7 @@ def version(self, value: str) -> None:
274281
version.value = convert_to_vcard("version", value, ObjectType.str)
275282

276283
@property
277-
def uid(self) -> str:
284+
def uid(self) -> Optional[str]:
278285
return self.get_first("uid")
279286

280287
@uid.setter
@@ -470,7 +477,7 @@ def _prepare_birthday_value(self, date: Date) -> tuple[Optional[str],
470477

471478
@property
472479
def kind(self) -> str:
473-
return self.get_first(self._kind_attribute_name().lower(), self._default_kind)
480+
return self.get_first(self._kind_attribute_name().lower()) or self._default_kind
474481

475482
@kind.setter
476483
def kind(self, value: str) -> None:
@@ -487,10 +494,15 @@ def _kind_attribute_name(self) -> str:
487494

488495
@property
489496
def formatted_name(self) -> str:
490-
return self.get_first("fn")
497+
fn = self.get_first("fn")
498+
if fn:
499+
return fn
500+
self.formatted_name = ""
501+
return self.get_first("fn") or ""
491502

492503
@formatted_name.setter
493504
def formatted_name(self, value: str) -> None:
505+
# TODO cardinality 1*
494506
"""Set the FN field to the new value.
495507
496508
All previously existing FN fields are deleted. Version 4 of the specs
@@ -1303,9 +1315,11 @@ def to_yaml(self) -> str:
13031315
"Note": self.notes,
13041316
"Webpage": self.webpages,
13051317
"Anniversary":
1306-
helpers.yaml_anniversary(self.anniversary, self.version),
1318+
helpers.yaml_anniversary(self.anniversary, self.version or
1319+
self._default_version),
13071320
"Birthday":
1308-
helpers.yaml_anniversary(self.birthday, self.version),
1321+
helpers.yaml_anniversary(self.birthday, self.version or
1322+
self._default_version),
13091323
"Address": helpers.yaml_addresses(
13101324
self.post_addresses, ["Box", "Extended", "Street", "Code",
13111325
"City", "Region", "Country"], defaults=["home"])

khard/khard.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,9 @@ def list_contacts(vcard_list: list[Contact], fields: Iterable[str] = (),
210210
row.append(formatter.get_special_field(vcard, field))
211211
elif field == 'uid':
212212
if parsable:
213-
row.append(vcard.uid)
214-
elif abook_collection.get_short_uid(vcard.uid):
215-
row.append(abook_collection.get_short_uid(vcard.uid))
213+
row.append(vcard.uid or "")
214+
elif uid := abook_collection.get_short_uid(vcard.uid or ""):
215+
row.append(uid)
216216
else:
217217
row.append("")
218218
else:

test/test_query.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,7 @@ def test_and_queries_match_after_sorting(self):
105105

106106

107107
class TestFieldQuery(unittest.TestCase):
108-
@unittest.expectedFailure
109108
def test_empty_field_values_match_if_the_field_is_present(self):
110-
# This test currently fails because the Contact class has all
111-
# attributes set because they are properties. So the test in the query
112-
# class if an attribute is present never fails.
113109
uid = "Some Test Uid"
114110
vcard1 = TestContact(uid=uid)
115111
vcard2 = TestContact()

test/test_vcard_wrapper.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -564,8 +564,7 @@ def test_get_only_the_first_property(self):
564564

565565
def test_returns_the_default(self):
566566
wrapper = TestVCardWrapper()
567-
self.assertEqual(wrapper.get_first("title"), "")
568-
self.assertEqual(wrapper.get_first("title", "foo"), "foo")
567+
self.assertIsNone(wrapper.get_first("title"))
569568

570569
def test_can_return_any_value_contradicting_type_annotation(self):
571570
"""This is discouraged!"""

0 commit comments

Comments
 (0)