From d887887cd3a41072a96f76af5e01d72e025a8281 Mon Sep 17 00:00:00 2001 From: Lucas Hoffmann Date: Wed, 6 Sep 2023 18:56:33 +0200 Subject: [PATCH 1/3] Add tests for issue #323 The failing tests are marked as expected failures. --- test/test_helpers.py | 31 +++++++++++++++++++++++++++++++ test/test_yaml_editable.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/test/test_helpers.py b/test/test_helpers.py index 17a8891..91d4556 100644 --- a/test/test_helpers.py +++ b/test/test_helpers.py @@ -20,6 +20,37 @@ def test_empty_strings_produce_empty_values(self): result = helpers.convert_to_yaml("Note", "", 0, 5, True) self.assertListEqual(result, ["Note : "]) + @unittest.expectedFailure + def test_preparing_multiple_addresses_with_same_label_for_yaml_conversion_returns_all_entries(self): + input = {'home': [{'street': 'street 1', + 'city': 'city1', + 'code': 'zip1', + 'country': ''}, + {'street': 'street 2', + 'city': 'city2', + 'code': 'zip2', + 'country': ''}]} + expected = [{'Street': 'street 1', + 'City': 'city1', + 'Code': 'zip1', + 'Country': None}, + {'Street': 'street 2', + 'City': 'city2', + 'Code': 'zip2', + 'Country': None}] + actual = helpers.yaml_addresses(input, ["Street", "Code", "City", + "Country"]) + self.assertEqual(expected, actual["home"]) + + def test_preparing_single_addresse_for_yaml_conversion_returns_dict_not_list(self): + input = {'home': [{'street': 'street', 'city': 'city', 'code': 'zip', + 'country': ''}]} + expected = {'Street': 'street', 'City': 'city', 'Code': 'zip', + 'Country': None} + actual = helpers.yaml_addresses(input, ["Street", "Code", "City", + "Country"]) + self.assertEqual(expected, actual["home"]) + if __name__ == "__main__": unittest.main() diff --git a/test/test_yaml_editable.py b/test/test_yaml_editable.py index b946013..51536f5 100644 --- a/test/test_yaml_editable.py +++ b/test/test_yaml_editable.py @@ -20,6 +20,34 @@ def test_yaml_quoted_special_characters(self): yaml_dump = yaml_editable.to_yaml() self.assertIn("'@khard'", yaml_dump) + @unittest.expectedFailure + def test_dumping_multiple_home_addresses_to_yaml(self): + yaml_editable = TestYAMLEditable() + yaml_editable._add_post_address("home", "", "", "street 1", "zip1", + "city1", "", "") + yaml_editable._add_post_address("home", "", "", "street 2", "zip2", + "city2", "", "") + yaml_dump = yaml_editable.to_yaml() + self.assertIn("zip1", yaml_dump) + self.assertIn("zip2", yaml_dump) + + def test_dumping_multiple_home_phone_number_to_yaml(self): + yaml_editable = TestYAMLEditable() + yaml_editable._add_phone_number("home", "1234567890") + yaml_editable._add_phone_number("home", "0987654321") + yaml_dump = yaml_editable.to_yaml() + self.assertIn("1234567890", yaml_dump) + self.assertIn("0987654321", yaml_dump) + + def test_dumping_multiple_home_email_addresses_to_yaml(self): + yaml_editable = TestYAMLEditable() + yaml_editable.add_email("home", "home1@example.org") + yaml_editable.add_email("home", "home2@example.org") + yaml_dump = yaml_editable.to_yaml() + self.assertIn("home1", yaml_dump) + self.assertIn("home2", yaml_dump) + + class ExceptionHandling(unittest.TestCase): From 1f2f3863613f64a40f356dbe61172e4971eca10f Mon Sep 17 00:00:00 2001 From: Lucas Hoffmann Date: Wed, 6 Sep 2023 20:06:11 +0200 Subject: [PATCH 2/3] Fix yaml conversion of multiple addresses with same label --- khard/helpers/__init__.py | 18 ++++++++++-------- test/test_helpers.py | 1 - test/test_yaml_editable.py | 1 - 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/khard/helpers/__init__.py b/khard/helpers/__init__.py index 1946c46..67b3df6 100644 --- a/khard/helpers/__init__.py +++ b/khard/helpers/__init__.py @@ -115,7 +115,7 @@ def yaml_dicts( return data_dict -def yaml_addresses(addresses: Optional[Dict[str, Any]], +def yaml_addresses(addresses: Optional[Dict[str, List]], address_properties: List[str], defaults: Optional[List[str]] = None ) -> Optional[Dict[str, Any]]: @@ -135,13 +135,15 @@ def yaml_addresses(addresses: Optional[Dict[str, Any]], return {address_type: address_fields for address_type in defaults} address_dict = {} - for address_type, address in addresses.items(): - if isinstance(address, list): - address = address[0] - address_dict[address_type] = { - key: yaml_clean(address.get(f"{key[0].lower()}{key[1:]}")) - for key in address_properties - } + for address_type, addresses_ in addresses.items(): + entry = [ + {key: yaml_clean(address.get(f"{key[0].lower()}{key[1:]}")) + for key in address_properties} + for address in addresses_ + ] + if len(entry) == 1: + entry = entry[0] + address_dict[address_type] = entry return address_dict diff --git a/test/test_helpers.py b/test/test_helpers.py index 91d4556..16eb10e 100644 --- a/test/test_helpers.py +++ b/test/test_helpers.py @@ -20,7 +20,6 @@ def test_empty_strings_produce_empty_values(self): result = helpers.convert_to_yaml("Note", "", 0, 5, True) self.assertListEqual(result, ["Note : "]) - @unittest.expectedFailure def test_preparing_multiple_addresses_with_same_label_for_yaml_conversion_returns_all_entries(self): input = {'home': [{'street': 'street 1', 'city': 'city1', diff --git a/test/test_yaml_editable.py b/test/test_yaml_editable.py index 51536f5..5e342b2 100644 --- a/test/test_yaml_editable.py +++ b/test/test_yaml_editable.py @@ -20,7 +20,6 @@ def test_yaml_quoted_special_characters(self): yaml_dump = yaml_editable.to_yaml() self.assertIn("'@khard'", yaml_dump) - @unittest.expectedFailure def test_dumping_multiple_home_addresses_to_yaml(self): yaml_editable = TestYAMLEditable() yaml_editable._add_post_address("home", "", "", "street 1", "zip1", From 5718d96dc6ce7e2959f217c09c8c3c79e889920b Mon Sep 17 00:00:00 2001 From: Lucas Hoffmann Date: Wed, 6 Sep 2023 20:36:23 +0200 Subject: [PATCH 3/3] Fix type signatures --- khard/carddav_object.py | 8 ++++---- khard/helpers/__init__.py | 16 ++++++++++------ khard/helpers/typing.py | 3 ++- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/khard/carddav_object.py b/khard/carddav_object.py index a7c6456..24df4df 100644 --- a/khard/carddav_object.py +++ b/khard/carddav_object.py @@ -24,8 +24,8 @@ from . import address_book # pylint: disable=unused-import # for type checking from . import helpers -from .helpers.typing import (convert_to_vcard, Date, ObjectType, StrList, - list_to_string, string_to_date, string_to_list) +from .helpers.typing import (Date, ObjectType, PostAddress, StrList, + convert_to_vcard, list_to_string, string_to_date, string_to_list) from .query import AnyQuery, Query @@ -770,11 +770,11 @@ def add_email(self, type: str, address: str) -> None: label_obj.value = custom_types[0] @property - def post_addresses(self) -> Dict[str, List[Dict[str, Union[List, str]]]]: + def post_addresses(self) -> Dict[str, List[PostAddress]]: """ :returns: dict of type and post address list """ - post_adr_dict: Dict[str, List[Dict[str, Union[List, str]]]] = {} + post_adr_dict: Dict[str, List[PostAddress]] = {} for child in self.vcard.getChildren(): if child.name == "ADR": type = list_to_string(self._get_types_for_vcard_object( diff --git a/khard/helpers/__init__.py b/khard/helpers/__init__.py index 67b3df6..e549730 100644 --- a/khard/helpers/__init__.py +++ b/khard/helpers/__init__.py @@ -7,7 +7,10 @@ from typing import Any, Dict, List, Optional, Sequence, Union from ruamel.yaml.scalarstring import LiteralScalarString -from .typing import list_to_string +from .typing import list_to_string, PostAddress + + +YamlPostAddresses = Dict[str, Union[List[Dict[str, Any]], Dict[str, Any]]] def pretty_print(table: List[List[str]], justify: str = "L") -> str: @@ -115,10 +118,10 @@ def yaml_dicts( return data_dict -def yaml_addresses(addresses: Optional[Dict[str, List]], +def yaml_addresses(addresses: Optional[Dict[str, List[PostAddress]]], address_properties: List[str], defaults: Optional[List[str]] = None - ) -> Optional[Dict[str, Any]]: + ) -> Optional[YamlPostAddresses]: """ build a dict from an address, using a list of properties, an address has. @@ -134,7 +137,7 @@ def yaml_addresses(addresses: Optional[Dict[str, List]], address_fields = {key: None for key in address_properties} return {address_type: address_fields for address_type in defaults} - address_dict = {} + address_dict: YamlPostAddresses = {} for address_type, addresses_ in addresses.items(): entry = [ {key: yaml_clean(address.get(f"{key[0].lower()}{key[1:]}")) @@ -142,8 +145,9 @@ def yaml_addresses(addresses: Optional[Dict[str, List]], for address in addresses_ ] if len(entry) == 1: - entry = entry[0] - address_dict[address_type] = entry + address_dict[address_type] = entry[0] + else: + address_dict[address_type] = entry return address_dict diff --git a/khard/helpers/typing.py b/khard/helpers/typing.py index 9afd025..1ea67c4 100644 --- a/khard/helpers/typing.py +++ b/khard/helpers/typing.py @@ -2,7 +2,7 @@ from datetime import datetime from enum import Enum -from typing import List, Union +from typing import Dict, List, Union class ObjectType(Enum): @@ -14,6 +14,7 @@ class ObjectType(Enum): # some type aliases Date = Union[str, datetime] StrList = Union[str, List[str]] +PostAddress = Dict[str, str] def convert_to_vcard(name: str, value: StrList, constraint: ObjectType