Skip to content

Commit de9ab25

Browse files
committed
Make Author a NamedTuple
1 parent 1ec8438 commit de9ab25

File tree

4 files changed

+49
-63
lines changed

4 files changed

+49
-63
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,51 @@
11
from __future__ import annotations
22

33
from typing import NamedTuple
4-
import unicodedata
54

65

7-
class Name(NamedTuple):
6+
class _Name(NamedTuple):
87
mononym: str = None
98
forename: str = None
109
surname: str = None
1110
suffix: str = None
1211

1312

14-
class Author:
15-
"""Represent PEP authors.
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.
1618

17-
Attributes:
18-
last_first: The author's name in Surname, Forename, Suffix order.
19-
nick: Author's nickname for PEP tables. Defaults to surname.
20-
email: The author's email address.
21-
_first_last: The author's full name, unchanged
2219

23-
"""
24-
__slots__ = "last_first", "nick", "email", "_first_last"
25-
26-
def __init__(self, author_email_tuple: tuple[str, str], authors_overrides: dict[str, dict[str, str]]):
27-
"""Parse the name and email address of an author."""
28-
name, email = author_email_tuple
29-
self._first_last: str = name.strip()
30-
self.email: str = email.lower()
31-
32-
self.last_first: str = ""
33-
self.nick: str = ""
34-
35-
if self._first_last in authors_overrides:
36-
name_dict = authors_overrides[self._first_last]
37-
self.last_first = name_dict["Surname First"]
38-
self.nick = name_dict["Name Reference"]
39-
else:
40-
name_parts = _parse_name(self._first_last)
41-
if name_parts.mononym is not None:
42-
self.last_first = self.nick = name_parts.mononym
43-
else:
44-
if name_parts.surname[1] == ".":
45-
# Add an escape to avoid docutils turning `v.` into `22.`.
46-
name_parts.surname = f"\\{name_parts.surname}"
47-
self.last_first = f"{name_parts.surname}, {name_parts.forename}"
48-
self.nick = name_parts.surname
49-
50-
if name_parts.suffix is not None:
51-
self.last_first += f", {name_parts.suffix}"
52-
53-
def __hash__(self):
54-
return hash(self.last_first)
55-
56-
def __eq__(self, other):
57-
if not isinstance(other, Author):
58-
return NotImplemented
59-
return self.last_first == other.last_first
60-
61-
def __len__(self):
62-
return len(unicodedata.normalize("NFC", self.last_first))
63-
64-
65-
def _parse_name(full_name: str) -> Name:
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 is not None:
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:
6649
"""Decompose a full name into parts.
6750
6851
If a mononym (e.g, 'Aahz') then return the full name. If there are
@@ -78,14 +61,14 @@ def _parse_name(full_name: str) -> Name:
7861
pre_suffix, _, raw_suffix = full_name.partition(",")
7962
name_parts = pre_suffix.strip().split(" ")
8063
num_parts = len(name_parts)
81-
suffix = raw_suffix.strip() or None
64+
suffix = raw_suffix.strip()
8265

8366
if num_parts == 0:
8467
raise ValueError("Name is empty!")
8568
elif num_parts == 1:
86-
return Name(mononym=name_parts[0], suffix=suffix)
69+
return _Name(mononym=name_parts[0], suffix=suffix)
8770
elif num_parts == 2:
88-
return Name(forename=name_parts[0].strip(), surname=name_parts[1], suffix=suffix)
71+
return _Name(forename=name_parts[0].strip(), surname=name_parts[1], suffix=suffix)
8972

9073
# handles rogue uncaught suffixes
9174
if name_parts[-1] in possible_suffixes:
@@ -95,16 +78,16 @@ def _parse_name(full_name: str) -> Name:
9578
if name_parts[-2].islower():
9679
forename = " ".join(name_parts[:-2]).strip()
9780
surname = " ".join(name_parts[-2:])
98-
return Name(forename=forename, surname=surname, suffix=suffix)
81+
return _Name(forename=forename, surname=surname, suffix=suffix)
9982

10083
# handles double surnames after a middle initial (e.g. N. Vander Weele)
10184
elif any(s.endswith(".") for s in name_parts):
10285
split_position = [i for i, x in enumerate(name_parts) if x.endswith(".")][-1] + 1
10386
forename = " ".join(name_parts[:split_position]).strip()
10487
surname = " ".join(name_parts[split_position:])
105-
return Name(forename=forename, surname=surname, suffix=suffix)
88+
return _Name(forename=forename, surname=surname, suffix=suffix)
10689

10790
# default to using the last item as the surname
10891
else:
10992
forename = " ".join(name_parts[:-1]).strip()
110-
return Name(forename=forename, surname=name_parts[-1], suffix=suffix)
93+
return _Name(forename=forename, surname=name_parts[-1], suffix=suffix)

pep_sphinx_extensions/pep_zero_generator/parser.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
from pathlib import Path
77
import re
88
import textwrap
9+
from typing import TYPE_CHECKING
910

10-
from pep_sphinx_extensions.pep_zero_generator.author import Author
11+
from pep_sphinx_extensions.pep_zero_generator.author import parse_author_email
1112
from pep_sphinx_extensions.pep_zero_generator.constants import ACTIVE_ALLOWED
1213
from pep_sphinx_extensions.pep_zero_generator.constants import HIDE_STATUS
1314
from pep_sphinx_extensions.pep_zero_generator.constants import SPECIAL_STATUSES
@@ -18,6 +19,9 @@
1819
from pep_sphinx_extensions.pep_zero_generator.constants import TYPE_VALUES
1920
from pep_sphinx_extensions.pep_zero_generator.errors import PEPError
2021

22+
if TYPE_CHECKING:
23+
from pep_sphinx_extensions.pep_zero_generator.author import Author
24+
2125

2226
class PEP:
2327
"""Representation of PEPs.
@@ -118,7 +122,7 @@ def _parse_authors(pep: PEP, author_header: str, authors_overrides: dict) -> lis
118122
authors_and_emails = _parse_author(author_header)
119123
if not authors_and_emails:
120124
raise _raise_pep_error(pep, "no authors found", pep_num=True)
121-
return [Author(author_tuple, authors_overrides) for author_tuple in authors_and_emails]
125+
return [parse_author_email(author_tuple, authors_overrides) for author_tuple in authors_and_emails]
122126

123127

124128
author_angled = re.compile(r"(?P<author>.+?) <(?P<email>.+?)>(,\s*)?")

pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,7 @@ def create_pep_zero(_: Sphinx, env: BuildEnvironment, docnames: list[str]) -> No
5656
pep = parser.PEP(path.joinpath(file_path).absolute(), authors_overrides)
5757
peps.append(pep)
5858

59-
pep_writer = writer.PEPZeroWriter()
60-
pep0_text = pep_writer.write_pep0(sorted(peps))
59+
pep0_text = writer.PEPZeroWriter().write_pep0(sorted(peps))
6160
Path(f"{pep_zero_filename}.rst").write_text(pep0_text, encoding="utf-8")
6261

6362
# Add to files for builder

pep_sphinx_extensions/pep_zero_generator/writer.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ def write_pep0(self, peps: list[PEP]):
195195

196196
# PEP owners
197197
authors_dict = _verify_email_addresses(peps)
198-
max_name_len = max(len(author) for author in authors_dict.keys())
198+
max_name_len = max(len(author.last_first) for author in authors_dict.keys())
199199
self.emit_title("Authors/Owners", "authors")
200200
self.emit_author_table_separator(max_name_len)
201201
self.emit_text(f"{'Name':{max_name_len}} Email Address")

0 commit comments

Comments
 (0)