1
1
from __future__ import annotations
2
2
3
3
from typing import NamedTuple
4
- import unicodedata
5
4
6
5
7
- class Name (NamedTuple ):
6
+ class _Name (NamedTuple ):
8
7
mononym : str = None
9
8
forename : str = None
10
9
surname : str = None
11
10
suffix : str = None
12
11
13
12
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.
16
18
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
22
19
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 :
66
49
"""Decompose a full name into parts.
67
50
68
51
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:
78
61
pre_suffix , _ , raw_suffix = full_name .partition ("," )
79
62
name_parts = pre_suffix .strip ().split (" " )
80
63
num_parts = len (name_parts )
81
- suffix = raw_suffix .strip () or None
64
+ suffix = raw_suffix .strip ()
82
65
83
66
if num_parts == 0 :
84
67
raise ValueError ("Name is empty!" )
85
68
elif num_parts == 1 :
86
- return Name (mononym = name_parts [0 ], suffix = suffix )
69
+ return _Name (mononym = name_parts [0 ], suffix = suffix )
87
70
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 )
89
72
90
73
# handles rogue uncaught suffixes
91
74
if name_parts [- 1 ] in possible_suffixes :
@@ -95,16 +78,16 @@ def _parse_name(full_name: str) -> Name:
95
78
if name_parts [- 2 ].islower ():
96
79
forename = " " .join (name_parts [:- 2 ]).strip ()
97
80
surname = " " .join (name_parts [- 2 :])
98
- return Name (forename = forename , surname = surname , suffix = suffix )
81
+ return _Name (forename = forename , surname = surname , suffix = suffix )
99
82
100
83
# handles double surnames after a middle initial (e.g. N. Vander Weele)
101
84
elif any (s .endswith ("." ) for s in name_parts ):
102
85
split_position = [i for i , x in enumerate (name_parts ) if x .endswith ("." )][- 1 ] + 1
103
86
forename = " " .join (name_parts [:split_position ]).strip ()
104
87
surname = " " .join (name_parts [split_position :])
105
- return Name (forename = forename , surname = surname , suffix = suffix )
88
+ return _Name (forename = forename , surname = surname , suffix = suffix )
106
89
107
90
# default to using the last item as the surname
108
91
else :
109
92
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 )
0 commit comments