From c6a2575aa94d753d6a16cb44eeb74487c165277d Mon Sep 17 00:00:00 2001 From: Lucas Hoffmann Date: Fri, 20 Dec 2024 18:10:08 +0100 Subject: [PATCH] Rename CarddavObject class and module Khard is a vcards based address book and does not directly interact with the carddav protocol. The name was long and misleading. --- khard/address_book.py | 12 ++-- khard/cli.py | 6 +- khard/{carddav_object.py => contacts.py} | 24 +++---- khard/formatter.py | 8 +-- khard/helpers/interactive.py | 10 +-- khard/khard.py | 62 +++++++++---------- khard/query.py | 30 ++++----- test/helpers.py | 16 ++--- test/test_address_book.py | 2 +- ...est_carddav_object.py => test_contacts.py} | 20 +++--- test/test_formatter.py | 4 +- test/test_khard.py | 12 ++-- test/test_query.py | 22 +++---- test/test_vcard_wrapper.py | 4 +- test/test_yaml.py | 34 +++++----- test/test_yaml_editable.py | 2 +- 16 files changed, 134 insertions(+), 134 deletions(-) rename khard/{carddav_object.py => contacts.py} (98%) rename test/{test_carddav_object.py => test_contacts.py} (78%) diff --git a/khard/address_book.py b/khard/address_book.py index a99d129..3d90b39 100644 --- a/khard/address_book.py +++ b/khard/address_book.py @@ -10,7 +10,7 @@ import vobject.base -from . import carddav_object +from . import contacts from .exceptions import AddressBookParseError from .query import AnyQuery, Query @@ -24,9 +24,9 @@ class AddressBook(metaclass=abc.ABCMeta): def __init__(self, name: str) -> None: """:param name: the name to identify the address book""" self._loaded = False - self.contacts: dict[str, "carddav_object.CarddavObject"] = {} + self.contacts: dict[str, "contacts.Contact"] = {} self._short_uids: Optional[dict[str, - "carddav_object.CarddavObject"]] = None + "contacts.Contact"]] = None self.name = name def __str__(self) -> str: @@ -49,7 +49,7 @@ def _compare_uids(uid1: str, uid2: str) -> int: """ return len(os.path.commonprefix((uid1, uid2))) - def search(self, query: Query) -> Generator["carddav_object.CarddavObject", + def search(self, query: Query) -> Generator["contacts.Contact", None, None]: """Search this address book for contacts matching the query. @@ -66,7 +66,7 @@ def search(self, query: Query) -> Generator["carddav_object.CarddavObject", yield contact def get_short_uid_dict(self, query: Query = AnyQuery()) -> dict[ - str, "carddav_object.CarddavObject"]: + str, "contacts.Contact"]: """Create a dictionary of shortened UIDs for all contacts. All arguments are only used if the address book is not yet initialized @@ -173,7 +173,7 @@ def load(self, query: Query = AnyQuery(), errors = 0 for filename in glob.glob(os.path.join(self.path, "*.vcf")): try: - card = carddav_object.CarddavObject.from_file( + card = contacts.Contact.from_file( self, filename, query if search_in_source_files else AnyQuery(), self._private_objects, self._localize_dates) diff --git a/khard/cli.py b/khard/cli.py index 592fdea..55f98a7 100644 --- a/khard/cli.py +++ b/khard/cli.py @@ -6,7 +6,7 @@ from .actions import Actions from .exceptions import ConfigError -from .carddav_object import CarddavObject +from .contacts import Contact from .config import Config from .query import AndQuery, AnyQuery, parse from .version import version as khard_version @@ -62,7 +62,7 @@ def create_parsers() -> tuple[argparse.ArgumentParser, # Create the base argument parser. It will be reused for the first and # second round of argument parsing. base = argparse.ArgumentParser( - description="Khard is a carddav address book for the console", + description="Khard is a vcard address book for the console", formatter_class=argparse.RawTextHelpFormatter, add_help=False) base.add_argument("-c", "--config", help="config file to use") base.add_argument("--debug", action="store_true", @@ -188,7 +188,7 @@ def create_parsers() -> tuple[argparse.ArgumentParser, help="Machine readable format: uid\\tcontact_name\\taddress_book_name") field_argument = FieldsArgument( 'index', 'name', 'phone', 'email', 'address_book', - *CarddavObject.get_properties(), nested=True) + *Contact.get_properties(), nested=True) list_parser.add_argument( "-F", "--fields", default=[], type=field_argument, help="Comma separated list of fields to show " diff --git a/khard/carddav_object.py b/khard/contacts.py similarity index 98% rename from khard/carddav_object.py rename to khard/contacts.py index 27766b6..159cda4 100644 --- a/khard/carddav_object.py +++ b/khard/contacts.py @@ -1331,7 +1331,7 @@ def to_yaml(self) -> str: return stream.getvalue() + "\n" -class CarddavObject(YAMLEditable): +class Contact(YAMLEditable): def __init__(self, vcard: vobject.base.Component, address_book: "address_book.VdirAddressBook", filename: str, @@ -1364,8 +1364,8 @@ def __init__(self, vcard: vobject.base.Component, def new(cls, address_book: "address_book.VdirAddressBook", supported_private_objects: Optional[list[str]] = None, version: Optional[str] = None, localize_dates: bool = False - ) -> "CarddavObject": - """Create a new CarddavObject from scratch""" + ) -> "Contact": + """Create a new Contact from scratch""" vcard = vobject.vCard() uid = helpers.get_random_uid() filename = os.path.join(address_book.path, uid + ".vcf") @@ -1378,8 +1378,8 @@ def new(cls, address_book: "address_book.VdirAddressBook", def from_file(cls, address_book: "address_book.VdirAddressBook", filename: str, query: Query = AnyQuery(), supported_private_objects: Optional[list[str]] = None, - localize_dates: bool = False) -> Optional["CarddavObject"]: - """Load a CarddavObject object from a .vcf file if the plain file + localize_dates: bool = False) -> Optional["Contact"]: + """Load a Contact object from a .vcf file if the plain file matches the query. :param address_book: the address book where this contact is stored @@ -1391,7 +1391,7 @@ def from_file(cls, address_book: "address_book.VdirAddressBook", object :param localize_dates: should the formatted output of anniversary and birthday be localized or should the iso format be used instead - :returns: the loaded CarddavObject or None if the file didn't match + :returns: the loaded Contact or None if the file didn't match """ with open(filename, "r") as file: contents = file.read() @@ -1411,7 +1411,7 @@ def from_file(cls, address_book: "address_book.VdirAddressBook", def from_yaml(cls, address_book: "address_book.VdirAddressBook", yaml: str, supported_private_objects: Optional[list[str]] = None, version: Optional[str] = None, localize_dates: bool = False - ) -> "CarddavObject": + ) -> "Contact": """Use this if you want to create a new contact from user input.""" contact = cls.new(address_book, supported_private_objects, version, localize_dates=localize_dates) @@ -1419,9 +1419,9 @@ def from_yaml(cls, address_book: "address_book.VdirAddressBook", yaml: str, return contact @classmethod - def clone_with_yaml_update(cls, contact: "CarddavObject", yaml: str, + def clone_with_yaml_update(cls, contact: "Contact", yaml: str, localize_dates: bool = False - ) -> "CarddavObject": + ) -> "Contact": """ Use this if you want to clone an existing contact and replace its data with new user input in one step. @@ -1439,7 +1439,7 @@ def clone_with_yaml_update(cls, contact: "CarddavObject", yaml: str, ###################################### def __eq__(self, other: object) -> bool: - return isinstance(other, CarddavObject) and \ + return isinstance(other, Contact) and \ self.pretty(False) == other.pretty(False) def __ne__(self, other: object) -> bool: @@ -1566,5 +1566,5 @@ def delete_vcard_file(self) -> None: @classmethod def get_properties(cls) -> list[str]: """Return the property names that are defined on this class.""" - return [name for name in dir(CarddavObject) - if isinstance(getattr(CarddavObject, name), property)] + return [name for name in dir(Contact) + if isinstance(getattr(Contact, name), property)] diff --git a/khard/formatter.py b/khard/formatter.py index 7470114..a5d34f3 100644 --- a/khard/formatter.py +++ b/khard/formatter.py @@ -1,11 +1,11 @@ """Formatting and sorting of contacts""" -from .carddav_object import CarddavObject +from .contacts import Contact class Formatter: - """A formatter for CarddavObject. + """A formatter for Contact. It receives some settings on initialisation which influence the formatting of the contact. @@ -48,7 +48,7 @@ def format_labeled_field(field: dict[str, list[str]], preferred: list[str] first_key = sorted(keys, key=lambda k: k.lower())[0] return "{}: {}".format(first_key, sorted(field.get(first_key, []))[0]) - def get_special_field(self, vcard: CarddavObject, field: str) -> str: + def get_special_field(self, vcard: Contact, field: str) -> str: """Returns certain fields with specific formatting options (for support of some list command options).""" if field == 'name': @@ -74,7 +74,7 @@ def get_special_field(self, vcard: CarddavObject, field: str) -> str: return "" @staticmethod - def get_nested_field(vcard: CarddavObject, field: str) -> str: + def get_nested_field(vcard: Contact, field: str) -> str: """Returns the value of a nested field from a string get_nested_field(vcard,'emails.home.1') is equivalent to diff --git a/khard/helpers/interactive.py b/khard/helpers/interactive.py index 2e9cae1..9144126 100644 --- a/khard/helpers/interactive.py +++ b/khard/helpers/interactive.py @@ -9,7 +9,7 @@ from typing import Callable, Generator, Optional, Sequence, TypeVar, Union from ..exceptions import Cancelled -from ..carddav_object import CarddavObject +from ..contacts import Contact T = TypeVar("T") @@ -162,16 +162,16 @@ def edit_files(self, file1: str, file2: Optional[str] = None) -> EditState: return EditState.unmodified return EditState.modified - def edit_templates(self, yaml2card: Callable[[str], CarddavObject], + def edit_templates(self, yaml2card: Callable[[str], Contact], template1: str, template2: Optional[str] = None - ) -> Optional[CarddavObject]: + ) -> Optional[Contact]: """Edit YAML templates of contacts and parse them back :param yaml2card: a function to convert the modified YAML templates - into a CarddavObject + into a Contact :param template1: the first template :param template2: the second template (optional, for merges) - :returns: the parsed CarddavObject or None + :returns: the parsed Contact or None """ templates = [t for t in (template1, template2) if t is not None] with contextlib.ExitStack() as stack: diff --git a/khard/khard.py b/khard/khard.py index 65061e4..109defc 100644 --- a/khard/khard.py +++ b/khard/khard.py @@ -17,7 +17,7 @@ from . import helpers from .address_book import AddressBookCollection, VdirAddressBook from .exceptions import AddressBookNameError, AddressBookParseError, Cancelled -from .carddav_object import CarddavObject +from .contacts import Contact from . import cli from .config import Config from .formatter import Formatter @@ -33,7 +33,7 @@ ExitStatus = Union[str, int, None] -def version_check(contact: CarddavObject, description: str) -> bool: +def version_check(contact: Contact, description: str) -> bool: if contact.version not in config.supported_vcard_versions: print("Warning:\nThe {} is based on vcard version {} but khard only " "supports the modification of vcards with version 3.0 and 4.0.\n" @@ -54,11 +54,11 @@ def create_new_contact(address_book: VdirAddressBook) -> None: "{}\n# if you want to cancel, exit without saving\n\n{}".format( address_book, config.preferred_vcard_version, helpers.get_new_contact_template(config.private_objects)) - new_contact = editor.edit_templates(lambda t: CarddavObject.from_yaml( + new_contact = editor.edit_templates(lambda t: Contact.from_yaml( address_book, t, config.private_objects, config.preferred_vcard_version, config.localize_dates), template) - # create carddav object from temp file + # create contact object from temp file if new_contact is None: print("Canceled") else: @@ -66,7 +66,7 @@ def create_new_contact(address_book: VdirAddressBook) -> None: print("Creation successful\n\n{}".format(new_contact.pretty())) -def modify_existing_contact(old_contact: CarddavObject) -> None: +def modify_existing_contact(old_contact: Contact) -> None: editor = interactive.Editor(config.editor, config.merge_editor) # create temp file and open it with the specified text editor text = ("# Edit contact: {}\n# Address book: {}\n# Vcard version: {}\n" @@ -74,7 +74,7 @@ def modify_existing_contact(old_contact: CarddavObject) -> None: old_contact, old_contact.address_book, old_contact.version, old_contact.to_yaml())) new_contact = editor.edit_templates( - lambda t: CarddavObject.clone_with_yaml_update( + lambda t: Contact.clone_with_yaml_update( old_contact, t, config.localize_dates), text) # check if the user changed anything @@ -85,8 +85,8 @@ def modify_existing_contact(old_contact: CarddavObject) -> None: print("Modification successful\n\n{}".format(new_contact.pretty())) -def merge_existing_contacts(source_contact: CarddavObject, - target_contact: CarddavObject, +def merge_existing_contacts(source_contact: Contact, + target_contact: Contact, delete_source_contact: bool) -> None: # show warning, if target vCard version is not 3.0 or 4.0 if not version_check(target_contact, "target contact in which to merge"): @@ -102,7 +102,7 @@ def merge_existing_contacts(source_contact: CarddavObject, target_contact, target_contact.address_book, target_contact.version, target_contact.to_yaml())) merged_contact = editor.edit_templates( - lambda t: CarddavObject.clone_with_yaml_update( + lambda t: Contact.clone_with_yaml_update( target_contact, t, config.localize_dates), src_text, target_text) # compare them if merged_contact is None or target_contact == merged_contact: @@ -129,7 +129,7 @@ def merge_existing_contacts(source_contact: CarddavObject, print("Merge successful\n\n{}".format(merged_contact.pretty())) -def copy_contact(contact: CarddavObject, target_address_book: VdirAddressBook, +def copy_contact(contact: Contact, target_address_book: VdirAddressBook, delete_source_contact: bool) -> None: source_contact_filename = "" if delete_source_contact: @@ -161,7 +161,7 @@ def list_address_books(address_books: Union[AddressBookCollection, print(helpers.pretty_print(table)) -def list_contacts(vcard_list: list[CarddavObject], fields: Iterable[str] = (), +def list_contacts(vcard_list: list[Contact], fields: Iterable[str] = (), parsable: bool = False) -> None: selected_address_books: list[VdirAddressBook] = [] selected_kinds = set() @@ -174,7 +174,7 @@ def list_contacts(vcard_list: list[CarddavObject], fields: Iterable[str] = (), # default table header table_header = ["index", "name", "phone", "email"] plural = "" - if config.show_kinds or len(selected_kinds) > 1 or CarddavObject._default_kind not in selected_kinds: + if config.show_kinds or len(selected_kinds) > 1 or Contact._default_kind not in selected_kinds: table_header.append("kind") if len(selected_address_books) > 1: plural = "s" @@ -251,9 +251,9 @@ def choose_address_book_from_list(header: str, abooks: Union[ return interactive.select(abooks) -def choose_vcard_from_list(header: str, vcards: list[CarddavObject], +def choose_vcard_from_list(header: str, vcards: list[Contact], include_none: bool = False - ) -> Optional[CarddavObject]: + ) -> Optional[Contact]: """Let the user select a contact from a list :param header: some text to print in front of the list @@ -272,22 +272,22 @@ def choose_vcard_from_list(header: str, vcards: list[CarddavObject], def get_contact_list(address_books: Union[VdirAddressBook, AddressBookCollection], - query: Query) -> list[CarddavObject]: + query: Query) -> list[Contact]: """Find contacts in the given address book grouped, sorted and reversed according to the loaded configuration. :param address_books: the address book to search :param query: the query to use when searching - :returns: list of found CarddavObject objects + :returns: list of found Contact objects """ contacts = address_books.search(query) return sort_contacts(contacts, config.reverse, config.group_by_addressbook, config.sort) -def sort_contacts(contacts: Iterable[CarddavObject], reverse: bool = False, +def sort_contacts(contacts: Iterable[Contact], reverse: bool = False, group: bool = False, sort: str = "first_name") -> list[ - CarddavObject]: + Contact]: """Sort a list of contacts :param contacts: the contact list to sort @@ -352,7 +352,7 @@ def prepare_search_queries(args: Namespace) -> dict[str, Query]: return queries2 -def generate_contact_list(args: Namespace) -> list[CarddavObject]: +def generate_contact_list(args: Namespace) -> list[Contact]: """Find the contact list with which we will work later on :param args: the command line arguments @@ -388,7 +388,7 @@ def new_subcommand(abooks: AddressBookCollection, data: str, open_editor: bool if data: # create new contact from stdin/the input file try: - new_contact = CarddavObject.from_yaml( + new_contact = Contact.from_yaml( abook, data, config.private_objects, config.preferred_vcard_version, config.localize_dates) except ValueError as err: @@ -610,7 +610,7 @@ def add_email_to_contact(name: str, email_address: str, if not confirm("Create contact?", False): print("Cancelled") return - selected_vcard = CarddavObject.from_yaml( + selected_vcard = Contact.from_yaml( selected_address_book, '\n'.join(template_data), config.private_objects, config.preferred_vcard_version, config.localize_dates) @@ -705,7 +705,7 @@ def add_email_subcommand( print("No more email addresses") -def birthdays_subcommand(vcard_list: list[CarddavObject], parsable: bool +def birthdays_subcommand(vcard_list: list[Contact], parsable: bool ) -> ExitStatus: """Print birthday contact table. @@ -751,7 +751,7 @@ def birthdays_subcommand(vcard_list: list[CarddavObject], parsable: bool return 1 -def phone_subcommand(search_terms: Query, vcard_list: list[CarddavObject], +def phone_subcommand(search_terms: Query, vcard_list: list[Contact], parsable: bool) -> ExitStatus: """Print a phone application friendly contact table. @@ -792,7 +792,7 @@ def phone_subcommand(search_terms: Query, vcard_list: list[CarddavObject], def post_address_subcommand(search_terms: Query, - vcard_list: list[CarddavObject], parsable: bool + vcard_list: list[Contact], parsable: bool ) -> ExitStatus: """Print a contact table with all postal / mailing addresses @@ -836,7 +836,7 @@ def post_address_subcommand(search_terms: Query, return 1 -def email_subcommand(search_terms: Query, vcard_list: list[CarddavObject], +def email_subcommand(search_terms: Query, vcard_list: list[Contact], parsable: bool, remove_first_line: bool) -> ExitStatus: """Print a mail client friendly contacts table that is compatible with the default format used by mutt. @@ -908,7 +908,7 @@ def _filter_email_post_or_phone_number_results(search_terms: Query, return matched_line_list if matched_line_list else field_line_list -def list_subcommand(vcard_list: list[CarddavObject], parsable: bool, +def list_subcommand(vcard_list: list[Contact], parsable: bool, fields: list[str]) -> ExitStatus: """Print a user friendly contacts table. @@ -924,7 +924,7 @@ def list_subcommand(vcard_list: list[CarddavObject], parsable: bool, list_contacts(vcard_list, fields, parsable) -def modify_subcommand(selected_vcard: CarddavObject, +def modify_subcommand(selected_vcard: Contact, input_from_stdin_or_file: str, open_editor: bool, source: bool = False) -> ExitStatus: """Modify a contact in an external editor. @@ -948,7 +948,7 @@ def modify_subcommand(selected_vcard: CarddavObject, if input_from_stdin_or_file: # create new contact from stdin try: - new_contact = CarddavObject.clone_with_yaml_update( + new_contact = Contact.clone_with_yaml_update( selected_vcard, input_from_stdin_or_file, config.localize_dates) except ValueError as err: @@ -969,7 +969,7 @@ def modify_subcommand(selected_vcard: CarddavObject, modify_existing_contact(selected_vcard) -def remove_subcommand(selected_vcard: CarddavObject, force: bool) -> None: +def remove_subcommand(selected_vcard: Contact, force: bool) -> None: """Remove a contact from the address book. :param selected_vcard: the contact to delete @@ -985,7 +985,7 @@ def remove_subcommand(selected_vcard: CarddavObject, force: bool) -> None: selected_vcard.formatted_name)) -def merge_subcommand(vcards: list[CarddavObject], +def merge_subcommand(vcards: list[Contact], abooks: AddressBookCollection, search_terms: Query ) -> ExitStatus: """Merge two contacts into one. @@ -1020,7 +1020,7 @@ def merge_subcommand(vcards: list[CarddavObject], merge_existing_contacts(source_vcard, target_vcard, True) -def copy_or_move_subcommand(action: str, vcards: list[CarddavObject], +def copy_or_move_subcommand(action: str, vcards: list[Contact], target_address_books: AddressBookCollection ) -> ExitStatus: """Copy or move a contact to a different address book. diff --git a/khard/query.py b/khard/query.py index efe37e8..ae7bf93 100644 --- a/khard/query.py +++ b/khard/query.py @@ -7,7 +7,7 @@ import re from typing import cast, Any, Optional, Union -from . import carddav_object +from . import contacts # constants FIELD_PHONE_NUMBERS = "phone_numbers" @@ -15,10 +15,10 @@ class Query(metaclass=abc.ABCMeta): - """A query to match against strings, lists of strings and CarddavObjects""" + """A query to match against strings, lists of strings and Contacts""" @abc.abstractmethod - def match(self, thing: Union[str, "carddav_object.CarddavObject"]) -> bool: + def match(self, thing: Union[str, "contacts.Contact"]) -> bool: """Match the self query against the given thing""" @abc.abstractmethod @@ -70,7 +70,7 @@ class NullQuery(Query): """The null-query, it matches nothing.""" - def match(self, thing: Union[str, "carddav_object.CarddavObject"]) -> bool: + def match(self, thing: Union[str, "contacts.Contact"]) -> bool: return False def get_term(self) -> None: @@ -84,7 +84,7 @@ class AnyQuery(Query): """The match-anything-query, it always matches.""" - def match(self, thing: Union[str, "carddav_object.CarddavObject"]) -> bool: + def match(self, thing: Union[str, "contacts.Contact"]) -> bool: return True def get_term(self) -> str: @@ -104,7 +104,7 @@ class TermQuery(Query): def __init__(self, term: str) -> None: self._term = term.lower() - def match(self, thing: Union[str, "carddav_object.CarddavObject"]) -> bool: + def match(self, thing: Union[str, "contacts.Contact"]) -> bool: if isinstance(thing, str): return self._term in thing.lower() return self._term in thing.pretty().lower() @@ -124,13 +124,13 @@ def __str__(self) -> str: class FieldQuery(TermQuery): - """A query to match against a certain field in a carddav object.""" + """A query to match against a certain field in a contact object.""" def __init__(self, field: str, value: str) -> None: self._field = field super().__init__(value) - def match(self, thing: Union[str, "carddav_object.CarddavObject"]) -> bool: + def match(self, thing: Union[str, "contacts.Contact"]) -> bool: if isinstance(thing, str): return super().match(thing) if hasattr(thing, self._field): @@ -172,7 +172,7 @@ class AndQuery(Query): def __init__(self, first: Query, second: Query, *queries: Query) -> None: self._queries = (first, second, *queries) - def match(self, thing: Union[str, "carddav_object.CarddavObject"]) -> bool: + def match(self, thing: Union[str, "contacts.Contact"]) -> bool: return all(q.match(thing) for q in self._queries) def get_term(self) -> Optional[str]: @@ -203,7 +203,7 @@ class OrQuery(Query): def __init__(self, first: Query, second: Query, *queries: Query) -> None: self._queries = (first, second, *queries) - def match(self, thing: Union[str, "carddav_object.CarddavObject"]) -> bool: + def match(self, thing: Union[str, "contacts.Contact"]) -> bool: return any(q.match(thing) for q in self._queries) def get_term(self) -> Optional[str]: @@ -236,7 +236,7 @@ def __init__(self, term: str) -> None: self._props_query = OrQuery(FieldQuery("formatted_name", term), FieldQuery("nicknames", term)) - def match(self, thing: Union[str, "carddav_object.CarddavObject"]) -> bool: + def match(self, thing: Union[str, "contacts.Contact"]) -> bool: m = super().match if isinstance(thing, str): return m(thing) @@ -266,7 +266,7 @@ def __init__(self, value: str) -> None: super().__init__(FIELD_PHONE_NUMBERS, value) self._term_only_digits = self._strip_phone_number(value) - def match(self, thing: Union[str, "carddav_object.CarddavObject"]) -> bool: + def match(self, thing: Union[str, "contacts.Contact"]) -> bool: if isinstance(thing, str): return self._match_union(thing) else: @@ -334,7 +334,7 @@ def parse(string: str) -> Union[TermQuery, FieldQuery]: The input string interpreted as a :py:class:`FieldQuery` if it starts with a valid property name of the - :py:class:`~khard.carddav_object.CarddavObject` class, followed by a colon + :py:class:`~khard.contacts.Contact` class, followed by a colon and an arbitrary search term. Otherwise it is interpreted as a :py:class:`TermQuery`. @@ -349,10 +349,10 @@ def parse(string: str) -> Union[TermQuery, FieldQuery]: if field == FIELD_PHONE_NUMBERS: return PhoneNumberQuery(term) if field == "kind": - for kind in carddav_object.CarddavObject._supported_kinds: + for kind in contacts.Contact._supported_kinds: if kind.startswith(term.lower()): return FieldQuery(field, kind) return TermQuery(string) - if field in carddav_object.CarddavObject.get_properties(): + if field in contacts.Contact.get_properties(): return FieldQuery(field, term) return TermQuery(string) diff --git a/test/helpers.py b/test/helpers.py index e7fa641..418af3a 100644 --- a/test/helpers.py +++ b/test/helpers.py @@ -11,7 +11,7 @@ import vobject from khard import address_book -from khard import carddav_object +from khard import contacts def vCard(**kwargs): @@ -28,17 +28,17 @@ def vCard(**kwargs): def TestVCardWrapper(**kwargs): """Create a simple VCardWrapper for tests.""" - return carddav_object.VCardWrapper(vCard(**kwargs)) + return contacts.VCardWrapper(vCard(**kwargs)) def TestYAMLEditable(**kwargs): """Create a simple YAMLEditable for tests.""" - return carddav_object.YAMLEditable(vCard(**kwargs)) + return contacts.YAMLEditable(vCard(**kwargs)) -def TestCarddavObject(**kwargs): - """Create a siple CarddavObject for tests.""" - return carddav_object.CarddavObject(vCard(**kwargs), None, None) +def TestContact(**kwargs): + """Create a siple Contact for tests.""" + return contacts.Contact(vCard(**kwargs), None, None) def mock_stream(name="stdout"): @@ -57,7 +57,7 @@ def mock_stream(name="stdout"): return context_manager -def load_contact(path: str) -> carddav_object.CarddavObject: +def load_contact(path: str) -> contacts.Contact: """Load a contact from the fixture directory. :param path: the file name (full, relative to cwd or the fixture dir) @@ -65,7 +65,7 @@ def load_contact(path: str) -> carddav_object.CarddavObject: abook = address_book.VdirAddressBook("test", "/tmp") if not os.path.exists(path): path = os.path.join("test/fixture/vcards", path) - contact = carddav_object.CarddavObject.from_file(abook, path) + contact = contacts.Contact.from_file(abook, path) if contact is None: raise FileNotFoundError(path) return contact diff --git a/test/test_address_book.py b/test/test_address_book.py index 96d505a..de90b5b 100644 --- a/test/test_address_book.py +++ b/test/test_address_book.py @@ -106,7 +106,7 @@ def test_unparsable_files_can_be_skipped(self): abook.load() self.assertEqual( cm.output[0], - "WARNING:khard.carddav_object:Filtering some problematic tags " + "WARNING:khard.contacts:Filtering some problematic tags " "from test/fixture/broken.abook/unparsable.vcf", ) # FIXME Remove this regex assert when either diff --git a/test/test_carddav_object.py b/test/test_contacts.py similarity index 78% rename from test/test_carddav_object.py rename to test/test_contacts.py index 2f3f277..7610e46 100644 --- a/test/test_carddav_object.py +++ b/test/test_contacts.py @@ -1,4 +1,4 @@ -"""Tests for the CarddavObject class from the carddav module.""" +"""Tests for the Contact class from the contacts module.""" # pylint: disable=missing-docstring @@ -7,35 +7,35 @@ import unittest from unittest import mock -from khard.carddav_object import CarddavObject, multi_property_key +from khard.contacts import Contact, multi_property_key -class CarddavObjectFormatDateObject(unittest.TestCase): +class ContactFormatDateObject(unittest.TestCase): def test_format_date_object_will_not_touch_strings(self): expected = "untouched string" - actual = CarddavObject._format_date_object(expected, False) + actual = Contact._format_date_object(expected, False) self.assertEqual(actual, expected) def test_format_date_object_with_simple_date_object(self): d = datetime.datetime(2018, 2, 13) - actual = CarddavObject._format_date_object(d, False) + actual = Contact._format_date_object(d, False) self.assertEqual(actual, "2018-02-13") def test_format_date_object_with_simple_datetime_object(self): d = datetime.datetime(2018, 2, 13, 0, 38, 31) with mock.patch("time.timezone", -7200): - actual = CarddavObject._format_date_object(d, False) + actual = Contact._format_date_object(d, False) self.assertEqual(actual, "2018-02-13T00:38:31+02:00") def test_format_date_object_with_date_1900(self): d = datetime.datetime(1900, 2, 13) - actual = CarddavObject._format_date_object(d, False) + actual = Contact._format_date_object(d, False) self.assertEqual(actual, "--02-13") class AltIds(unittest.TestCase): def test_altids_are_read(self): - card = CarddavObject.from_file(None, "test/fixture/vcards/altid.vcf") + card = Contact.from_file(None, "test/fixture/vcards/altid.vcf") expected = "one representation" self.assertEqual(expected, card.get_first_name_last_name()) @@ -46,11 +46,11 @@ class Photo(unittest.TestCase): PNG_HEADER = b"\x89PNG\r\n\x1a\n" def test_parsing_base64_ecoded_photo_vcard_v3(self): - c = CarddavObject.from_file(None, "test/fixture/vcards/photov3.vcf") + c = Contact.from_file(None, "test/fixture/vcards/photov3.vcf") self.assertEqual(c.vcard.photo.value[:8], self.PNG_HEADER) def test_parsing_base64_ecoded_photo_vcard_v4(self): - c = CarddavObject.from_file(None, "test/fixture/vcards/photov4.vcf") + c = Contact.from_file(None, "test/fixture/vcards/photov4.vcf") uri_stuff, data = c.vcard.photo.value.split(",") self.assertEqual(uri_stuff, "data:image/png;base64") data = base64.decodebytes(data.encode()) diff --git a/test/test_formatter.py b/test/test_formatter.py index 5809e31..a020b29 100644 --- a/test/test_formatter.py +++ b/test/test_formatter.py @@ -5,7 +5,7 @@ from vobject.vcard import Name from khard.address_book import VdirAddressBook -from khard.carddav_object import CarddavObject +from khard.contacts import Contact from khard.formatter import Formatter from .helpers import vCard @@ -66,7 +66,7 @@ class GetSpecialField(unittest.TestCase): prefix="Prefix", suffix="Suffix", ) - _vcard = CarddavObject( + _vcard = Contact( vCard(fn="Formatted Name", n=_name, nickname="Nickname"), VdirAddressBook("test", "/tmp"), "", diff --git a/test/test_khard.py b/test/test_khard.py index ca8b377..8c9a363 100644 --- a/test/test_khard.py +++ b/test/test_khard.py @@ -8,7 +8,7 @@ from khard import config, khard, query from khard.khard import find_email_addresses -from .helpers import TestCarddavObject, TmpAbook, load_contact +from .helpers import TestContact, TmpAbook, load_contact class TestSearchQueryPreparation(unittest.TestCase): @@ -240,11 +240,11 @@ def test_group_by_addressbook(self): def test_sort_order_for_accentuated_names(self): # reported in issue #127 - albert = TestCarddavObject(fn="Albert") - eleanor = TestCarddavObject(fn="Eleanor") - eugene = TestCarddavObject(fn="Eugene") - zakari = TestCarddavObject(fn="Zakari") - eric = TestCarddavObject(fn="Éric") + albert = TestContact(fn="Albert") + eleanor = TestContact(fn="Eleanor") + eugene = TestContact(fn="Eugene") + zakari = TestContact(fn="Zakari") + eric = TestContact(fn="Éric") unsorted = [albert, eleanor, eugene, zakari, eric] sorted = khard.sort_contacts(unsorted, sort="formatted_name") self.assertEqual(sorted, [albert, eleanor, eric, eugene, zakari]) diff --git a/test/test_query.py b/test/test_query.py index 4e4665b..f1d45bd 100644 --- a/test/test_query.py +++ b/test/test_query.py @@ -11,7 +11,7 @@ parse, ) -from .helpers import TestCarddavObject, load_contact +from .helpers import TestContact, load_contact class TestTermQuery(unittest.TestCase): @@ -107,43 +107,43 @@ def test_and_queries_match_after_sorting(self): class TestFieldQuery(unittest.TestCase): @unittest.expectedFailure def test_empty_field_values_match_if_the_field_is_present(self): - # This test currently fails because the CarddavObject class has all + # This test currently fails because the Contact class has all # attributes set because they are properties. So the test in the query # class if an attribute is present never fails. uid = "Some Test Uid" - vcard1 = TestCarddavObject(uid=uid) - vcard2 = TestCarddavObject() + vcard1 = TestContact(uid=uid) + vcard2 = TestContact() query = FieldQuery("uid", "") self.assertTrue(query.match(vcard1)) self.assertFalse(query.match(vcard2)) def test_empty_field_values_fails_if_the_field_is_absent(self): - vcard = TestCarddavObject() + vcard = TestContact() query = FieldQuery("emails", "") self.assertFalse(query.match(vcard)) def test_values_can_match_exact(self): uid = "Some Test Uid" - vcard = TestCarddavObject(uid=uid) + vcard = TestContact(uid=uid) query = FieldQuery("uid", uid) self.assertTrue(query.match(vcard)) def test_values_can_match_substrings(self): uid = "Some Test Uid" - vcard = TestCarddavObject(uid=uid) + vcard = TestContact(uid=uid) query = FieldQuery("uid", "e Test U") self.assertTrue(query.match(vcard)) def test_values_can_match_case_insensitive(self): uid = "Some Test Uid" - vcard = TestCarddavObject(uid=uid) + vcard = TestContact(uid=uid) query1 = FieldQuery("uid", uid.upper()) query2 = FieldQuery("uid", uid.lower()) self.assertTrue(query1.match(vcard)) self.assertTrue(query2.match(vcard)) def test_match_formatted_name(self): - vcard = TestCarddavObject(fn="foo bar") + vcard = TestContact(fn="foo bar") query = FieldQuery("formatted_name", "foo") self.assertTrue(query.match(vcard)) @@ -183,12 +183,12 @@ def test_kind_query_with_nonsensical_value(self): self.assertFalse(query.match(vcard)) def test_kind_query_with_explicit_match(self): - contact = TestCarddavObject(kind="org", version="4.0") + contact = TestContact(kind="org", version="4.0") query = FieldQuery("kind", "org") self.assertTrue(query.match(contact)) def test_kind_query_with_explicit_mismatch(self): - contact = TestCarddavObject(kind="org", version="4.0") + contact = TestContact(kind="org", version="4.0") query = FieldQuery("kind", "individual") self.assertFalse(query.match(contact)) diff --git a/test/test_vcard_wrapper.py b/test/test_vcard_wrapper.py index 29ce84b..19d98f3 100644 --- a/test/test_vcard_wrapper.py +++ b/test/test_vcard_wrapper.py @@ -1,4 +1,4 @@ -"""Tests for the VCardWrapper class from the carddav module.""" +"""Tests for the VCardWrapper class from the contacts module.""" # pylint: disable=missing-docstring import contextlib @@ -8,7 +8,7 @@ import vobject -from khard.carddav_object import VCardWrapper +from khard.contacts import VCardWrapper from khard.helpers.typing import ObjectType from .helpers import vCard, TestVCardWrapper diff --git a/test/test_yaml.py b/test/test_yaml.py index 0af6c4a..44f11b0 100644 --- a/test/test_yaml.py +++ b/test/test_yaml.py @@ -11,7 +11,7 @@ from ruamel.yaml import YAML import khard.helpers -from khard.carddav_object import CarddavObject +from khard.contacts import Contact from .helpers import TestYAMLEditable as create_test_card @@ -24,13 +24,13 @@ def to_yaml(data): return stream.getvalue() -def parse_yaml(yaml: str = "") -> CarddavObject: - """Parse some yaml string into a CarddavObject +def parse_yaml(yaml: str = "") -> Contact: + """Parse some yaml string into a Contact :param yaml: the yaml input string to parse - :returns: the parsed CarddavObject + :returns: the parsed Contact """ - return CarddavObject.from_yaml( + return Contact.from_yaml( address_book=mock.Mock(path="foo-path"), yaml=yaml, supported_private_objects=[], @@ -42,67 +42,67 @@ def parse_yaml(yaml: str = "") -> CarddavObject: class EmptyFieldsAndSpaces(unittest.TestCase): def test_empty_birthday_in_yaml_input(self): empty_birthday = "First name: foo\nBirthday:" - with mock.patch("khard.carddav_object.logger"): + with mock.patch("khard.contacts.logger"): x = parse_yaml(empty_birthday) self.assertIsNone(x.birthday) def test_only_spaces_in_birthday_in_yaml_input(self): spaces_birthday = "First name: foo\nBirthday: " - with mock.patch("khard.carddav_object.logger"): + with mock.patch("khard.contacts.logger"): x = parse_yaml(spaces_birthday) self.assertIsNone(x.birthday) def test_empty_anniversary_in_yaml_input(self): empty_anniversary = "First name: foo\nAnniversary:" - with mock.patch("khard.carddav_object.logger"): + with mock.patch("khard.contacts.logger"): x = parse_yaml(empty_anniversary) self.assertIsNone(x.anniversary) def test_empty_organisation_in_yaml_input(self): empty_organisation = "First name: foo\nOrganisation:" - with mock.patch("khard.carddav_object.logger"): + with mock.patch("khard.contacts.logger"): x = parse_yaml(empty_organisation) self.assertListEqual(x.organisations, []) def test_empty_nickname_in_yaml_input(self): empty_nickname = "First name: foo\nNickname:" - with mock.patch("khard.carddav_object.logger"): + with mock.patch("khard.contacts.logger"): x = parse_yaml(empty_nickname) self.assertListEqual(x.nicknames, []) def test_empty_role_in_yaml_input(self): empty_role = "First name: foo\nRole:" - with mock.patch("khard.carddav_object.logger"): + with mock.patch("khard.contacts.logger"): x = parse_yaml(empty_role) self.assertListEqual(x.roles, []) def test_empty_title_in_yaml_input(self): empty_title = "First name: foo\nTitle:" - with mock.patch("khard.carddav_object.logger"): + with mock.patch("khard.contacts.logger"): x = parse_yaml(empty_title) self.assertListEqual(x.titles, []) def test_empty_categories_in_yaml_input(self): empty_categories = "First name: foo\nCategories:" - with mock.patch("khard.carddav_object.logger"): + with mock.patch("khard.contacts.logger"): x = parse_yaml(empty_categories) self.assertListEqual(x.categories, []) def test_empty_webpage_in_yaml_input(self): empty_webpage = "First name: foo\nWebpage:" - with mock.patch("khard.carddav_object.logger"): + with mock.patch("khard.contacts.logger"): x = parse_yaml(empty_webpage) self.assertListEqual(x.webpages, []) def test_empty_note_in_yaml_input(self): empty_note = "First name: foo\nNote:" - with mock.patch("khard.carddav_object.logger"): + with mock.patch("khard.contacts.logger"): x = parse_yaml(empty_note) self.assertListEqual(x.notes, []) def test_empty_kind_in_yaml_input(self): empty_kind = "First name: foo\nKind:" - with mock.patch("khard.carddav_object.logger"): + with mock.patch("khard.contacts.logger"): x = parse_yaml(empty_kind) self.assertEqual(x.kind, "individual") @@ -113,7 +113,7 @@ def test_ablabelled_url_in_yaml_input(self): "First name: foo\nWebpage:\n - http://example.com\n" " - github: https://github.com/scheibler/khard" ) - with mock.patch("khard.carddav_object.logger"): + with mock.patch("khard.contacts.logger"): x = parse_yaml(ablabel_url) self.assertListEqual( x.webpages, diff --git a/test/test_yaml_editable.py b/test/test_yaml_editable.py index 0ca1da1..22708b7 100644 --- a/test/test_yaml_editable.py +++ b/test/test_yaml_editable.py @@ -1,4 +1,4 @@ -"""Tests for the carddav_object.YAMLEditable class""" +"""Tests for the contacts.YAMLEditable class""" import unittest