Skip to content

Commit a1ab930

Browse files
authored
Merge branch 'master' into patch-1
2 parents c60ba11 + a7ec757 commit a1ab930

10 files changed

Lines changed: 139 additions & 28 deletions

File tree

.editorconfig

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# http://editorconfig.org
2+
3+
root = true
4+
5+
[*]
6+
charset = utf-8
7+
end_of_line = lf
8+
insert_final_newline = true
9+
trim_trailing_whitespace = true
10+
11+
[*.{py,rst,ini}]
12+
indent_style = space
13+
indent_size = 4
14+
15+
[*.{html,json,yml}]
16+
indent_style = space
17+
indent_size = 2
18+
19+
[*.md]
20+
trim_trailing_whitespace = false

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ build
1010
.coverage
1111
dist
1212
.idea
13+
Pipfile
14+
Pipfile.lock
1315

1416
# docs
1517
docs/_*

.travis.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
language: python
22
python:
3-
- "2.6"
43
- "2.7"
5-
- "3.2"
6-
- "3.3"
74
- "3.4"
85
- "3.5"
96
- "3.6"
7+
- "3.7"
8+
- "3.8"
109
# command to install dependencies
11-
install:
10+
install:
1211
- if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2; fi
1312
- "pip install dill"
1413
- "python setup.py install"

docs/customize.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ Other editable attributes
5757

5858
* :py:obj:`~nameparser.config.Constants.string_format` - controls output from `str()`
5959
* :py:obj:`~nameparser.config.Constants.empty_attribute_default` - value returned by empty attributes, defaults to empty string
60+
* :py:obj:`~nameparser.config.Constants.capitalize_name` - If set, applies :py:meth:`~nameparser.parser.HumanName.capitalize` to :py:class:`~nameparser.parser.HumanName` instance.
61+
* :py:obj:`~nameparser.config.Constants.force_mixed_case_capitalization` - If set, forces the capitalization of mixed case strings when :py:meth:`~nameparser.parser.HumanName.capitalize` is called.
6062

6163

6264

docs/usage.rst

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,8 @@ Capitalization Support
7272

7373
The HumanName class can try to guess the correct capitalization of name
7474
entered in all upper or lower case. By default, it will not adjust
75-
the case of names entered in mixed case. To run capitalization on all names
76-
pass the parameter `force=True`.
77-
75+
the case of names entered in mixed case. To run capitalization on a
76+
`HumanName` instance, pass the parameter `force=True`.
7877

7978
Capitalize the name.
8079

@@ -94,6 +93,31 @@ pass the parameter `force=True`.
9493
>>> str(name)
9594
'Shirley MacLaine'
9695

96+
To apply capitalization to all `HumanName` instances, set
97+
:py:attr:`~nameparser.config.Constants.capitalize_name` to `True`.
98+
99+
.. doctest:: capitalize_name
100+
:options: +NORMALIZE_WHITESPACE
101+
102+
>>> from nameparser.config import CONSTANTS
103+
>>> CONSTANTS.capitalize_name = True
104+
>>> name = HumanName("bob v. de la macdole-eisenhower phd")
105+
>>> str(name)
106+
'Bob V. de la MacDole-Eisenhower Ph.D.'
107+
108+
To force the capitalization of mixed case strings on all `HumanName` instances,
109+
set :py:attr:`~nameparser.config.Constants.force_mixed_case_capitalization` to `True`.
110+
111+
.. doctest:: force_mixed_case_capitalization
112+
:options: +NORMALIZE_WHITESPACE
113+
114+
>>> from nameparser.config import CONSTANTS
115+
>>> CONSTANTS.force_mixed_case_capitalization = True
116+
>>> name = HumanName('Shirley Maclaine')
117+
>>> name.capitalize()
118+
>>> str(name)
119+
'Shirley MacLaine'
120+
97121

98122
Nickname Handling
99123
------------------

