|
| 1 | +from __future__ import annotations |
| 2 | + |
| 3 | +from typing import NamedTuple |
| 4 | + |
| 5 | + |
| 6 | +class _Name(NamedTuple): |
| 7 | + mononym: str = None |
| 8 | + forename: str = None |
| 9 | + surname: str = None |
| 10 | + suffix: str = None |
| 11 | + |
| 12 | + |
| 13 | +class Author(NamedTuple): |
| 14 | + """Represent PEP authors.""" |
| 15 | + last_first: str # The author's name in Surname, Forename, Suffix order. |
| 16 | + nick: str # Author's nickname for PEP tables. Defaults to surname. |
| 17 | + email: str # The author's email address. |
| 18 | + |
| 19 | + |
| 20 | +def parse_author_email(author_email_tuple: tuple[str, str], authors_overrides: dict[str, dict[str, str]]) -> Author: |
| 21 | + """Parse the name and email address of an author.""" |
| 22 | + name, email = author_email_tuple |
| 23 | + _first_last = name.strip() |
| 24 | + email = email.lower() |
| 25 | + |
| 26 | + if _first_last in authors_overrides: |
| 27 | + name_dict = authors_overrides[_first_last] |
| 28 | + last_first = name_dict["Surname First"] |
| 29 | + nick = name_dict["Name Reference"] |
| 30 | + return Author(last_first, nick, email) |
| 31 | + |
| 32 | + name_parts = _parse_name(_first_last) |
| 33 | + if name_parts.mononym is not None: |
| 34 | + return Author(name_parts.mononym, name_parts.mononym, email) |
| 35 | + |
| 36 | + if name_parts.surname[1] == ".": |
| 37 | + # Add an escape to avoid docutils turning `v.` into `22.`. |
| 38 | + name_parts.surname = f"\\{name_parts.surname}" |
| 39 | + |
| 40 | + if name_parts.suffix: |
| 41 | + last_first = f"{name_parts.surname}, {name_parts.forename}, {name_parts.suffix}" |
| 42 | + return Author(last_first, name_parts.surname, email) |
| 43 | + |
| 44 | + last_first = f"{name_parts.surname}, {name_parts.forename}" |
| 45 | + return Author(last_first, name_parts.surname, email) |
| 46 | + |
| 47 | + |
| 48 | +def _parse_name(full_name: str) -> _Name: |
| 49 | + """Decompose a full name into parts. |
| 50 | +
|
| 51 | + If a mononym (e.g, 'Aahz') then return the full name. If there are |
| 52 | + suffixes in the name (e.g. ', Jr.' or 'II'), then find and extract |
| 53 | + them. If there is a middle initial followed by a full stop, then |
| 54 | + combine the following words into a surname (e.g. N. Vander Weele). If |
| 55 | + there is a leading, lowercase portion to the last name (e.g. 'van' or |
| 56 | + 'von') then include it in the surname. |
| 57 | +
|
| 58 | + """ |
| 59 | + possible_suffixes = {"Jr", "Jr.", "II", "III"} |
| 60 | + |
| 61 | + pre_suffix, _, raw_suffix = full_name.partition(",") |
| 62 | + name_parts = pre_suffix.strip().split(" ") |
| 63 | + num_parts = len(name_parts) |
| 64 | + suffix = raw_suffix.strip() |
| 65 | + |
| 66 | + if num_parts == 0: |
| 67 | + raise ValueError("Name is empty!") |
| 68 | + elif num_parts == 1: |
| 69 | + return _Name(mononym=name_parts[0], suffix=suffix) |
| 70 | + elif num_parts == 2: |
| 71 | + return _Name(forename=name_parts[0].strip(), surname=name_parts[1], suffix=suffix) |
| 72 | + |
| 73 | + # handles rogue uncaught suffixes |
| 74 | + if name_parts[-1] in possible_suffixes: |
| 75 | + suffix = f"{name_parts.pop(-1)} {suffix}".strip() |
| 76 | + |
| 77 | + # handles von, van, v. etc. |
| 78 | + if name_parts[-2].islower(): |
| 79 | + forename = " ".join(name_parts[:-2]).strip() |
| 80 | + surname = " ".join(name_parts[-2:]) |
| 81 | + return _Name(forename=forename, surname=surname, suffix=suffix) |
| 82 | + |
| 83 | + # handles double surnames after a middle initial (e.g. N. Vander Weele) |
| 84 | + elif any(s.endswith(".") for s in name_parts): |
| 85 | + split_position = [i for i, x in enumerate(name_parts) if x.endswith(".")][-1] + 1 |
| 86 | + forename = " ".join(name_parts[:split_position]).strip() |
| 87 | + surname = " ".join(name_parts[split_position:]) |
| 88 | + return _Name(forename=forename, surname=surname, suffix=suffix) |
| 89 | + |
| 90 | + # default to using the last item as the surname |
| 91 | + else: |
| 92 | + forename = " ".join(name_parts[:-1]).strip() |
| 93 | + return _Name(forename=forename, surname=name_parts[-1], suffix=suffix) |
0 commit comments