nameparser/config/__init__.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
unexpected results. See `Customizing the Parser <customize.html>`_.
3030
"""
3131
from __future__ import unicode_literals
32-
32+
from collections.abc import Set
3333
import sys
3434
try:
3535
# Python 3.3+
@@ -184,8 +184,37 @@ class Constants(object):
184184
'John'
185185
186186
"""
187-
188-
187+
capitalize_name = False
188+
"""
189+
If set, applies :py:meth:`~nameparser.parser.HumanName.capitalize` to
190+
:py:class:`~nameparser.parser.HumanName` instance.
191+
192+
.. doctest::
193+
194+
>>> from nameparser.config import CONSTANTS
195+
>>> CONSTANTS.capitalize_name = True
196+
>>> name = HumanName("bob v. de la macdole-eisenhower phd")
197+
>>> str(name)
198+
'Bob V. de la MacDole-Eisenhower Ph.D.'
199+
200+
"""
201+
force_mixed_case_capitalization = False
202+
"""
203+
If set, forces the capitalization of mixed case strings when
204+
:py:meth:`~nameparser.parser.HumanName.capitalize` is called.
205+
206+
.. doctest::
207+
208+
>>> from nameparser.config import CONSTANTS
209+
>>> CONSTANTS.force_mixed_case_capitalization = True
210+
>>> name = HumanName('Shirley Maclaine')
211+
>>> name.capitalize()
212+
>>> str(name)
213+
'Shirley MacLaine'
214+
215+
"""
216+
217+
189218
def __init__(self,
190219
prefixes=PREFIXES,
191220
suffix_acronyms=SUFFIX_ACRONYMS,

nameparser/config/capitalization.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
from __future__ import unicode_literals
33

44
CAPITALIZATION_EXCEPTIONS = (
5-
('ii' ,'II'),
6-
('iii','III'),
7-
('iv' ,'IV'),
8-
('md' ,'M.D.'),
9-
('phd','Ph.D.'),
5+
('ii', 'II'),
6+
('iii', 'III'),
7+
('iv', 'IV'),
8+
('md', 'M.D.'),
9+
('phd', 'Ph.D.'),
1010
)
1111
"""
1212
Any pieces that are not capitalized by capitalizing the first letter.

nameparser/config/prefixes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#: Name pieces that appear before a last name. Prefixes join to the piece
55
#: that follows them to make one new piece. They can be chained together, e.g
66
#: "von der" and "de la". Because they only appear in middle or last names,
7-
#: they also signifiy that all following name pieces should be in the same name
7+
#: they also signify that all following name pieces should be in the same name
88
#: part, for example, "von" will be joined to all following pieces that are not
99
#: prefixes or suffixes, allowing recognition of double last names when they
1010
#: appear after a prefixes. So in "pennie von bergen wessels MD", "von" will

nameparser/parser.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ def pre_process(self):
387387
This method happens at the beginning of the :py:func:`parse_full_name`
388388
before any other processing of the string aside from unicode
389389
normalization, so it's a good place to do any custom handling in a
390-
subclass. Runs :py:func:`parse_nicknames` and py:func:`squash_emoji`.
390+
subclass. Runs :py:func:`parse_nicknames` and :py:func:`squash_emoji`.
391391
392392
"""
393393
self.fix_phd()
@@ -397,9 +397,11 @@ def pre_process(self):
397397
def post_process(self):
398398
"""
399399
This happens at the end of the :py:func:`parse_full_name` after
400-
all other processing has taken place. Runs :py:func:`handle_firstnames`.
400+
all other processing has taken place. Runs :py:func:`handle_firstnames`
401+
and :py:func:`handle_capitalization`.
401402
"""
402403
self.handle_firstnames()
404+
self.handle_capitalization()
403405

404406
def fix_phd(self):
405407
_re = self.C.regexes.phd
@@ -675,9 +677,9 @@ def join_on_conjunctions(self, pieces, additional_parts_count=0):
675677
:param list pieces: name pieces strings after split on spaces
676678
:param int additional_parts_count:
677679
:return: new list with piece next to conjunctions merged into one piece
678-
with spaces in it.
680+
with spaces in it.
679681
:rtype: list
680-
682+
681683
"""
682684
length = len(pieces) + additional_parts_count
683685
# don't join on conjunctions if there's only 2 parts
@@ -833,14 +835,16 @@ def cap_piece(self, piece, attribute):
833835
replacement = lambda m: self.cap_word(m.group(0), attribute)
834836
return self.C.regexes.word.sub(replacement, piece)
835837

836-
def capitalize(self, force=False):
838+
def capitalize(self, force=None):
837839
"""
838840
The HumanName class can try to guess the correct capitalization of name
839841
entered in all upper or lower case. By default, it will not adjust the
840842
case of names entered in mixed case. To run capitalization on all names
841843
pass the parameter `force=True`.
842844
843-
:param bool force: force capitalization of strings that include mixed case
845+
:param bool force: Forces capitalization of mixed case strings. This
846+
parameter overrides rules set within
847+
:py:class:`~nameparser.config.CONSTANTS`.
844848
845849
**Usage**
846850
@@ -861,10 +865,21 @@ def capitalize(self, force=False):
861865
862866
"""
863867
name = u(self)
868+
force = self.C.force_mixed_case_capitalization \
869+
if force is None else force
870+
864871
if not force and not (name == name.upper() or name == name.lower()):
865872
return
866873
self.title_list = self.cap_piece(self.title , 'title').split(' ')
867874
self.first_list = self.cap_piece(self.first , 'first').split(' ')
868875
self.middle_list = self.cap_piece(self.middle, 'middle').split(' ')
869876
self.last_list = self.cap_piece(self.last , 'last').split(' ')
870877
self.suffix_list = self.cap_piece(self.suffix, 'suffix').split(', ')
878+
879+
def handle_capitalization(self):
880+
"""
881+
Handles capitalization configurations set within
882+
:py:class:`~nameparser.config.CONSTANTS`.
883+
"""
884+
if self.C.capitalize_name:
885+
self.capitalize()

tests.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
class HumanNameTestBase(unittest.TestCase):
4242
def m(self, actual, expected, hn):
43-
"""assertEquals with a better message and awareness of hn.C.empty_attribute_default"""
43+
"""assertEqual with a better message and awareness of hn.C.empty_attribute_default"""
4444
expected = expected or hn.C.empty_attribute_default
4545
try:
4646
self.assertEqual(actual, expected, "'%s' != '%s' for '%s'\n%r" % (
@@ -50,7 +50,7 @@ def m(self, actual, expected, hn):
5050
hn
5151
))
5252
except UnicodeDecodeError:
53-
self.assertEquals(actual, expected)
53+
self.assertEqual(actual, expected)
5454

5555

5656
class HumanNamePythonTests(HumanNameTestBase):
@@ -62,8 +62,6 @@ def test_utf8(self):
6262

6363
def test_string_output(self):
6464
hn = HumanName("de la Véña, Jüan")
65-
print(hn)
66-
print(repr(hn))
6765

6866
def test_escaped_utf8_bytes(self):
6967
hn = HumanName(b'B\xc3\xb6ck, Gerald')
@@ -1267,7 +1265,7 @@ class ConstantsCustomization(HumanNameTestBase):
12671265
def test_add_title(self):
12681266
hn = HumanName("Te Awanui-a-Rangi Black", constants=None)
12691267
start_len = len(hn.C.titles)
1270-
self.assert_(start_len > 0)
1268+
self.assertTrue(start_len > 0)
12711269
hn.C.titles.add('te')
12721270
self.assertEqual(start_len + 1, len(hn.C.titles))
12731271
hn.parse_full_name()
@@ -1278,7 +1276,7 @@ def test_add_title(self):
12781276
def test_remove_title(self):
12791277
hn = HumanName("Hon Solo", constants=None)
12801278
start_len = len(hn.C.titles)
1281-
self.assert_(start_len > 0)
1279+
self.assertTrue(start_len > 0)
12821280
hn.C.titles.remove('hon')
12831281
self.assertEqual(start_len - 1, len(hn.C.titles))
12841282
hn.parse_full_name()
@@ -2090,6 +2088,28 @@ def test_formatting_constants_attribute(self):
20902088
self.assertEqual(u(hn), "TEST2")
20912089
CONSTANTS.string_format = _orig
20922090

2091+
def test_capitalize_name_constants_attribute(self):
2092+
from nameparser.config import CONSTANTS
2093+
CONSTANTS.capitalize_name = True
2094+
hn = HumanName("bob v. de la macdole-eisenhower phd")
2095+
self.assertEqual(str(hn), "Bob V. de la MacDole-Eisenhower Ph.D.")
2096+
CONSTANTS.capitalize_name = False
2097+
2098+
def test_force_mixed_case_capitalization_constants_attribute(self):
2099+
from nameparser.config import CONSTANTS
2100+
CONSTANTS.force_mixed_case_capitalization = True
2101+
hn = HumanName('Shirley Maclaine')
2102+
hn.capitalize()
2103+
self.assertEqual(str(hn), "Shirley MacLaine")
2104+
CONSTANTS.force_mixed_case_capitalization = False
2105+
2106+
def test_capitalize_name_and_force_mixed_case_capitalization_constants_attributes(self):
2107+
from nameparser.config import CONSTANTS
2108+
CONSTANTS.capitalize_name = True
2109+
CONSTANTS.force_mixed_case_capitalization = True
2110+
hn = HumanName('Shirley Maclaine')
2111+
self.assertEqual(str(hn), "Shirley MacLaine")
2112+
20932113
def test_quote_nickname_formating(self):
20942114
hn = HumanName("Rev John A. Kenneth Doe III (Kenny)")
20952115
hn.string_format = "{title} {first} {middle} {last} {suffix} '{nickname}'"

0 commit comments

Comments
 (0)